summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRené 'Necoro' Neumann <necoro@necoro.net>2008-09-02 13:01:17 +0200
committerRené 'Necoro' Neumann <necoro@necoro.net>2008-09-02 13:01:17 +0200
commitafa1de13f0576ace6dcbb0176490fd20922950cd (patch)
tree056a5fd646f53dfa83f2fe231ec0943747b15ffc
parent02d96210d9102f0cdec95b4e0f595cbd8fdd1e10 (diff)
downloadportato-afa1de13f0576ace6dcbb0176490fd20922950cd.tar.gz
portato-afa1de13f0576ace6dcbb0176490fd20922950cd.tar.bz2
portato-afa1de13f0576ace6dcbb0176490fd20922950cd.zip
Switch from tabs to 4 spaces
Diffstat (limited to '')
-rw-r--r--plugins/etc_proposals.py42
-rw-r--r--plugins/exception.py2
-rw-r--r--plugins/gpytage.py14
-rw-r--r--plugins/new_version.py94
-rw-r--r--plugins/notify.py48
-rw-r--r--plugins/reload_portage.py18
-rwxr-xr-xportato.py118
-rw-r--r--portato/TEST_helper.py36
-rw-r--r--portato/__init__.py62
-rw-r--r--portato/backend/__init__.py64
-rw-r--r--portato/backend/exceptions.py16
-rw-r--r--portato/backend/flags.py1308
-rw-r--r--portato/backend/package.py786
-rw-r--r--portato/backend/portage/__init__.py8
-rw-r--r--portato/backend/portage/package.py572
-rw-r--r--portato/backend/portage/package_22.py10
-rw-r--r--portato/backend/portage/sets.py168
-rw-r--r--portato/backend/portage/settings.py78
-rw-r--r--portato/backend/portage/settings_22.py12
-rw-r--r--portato/backend/portage/system.py768
-rw-r--r--portato/backend/portage/system_22.py60
-rw-r--r--portato/backend/system_interface.py492
-rw-r--r--portato/config_parser.py958
-rw-r--r--portato/dependency.py260
-rw-r--r--portato/gui/__init__.py18
-rw-r--r--portato/gui/dialogs.py134
-rw-r--r--portato/gui/exception_handling.py196
-rw-r--r--portato/gui/queue.py1234
-rw-r--r--portato/gui/session.py16
-rw-r--r--portato/gui/updater.py200
-rw-r--r--portato/gui/utils.py530
-rw-r--r--portato/gui/views.py214
-rw-r--r--portato/gui/windows/about.py16
-rw-r--r--portato/gui/windows/basic.py180
-rw-r--r--portato/gui/windows/mailinfo.py106
-rw-r--r--portato/gui/windows/main.py3482
-rw-r--r--portato/gui/windows/plugin.py316
-rw-r--r--portato/gui/windows/preference.py384
-rw-r--r--portato/gui/windows/search.py98
-rw-r--r--portato/gui/windows/splash.py60
-rw-r--r--portato/gui/windows/update.py188
-rw-r--r--portato/gui/wrapper.py622
-rw-r--r--portato/helper.py214
-rw-r--r--portato/plistener.py222
-rw-r--r--portato/plugin.py966
-rw-r--r--portato/session.py202
-rw-r--r--portato/waiting_queue.py88
-rw-r--r--setup.py28
48 files changed, 7854 insertions, 7854 deletions
diff --git a/plugins/etc_proposals.py b/plugins/etc_proposals.py
index c32c8f3..5b4f67a 100644
--- a/plugins/etc_proposals.py
+++ b/plugins/etc_proposals.py
@@ -16,26 +16,26 @@ import os
from subprocess import Popen
class EtcProposals (Plugin):
- __author__ = "René 'Necoro' Neumann"
- __description__ = "Adds support for <b>etc-proposals</b>, a graphical etc-update replacement."
- __dependency__ = ["app-portage/etc-proposals"]
-
- def init (self):
- self.prog = ["/usr/sbin/etc-proposals"]
- self.add_call("after_emerge", self.hook, type = "after")
- self.add_menu("Et_c-Proposals", self.menu)
-
- def launch (self, options = []):
- if os.getuid() == 0:
- Popen(self.prog+options)
- else:
- error("ETC_PROPOSALS :: %s",_("Cannot start etc-proposals. Not root!"))
-
- def hook (self, *args, **kwargs):
- """Entry point for this plugin."""
- self.launch(["--fastexit"])
-
- def menu (self, *args):
- self.launch()
+ __author__ = "René 'Necoro' Neumann"
+ __description__ = "Adds support for <b>etc-proposals</b>, a graphical etc-update replacement."
+ __dependency__ = ["app-portage/etc-proposals"]
+
+ def init (self):
+ self.prog = ["/usr/sbin/etc-proposals"]
+ self.add_call("after_emerge", self.hook, type = "after")
+ self.add_menu("Et_c-Proposals", self.menu)
+
+ def launch (self, options = []):
+ if os.getuid() == 0:
+ Popen(self.prog+options)
+ else:
+ error("ETC_PROPOSALS :: %s",_("Cannot start etc-proposals. Not root!"))
+
+ def hook (self, *args, **kwargs):
+ """Entry point for this plugin."""
+ self.launch(["--fastexit"])
+
+ def menu (self, *args):
+ self.launch()
register(EtcProposals)
diff --git a/plugins/exception.py b/plugins/exception.py
index e653853..ff5a8e9 100644
--- a/plugins/exception.py
+++ b/plugins/exception.py
@@ -11,7 +11,7 @@
# Written by René 'Necoro' Neumann <necoro@necoro.net>
def throw (*args, **kwargs):
- raise Exception, "As requested, Sir!"
+ raise Exception, "As requested, Sir!"
p = Plugin()
p.__name__ = "ExceptionThrower"
diff --git a/plugins/gpytage.py b/plugins/gpytage.py
index d8c2831..5ebd6b4 100644
--- a/plugins/gpytage.py
+++ b/plugins/gpytage.py
@@ -13,14 +13,14 @@
from subprocess import Popen
class GPytage (Plugin):
- __author__ = "René 'Necoro' Neumann"
- __description__ = "Adds a menu entry to directly start <b>gpytage</b>, a config editor."
- __dependency__ = ["app-portage/gpytage"]
+ __author__ = "René 'Necoro' Neumann"
+ __description__ = "Adds a menu entry to directly start <b>gpytage</b>, a config editor."
+ __dependency__ = ["app-portage/gpytage"]
- def init (self):
- self.add_menu("Config _Editor", self.menu)
+ def init (self):
+ self.add_menu("Config _Editor", self.menu)
- def menu (self, *args):
- Popen(["/usr/bin/gpytage"])
+ def menu (self, *args):
+ Popen(["/usr/bin/gpytage"])
register(GPytage)
diff --git a/plugins/new_version.py b/plugins/new_version.py
index f3479b4..5391507 100644
--- a/plugins/new_version.py
+++ b/plugins/new_version.py
@@ -11,9 +11,9 @@
# Written by René 'Necoro' Neumann <necoro@necoro.net>
try:
- from bzrlib import plugin, branch
+ from bzrlib import plugin, branch
except ImportError:
- plugin = branch = None
+ plugin = branch = None
import gobject
from portato.helper import debug, warning
@@ -22,59 +22,59 @@ from portato.constants import VERSION, APP_ICON, APP
from portato.gui.utils import GtkThread
class NewVersionFinder(Plugin):
- """
- Checks for a new version of portato every 30 minutes and on startup.
- """
- __author__ = "René 'Necoro' Neumann"
- __dependency__ = ["dev-util/bzr"]
+ """
+ Checks for a new version of portato every 30 minutes and on startup.
+ """
+ __author__ = "René 'Necoro' Neumann"
+ __dependency__ = ["dev-util/bzr"]
- def init (self):
- self.add_call("main", self.run)
- self.add_menu("Check for new _versions", self.menu)
+ def init (self):
+ self.add_call("main", self.run)
+ self.add_menu("Check for new _versions", self.menu)
- def find_version (self, rev):
- try:
- b = branch.Branch.open("lp:portato")
- except Exception, e:
- warning("NEW_VERSION :: Exception occured while accessing the remote branch: %s", str(e))
- return
+ def find_version (self, rev):
+ try:
+ b = branch.Branch.open("lp:portato")
+ except Exception, e:
+ warning("NEW_VERSION :: Exception occured while accessing the remote branch: %s", str(e))
+ return
- debug("NEW_VERSION :: Installed rev: %s - Current rev: %s", rev, b.revno())
- if int(rev) < int(b.revno()):
- def callback():
- get_listener().send_notify(base = "New Portato Live Version Found", descr = "You have rev. %s, but the most recent revision is %s." % (rev, b.revno()), icon = APP_ICON)
- return False
-
- gobject.idle_add(callback)
+ debug("NEW_VERSION :: Installed rev: %s - Current rev: %s", rev, b.revno())
+ if int(rev) < int(b.revno()):
+ def callback():
+ get_listener().send_notify(base = "New Portato Live Version Found", descr = "You have rev. %s, but the most recent revision is %s." % (rev, b.revno()), icon = APP_ICON)
+ return False
+
+ gobject.idle_add(callback)
- def start_thread(self, rev):
- t = GtkThread(target = self.find_version, name = "Version Updater Thread", args = (rev,))
- t.setDaemon(True)
- t.start()
- return True
+ def start_thread(self, rev):
+ t = GtkThread(target = self.find_version, name = "Version Updater Thread", args = (rev,))
+ t.setDaemon(True)
+ t.start()
+ return True
- def menu (self, *args, **kwargs):
- """
- Run the thread once.
- """
- v = VERSION.split()
- if len(v) != 3 or v[0] != "9999":
- return None
+ def menu (self, *args, **kwargs):
+ """
+ Run the thread once.
+ """
+ v = VERSION.split()
+ if len(v) != 3 or v[0] != "9999":
+ return None
- rev = v[-1]
+ rev = v[-1]
- plugin.load_plugins() # to have lp: addresses parsed
-
- self.start_thread(rev)
- return rev
+ plugin.load_plugins() # to have lp: addresses parsed
+
+ self.start_thread(rev)
+ return rev
- def run (self, *args, **kwargs):
- """
- Run the thread once and add a 30 minutes timer.
- """
- rev = self.menu()
+ def run (self, *args, **kwargs):
+ """
+ Run the thread once and add a 30 minutes timer.
+ """
+ rev = self.menu()
- if rev is not None:
- gobject.timeout_add(30*60*1000, self.start_thread, rev) # call it every 30 minutes
+ if rev is not None:
+ gobject.timeout_add(30*60*1000, self.start_thread, rev) # call it every 30 minutes
register(NewVersionFinder, (branch is None))
diff --git a/plugins/notify.py b/plugins/notify.py
index 6446812..7a3776a 100644
--- a/plugins/notify.py
+++ b/plugins/notify.py
@@ -13,9 +13,9 @@
disable = False
try:
- import pynotify
+ import pynotify
except ImportError:
- disable = True
+ disable = True
from portato import get_listener
@@ -23,27 +23,27 @@ from portato.helper import warning, error, debug
from portato.constants import APP_ICON, APP
class Notify (Plugin):
- __author__ = "René 'Necoro' Neumann"
- __description__ = "Show notifications when an emerge process finishes."
- __dependency__ = ["dev-python/notify-python"]
-
- def init (self):
- self.add_call("after_emerge", self.notify)
-
- def notify (self, retcode, **kwargs):
- if retcode is None:
- warning("NOTIFY :: %s", _("Notify called while process is still running!"))
- else:
- icon = APP_ICON
- if retcode == 0:
- text = _("Emerge finished!")
- descr = ""
- urgency = pynotify.URGENCY_NORMAL
- else:
- text = _("Emerge failed!")
- descr = _("Error Code: %d") % retcode
- urgency = pynotify.URGENCY_CRITICAL
-
- get_listener().send_notify(base = text, descr = descr, icon = icon, urgency = urgency)
+ __author__ = "René 'Necoro' Neumann"
+ __description__ = "Show notifications when an emerge process finishes."
+ __dependency__ = ["dev-python/notify-python"]
+
+ def init (self):
+ self.add_call("after_emerge", self.notify)
+
+ def notify (self, retcode, **kwargs):
+ if retcode is None:
+ warning("NOTIFY :: %s", _("Notify called while process is still running!"))
+ else:
+ icon = APP_ICON
+ if retcode == 0:
+ text = _("Emerge finished!")
+ descr = ""
+ urgency = pynotify.URGENCY_NORMAL
+ else:
+ text = _("Emerge failed!")
+ descr = _("Error Code: %d") % retcode
+ urgency = pynotify.URGENCY_CRITICAL
+
+ get_listener().send_notify(base = text, descr = descr, icon = icon, urgency = urgency)
register(Notify, disable)
diff --git a/plugins/reload_portage.py b/plugins/reload_portage.py
index 280bd92..1aea01f 100644
--- a/plugins/reload_portage.py
+++ b/plugins/reload_portage.py
@@ -13,15 +13,15 @@
from portato.backend import system
class ReloadPortage (Plugin):
- __author__ = "René 'Necoro' Neumann"
- __description__ = """Reloads portage when an emerge process has finished.
+ __author__ = "René 'Necoro' Neumann"
+ __description__ = """Reloads portage when an emerge process has finished.
This can take some time, but sometimes it is necessairy."""
-
- def init(self):
- self.add_call("after_emerge", self.hook, type = "after", dep = "EtcProposals")
- self.status = self.STAT_DISABLED # disable by default
-
- def hook (self, *args, **kwargs):
- system.reload_settings()
+
+ def init(self):
+ self.add_call("after_emerge", self.hook, type = "after", dep = "EtcProposals")
+ self.status = self.STAT_DISABLED # disable by default
+
+ def hook (self, *args, **kwargs):
+ system.reload_settings()
register(ReloadPortage)
diff --git a/portato.py b/portato.py
index cc973be..c2b861e 100755
--- a/portato.py
+++ b/portato.py
@@ -25,73 +25,73 @@ from portato.helper import debug, info
from portato.constants import VERSION, LOCALE_DIR, APP, SU_COMMAND
def main ():
- # set gettext stuff
- locale.setlocale(locale.LC_ALL, '')
- gettext.install(APP, LOCALE_DIR, unicode = True)
+ # set gettext stuff
+ locale.setlocale(locale.LC_ALL, '')
+ gettext.install(APP, LOCALE_DIR, unicode = True)
- # build the parser
- desc = "Portato - A Portage GUI."
- usage = "%prog [options] [frontend]"
- vers = "%%prog v. %s" % VERSION
+ # build the parser
+ desc = "Portato - A Portage GUI."
+ usage = "%prog [options] [frontend]"
+ vers = "%%prog v. %s" % VERSION
- parser = OptionParser(version = vers, prog = "Portato", description = desc, usage = usage)
-
- parser.add_option("--shm", action = "store", nargs = 3, type="long", dest = "shm",
- help = SUPPRESS_HELP)
+ parser = OptionParser(version = vers, prog = "Portato", description = desc, usage = usage)
+
+ parser.add_option("--shm", action = "store", nargs = 3, type="long", dest = "shm",
+ help = SUPPRESS_HELP)
- parser.add_option("-F", "--no-fork", "-L", action = "store_true", dest = "nofork", default = False,
- help = _("do not fork off as root") + (" (%s)" % _("-L is deprecated")))
+ parser.add_option("-F", "--no-fork", "-L", action = "store_true", dest = "nofork", default = False,
+ help = _("do not fork off as root") + (" (%s)" % _("-L is deprecated")))
- # run parser
- (options, args) = parser.parse_args()
+ # run parser
+ (options, args) = parser.parse_args()
- # close listener at exit
- atexit.register(get_listener().close)
+ # close listener at exit
+ atexit.register(get_listener().close)
- if options.nofork or os.getuid() == 0: # start GUI
- from portato.gui import run
- info("%s v. %s", _("Starting Portato"), VERSION)
-
- if options.shm:
- get_listener().set_send(*options.shm)
- else:
- get_listener().set_send()
-
- try:
- run()
- except KeyboardInterrupt:
- debug("Got KeyboardInterrupt.")
-
- else: # start us again in root modus and launch listener
-
- import shm_wrapper as shm
+ if options.nofork or os.getuid() == 0: # start GUI
+ from portato.gui import run
+ info("%s v. %s", _("Starting Portato"), VERSION)
+
+ if options.shm:
+ get_listener().set_send(*options.shm)
+ else:
+ get_listener().set_send()
+
+ try:
+ run()
+ except KeyboardInterrupt:
+ debug("Got KeyboardInterrupt.")
+
+ else: # start us again in root modus and launch listener
+
+ import shm_wrapper as shm
- mem = shm.create_memory(1024, permissions=0600)
- sig = shm.create_semaphore(InitialValue = 0, permissions = 0600)
- rw = shm.create_semaphore(InitialValue = 1, permissions = 0600)
-
- # start listener
- lt = threading.Thread(target=get_listener().set_recv, args = (mem, sig, rw))
- lt.setDaemon(False)
- lt.start()
-
- # set DBUS_SESSION_BUS_ADDRESS to "" to make dbus work as root ;)
- env = os.environ.copy()
- env.update(DBUS_SESSION_BUS_ADDRESS="")
- cmd = SU_COMMAND.split()
-
- sp = subprocess.Popen(cmd+["%s --no-fork --shm %ld %ld %ld" % (sys.argv[0], mem.key, sig.key, rw.key)], env = env)
+ mem = shm.create_memory(1024, permissions=0600)
+ sig = shm.create_semaphore(InitialValue = 0, permissions = 0600)
+ rw = shm.create_semaphore(InitialValue = 1, permissions = 0600)
+
+ # start listener
+ lt = threading.Thread(target=get_listener().set_recv, args = (mem, sig, rw))
+ lt.setDaemon(False)
+ lt.start()
+
+ # set DBUS_SESSION_BUS_ADDRESS to "" to make dbus work as root ;)
+ env = os.environ.copy()
+ env.update(DBUS_SESSION_BUS_ADDRESS="")
+ cmd = SU_COMMAND.split()
+
+ sp = subprocess.Popen(cmd+["%s --no-fork --shm %ld %ld %ld" % (sys.argv[0], mem.key, sig.key, rw.key)], env = env)
- # wait for process to finish
- try:
- sp.wait()
- debug("Subprocess finished")
- except KeyboardInterrupt:
- debug("Got KeyboardInterrupt.")
+ # wait for process to finish
+ try:
+ sp.wait()
+ debug("Subprocess finished")
+ except KeyboardInterrupt:
+ debug("Got KeyboardInterrupt.")
- if lt.isAlive():
- debug("Listener is still running. Close it.")
- get_listener().close()
+ if lt.isAlive():
+ debug("Listener is still running. Close it.")
+ get_listener().close()
if __name__ == "__main__":
- main()
+ main()
diff --git a/portato/TEST_helper.py b/portato/TEST_helper.py
index cce0b61..f0b069b 100644
--- a/portato/TEST_helper.py
+++ b/portato/TEST_helper.py
@@ -5,28 +5,28 @@ import helper
class HelperTest (unittest.TestCase):
- def testFlatten(self):
- list = [[1,2],[3,4],[[5],[6,7,8], 9]]
- flist = helper.flatten(list)
- self.assertEqual(flist, [1,2,3,4,5,6,7,8,9], "List not flattend correctly.")
+ def testFlatten(self):
+ list = [[1,2],[3,4],[[5],[6,7,8], 9]]
+ flist = helper.flatten(list)
+ self.assertEqual(flist, [1,2,3,4,5,6,7,8,9], "List not flattend correctly.")
- def testUniqueArray(self):
+ def testUniqueArray(self):
- def equal (l1, l2):
- for i in l1:
- if i not in l2:
- return False
- l2.remove(i)
- return True
+ def equal (l1, l2):
+ for i in l1:
+ if i not in l2:
+ return False
+ l2.remove(i)
+ return True
- list1 = [1,4,5,2,1,7,9,11,2,4,7,12]
- result1 = [1,4,5,2,7,9,11,12]
+ list1 = [1,4,5,2,1,7,9,11,2,4,7,12]
+ result1 = [1,4,5,2,7,9,11,12]
- list2 = [[x] for x in list1]
- result2 = [[x] for x in result1]
+ list2 = [[x] for x in list1]
+ result2 = [[x] for x in result1]
- self.assert_(equal(helper.unique_array(list1), result1), "Make hashable list unique does not work.")
- self.assert_(equal(helper.unique_array(list2), result2), "Make unhashable list unique does not work.")
+ self.assert_(equal(helper.unique_array(list1), result1), "Make hashable list unique does not work.")
+ self.assert_(equal(helper.unique_array(list2), result2), "Make unhashable list unique does not work.")
if __name__ == "__main__":
- unittest.main()
+ unittest.main()
diff --git a/portato/__init__.py b/portato/__init__.py
index 4b6a808..b5a9859 100644
--- a/portato/__init__.py
+++ b/portato/__init__.py
@@ -18,36 +18,36 @@ import os
class OutputFormatter (logging.Formatter):
- colors = {
- "blue" : 34,
- "green" : 32,
- "red" : 31,
- "yellow": 33
- }
+ colors = {
+ "blue" : 34,
+ "green" : 32,
+ "red" : 31,
+ "yellow": 33
+ }
- def __init__(self, *args, **kwargs):
- logging.Formatter.__init__(self, *args, **kwargs)
+ def __init__(self, *args, **kwargs):
+ logging.Formatter.__init__(self, *args, **kwargs)
- for key, value in self.colors.iteritems():
- self.colors[key] = "\x1b[01;%02dm*\x1b[39;49;00m" % value
+ for key, value in self.colors.iteritems():
+ self.colors[key] = "\x1b[01;%02dm*\x1b[39;49;00m" % value
- def format (self, record):
- string = logging.Formatter.format(self, record)
- color = None
+ def format (self, record):
+ string = logging.Formatter.format(self, record)
+ color = None
- if os.isatty(sys.stderr.fileno()):
- if record.levelno <= logging.DEBUG:
- color = self.colors["blue"]
- elif record.levelno <= logging.INFO:
- color = self.colors["green"]
- elif record.levelno <= logging.WARNING:
- color = self.colors["yellow"]
- else:
- color = self.colors["red"]
- else:
- color = "%s:" % record.levelname
+ if os.isatty(sys.stderr.fileno()):
+ if record.levelno <= logging.DEBUG:
+ color = self.colors["blue"]
+ elif record.levelno <= logging.INFO:
+ color = self.colors["green"]
+ elif record.levelno <= logging.WARNING:
+ color = self.colors["yellow"]
+ else:
+ color = self.colors["red"]
+ else:
+ color = "%s:" % record.levelname
- return "%s %s" % (color, string)
+ return "%s %s" % (color, string)
# set the whole logging stuff
formatter = OutputFormatter("%(message)s (%(filename)s:%(lineno)s)")
@@ -61,9 +61,9 @@ logging.getLogger("portatoLogger").propagate = False
__listener = None
def get_listener():
- global __listener
- if __listener is None:
- from .plistener import PListener
- __listener = PListener()
-
- return __listener
+ global __listener
+ if __listener is None:
+ from .plistener import PListener
+ __listener = PListener()
+
+ return __listener
diff --git a/portato/backend/__init__.py b/portato/backend/__init__.py
index b2a5a43..1f62d6b 100644
--- a/portato/backend/__init__.py
+++ b/portato/backend/__init__.py
@@ -20,52 +20,52 @@ SYSTEM = "portage" # the name of the current system
_sys = None # the SystemInterface-instance
class _Package (object):
- """Wrapping class from which L{portato.backend.Package} inherits. This is used by the flags module to check
- whether an object is a package. It cannot use the normal Package class as this results in cyclic dependencies."""
+ """Wrapping class from which L{portato.backend.Package} inherits. This is used by the flags module to check
+ whether an object is a package. It cannot use the normal Package class as this results in cyclic dependencies."""
- def __init__ (self):
- raise TypeError, "Calling __init__ on portato.backend._Package objects is not allowed."
+ def __init__ (self):
+ raise TypeError, "Calling __init__ on portato.backend._Package objects is not allowed."
class SystemWrapper (SystemInterface):
- """This is a wrapper to the different system interfaces, allowing the direct import via C{from portato.backend import system}.
- With this wrapper a change of the system is propagated to all imports."""
-
- def __getattribute__ (self, name):
- """Just pass all attribute accesses directly to _sys."""
- return getattr(_sys, name)
+ """This is a wrapper to the different system interfaces, allowing the direct import via C{from portato.backend import system}.
+ With this wrapper a change of the system is propagated to all imports."""
+
+ def __getattribute__ (self, name):
+ """Just pass all attribute accesses directly to _sys."""
+ return getattr(_sys, name)
def set_system (new_sys):
- """Sets the current system to a new one.
+ """Sets the current system to a new one.
- @param new_sys: the name of the system to take
- @type new_sys: string"""
+ @param new_sys: the name of the system to take
+ @type new_sys: string"""
- global SYSTEM
- if new_sys != SYSTEM:
- SYSTEM = new_sys
- load_system()
+ global SYSTEM
+ if new_sys != SYSTEM:
+ SYSTEM = new_sys
+ load_system()
def load_system ():
- """Loads the current chosen system.
+ """Loads the current chosen system.
- @raises InvalidSystemError: if an inappropriate system is set"""
-
- global _sys
+ @raises InvalidSystemError: if an inappropriate system is set"""
+
+ global _sys
- if SYSTEM == "portage":
- debug("Setting Portage System")
- from .portage import PortageSystem
- _sys = PortageSystem ()
- elif SYSTEM == "catapult":
- debug("Setting Catapult System")
- from .catapult import CatapultSystem
- _sys = CatapultSystem()
- else:
- raise InvalidSystemError, SYSTEM
+ if SYSTEM == "portage":
+ debug("Setting Portage System")
+ from .portage import PortageSystem
+ _sys = PortageSystem ()
+ elif SYSTEM == "catapult":
+ debug("Setting Catapult System")
+ from .catapult import CatapultSystem
+ _sys = CatapultSystem()
+ else:
+ raise InvalidSystemError, SYSTEM
system = SystemWrapper()
def is_package(what):
- return isinstance(what, _Package)
+ return isinstance(what, _Package)
load_system()
diff --git a/portato/backend/exceptions.py b/portato/backend/exceptions.py
index 42f9d44..37e9bda 100644
--- a/portato/backend/exceptions.py
+++ b/portato/backend/exceptions.py
@@ -11,17 +11,17 @@
# Written by René 'Necoro' Neumann <necoro@necoro.net>
class BlockedException (Exception):
- """An exception marking, that some package is blocking another one."""
- pass
+ """An exception marking, that some package is blocking another one."""
+ pass
class PackageNotFoundException (Exception):
- """An exception marking that a package could not be found."""
- pass
+ """An exception marking that a package could not be found."""
+ pass
class DependencyCalcError (Exception):
- """An error occured during dependency calculation."""
- pass
+ """An error occured during dependency calculation."""
+ pass
class InvalidSystemError (Exception):
- """An invalid system is set."""
- pass
+ """An invalid system is set."""
+ pass
diff --git a/portato/backend/flags.py b/portato/backend/flags.py
index b151665..4aa6b8e 100644
--- a/portato/backend/flags.py
+++ b/portato/backend/flags.py
@@ -20,738 +20,738 @@ from . import system, is_package
from ..helper import debug, error, unique_array
CONFIG = {
- "usefile" : "portato",
- "maskfile" : "portato",
- "testingfile" : "portato",
- "usePerVersion" : True,
- "maskPerVersion" : True,
- "testingPerVersion" : True
- }
+ "usefile" : "portato",
+ "maskfile" : "portato",
+ "testingfile" : "portato",
+ "usePerVersion" : True,
+ "maskPerVersion" : True,
+ "testingPerVersion" : True
+ }
class Constants:
- def __init__ (self):
- self.clear()
-
- def clear (self):
- self._use_path = None
- self._mask_path = None
- self._unmask_path = None
- self._testing_path = None
- self._use_path_is_dir = None
- self._mask_path_is_dir = None
- self._unmask_path_is_dir = None
- self._testing_path_is_dir = None
+ def __init__ (self):
+ self.clear()
+
+ def clear (self):
+ self._use_path = None
+ self._mask_path = None
+ self._unmask_path = None
+ self._testing_path = None
+ self._use_path_is_dir = None
+ self._mask_path_is_dir = None
+ self._unmask_path_is_dir = None
+ self._testing_path_is_dir = None
- def __get (self, name, path):
- if self.__dict__[name] is None:
- self.__dict__[name] = os.path.join(system.get_config_path(), path)
+ def __get (self, name, path):
+ if self.__dict__[name] is None:
+ self.__dict__[name] = os.path.join(system.get_config_path(), path)
- return self.__dict__[name]
+ return self.__dict__[name]
- def __is_dir(self, path):
- name = "_" + path + "_is_dir"
- if self.__dict__[name] is None:
- self.__dict__[name] = os.path.isdir(self.__class__.__dict__[path](self))
- return self.__dict__[name]
-
- def use_path (self):
- return self.__get("_use_path", "package.use")
+ def __is_dir(self, path):
+ name = "_" + path + "_is_dir"
+ if self.__dict__[name] is None:
+ self.__dict__[name] = os.path.isdir(self.__class__.__dict__[path](self))
+ return self.__dict__[name]
+
+ def use_path (self):
+ return self.__get("_use_path", "package.use")
- def use_path_is_dir (self):
- return self.__is_dir("use_path")
+ def use_path_is_dir (self):
+ return self.__is_dir("use_path")
- def mask_path (self):
- return self.__get("_mask_path", "package.mask")
+ def mask_path (self):
+ return self.__get("_mask_path", "package.mask")
- def mask_path_is_dir (self):
- return self.__is_dir("mask_path")
+ def mask_path_is_dir (self):
+ return self.__is_dir("mask_path")
- def unmask_path (self):
- return self.__get("_unmask_path", "package.unmask")
+ def unmask_path (self):
+ return self.__get("_unmask_path", "package.unmask")
- def unmask_path_is_dir (self):
- return self.__is_dir("unmask_path")
+ def unmask_path_is_dir (self):
+ return self.__is_dir("unmask_path")
- def testing_path (self):
- return self.__get("_testing_path", "package.keywords")
+ def testing_path (self):
+ return self.__get("_testing_path", "package.keywords")
- def testing_path_is_dir (self):
- return self.__is_dir("testing_path")
+ def testing_path_is_dir (self):
+ return self.__is_dir("testing_path")
CONST = Constants()
### GENERAL PART ###
def grep (pkg, path):
- """Grep runs "egrep" on a given path and looks for occurences of a given package.
- @param pkg: the package
- @type pkg: string (cpv) or L{backend.Package}-object
- @param path: path to look in
- @type path: string
-
- @returns: occurences of pkg in the format: "file:line-no:complete_line_found"
- @rtype: string"""
-
- if not is_package(pkg):
- pkg = system.new_package(pkg) # assume it is a cpv or a gentoolkit.Package
-
- if os.path.exists(path):
- command = "egrep -x -n -r -H '^[<>!=~]{0,2}%s(-[0-9].*)?[[:space:]]?.*$' %s" # %s is replaced in the next line ;)
- return Popen((command % (pkg.get_cp(), path)), shell = True, stdout = PIPE).communicate()[0].splitlines()
- else:
- return []
+ """Grep runs "egrep" on a given path and looks for occurences of a given package.
+ @param pkg: the package
+ @type pkg: string (cpv) or L{backend.Package}-object
+ @param path: path to look in
+ @type path: string
+
+ @returns: occurences of pkg in the format: "file:line-no:complete_line_found"
+ @rtype: string"""
+
+ if not is_package(pkg):
+ pkg = system.new_package(pkg) # assume it is a cpv or a gentoolkit.Package
+
+ if os.path.exists(path):
+ command = "egrep -x -n -r -H '^[<>!=~]{0,2}%s(-[0-9].*)?[[:space:]]?.*$' %s" # %s is replaced in the next line ;)
+ return Popen((command % (pkg.get_cp(), path)), shell = True, stdout = PIPE).communicate()[0].splitlines()
+ else:
+ return []
def get_data(pkg, path):
- """This splits up the data of L{grep} and builds tuples in the format (file,line,criterion,list_of_flags).
- @param pkg: package to find
- @type pkg: string (cpv) or L{backend.Package}-object
- @param path: path to look in
- @type path: string
-
- @returns: a list of tuples in the form (file,line,criterion,list_of_flags)
- @rtype: (string,string,string,string[])[]"""
-
- flags = []
-
- # do grep
- list = grep(pkg, path)
-
- for i in range(len(list)):
- file, line, fl = tuple(list[i].split(":")) # get file, line and flag-list
- fl = fl.split()
- crit = fl[0]
- fl = fl[1:]
- # stop after first comment
- for j in range(len(fl)):
- if fl[j][0] == "#": # comment - stop here
- fl = fl[:j]
- break
- flags.append((file,line,crit,fl))
-
- return flags
+ """This splits up the data of L{grep} and builds tuples in the format (file,line,criterion,list_of_flags).
+ @param pkg: package to find
+ @type pkg: string (cpv) or L{backend.Package}-object
+ @param path: path to look in
+ @type path: string
+
+ @returns: a list of tuples in the form (file,line,criterion,list_of_flags)
+ @rtype: (string,string,string,string[])[]"""
+
+ flags = []
+
+ # do grep
+ list = grep(pkg, path)
+
+ for i in range(len(list)):
+ file, line, fl = tuple(list[i].split(":")) # get file, line and flag-list
+ fl = fl.split()
+ crit = fl[0]
+ fl = fl[1:]
+ # stop after first comment
+ for j in range(len(fl)):
+ if fl[j][0] == "#": # comment - stop here
+ fl = fl[:j]
+ break
+ flags.append((file,line,crit,fl))
+
+ return flags
def set_config (cfg):
- """This function sets the CONFIG-variable for the whole module. Use this instead of modifying L{CONFIG} directly.
- @param cfg: a dictionary with at least all the keys of the CONFIG-var
- @type cfg: dict
- @raises KeyError: if a keyword is missing in the new cfg"""
+ """This function sets the CONFIG-variable for the whole module. Use this instead of modifying L{CONFIG} directly.
+ @param cfg: a dictionary with at least all the keys of the CONFIG-var
+ @type cfg: dict
+ @raises KeyError: if a keyword is missing in the new cfg"""
- for i in CONFIG.keys():
- if not i in cfg:
- raise KeyError, "Missing keyword in config: "+i
+ for i in CONFIG.keys():
+ if not i in cfg:
+ raise KeyError, "Missing keyword in config: "+i
- for i in CONFIG:
- CONFIG[i] = cfg[i]
+ for i in CONFIG:
+ CONFIG[i] = cfg[i]
def generate_path (cpv, exp):
- """Generates the correct path out of given wildcards.
- These wildcards can be:
- - $(cat) : category
- - $(cat-1): first part of the category (e.g. "app")
- - $(cat-2): second part of the category
- - $(pkg) : name of the package
- - $(version) : version of the package
-
- @param cpv: the cpv of the current package
- @type cpv: string (cat/pkg-ver)
- @param exp: the expression to render the path from
- @type exp: string
- @returns: rendered path
- @rtype string"""
-
- if exp.find("$(") != -1:
- cat, pkg, ver, rev = system.split_cpv(cpv)
- if rev != "r0":
- ver = "%s-%s" % (ver, rev)
- exp = exp.replace("$(cat)",cat).\
- replace("$(pkg)",pkg).\
- replace("$(cat-1)",cat.split("-")[0]).\
- replace("$(cat-2)",cat.split("-")[1]).\
- replace("$(version)",ver)
- return exp
+ """Generates the correct path out of given wildcards.
+ These wildcards can be:
+ - $(cat) : category
+ - $(cat-1): first part of the category (e.g. "app")
+ - $(cat-2): second part of the category
+ - $(pkg) : name of the package
+ - $(version) : version of the package
+
+ @param cpv: the cpv of the current package
+ @type cpv: string (cat/pkg-ver)
+ @param exp: the expression to render the path from
+ @type exp: string
+ @returns: rendered path
+ @rtype string"""
+
+ if exp.find("$(") != -1:
+ cat, pkg, ver, rev = system.split_cpv(cpv)
+ if rev != "r0":
+ ver = "%s-%s" % (ver, rev)
+ exp = exp.replace("$(cat)",cat).\
+ replace("$(pkg)",pkg).\
+ replace("$(cat-1)",cat.split("-")[0]).\
+ replace("$(cat-2)",cat.split("-")[1]).\
+ replace("$(version)",ver)
+ return exp
### USE FLAG PART ###
useFlags = {} # useFlags in the file
newUseFlags = {} # useFlags as we want them to be: format: cpv -> [(file, line, useflag, (true if removed from list / false if added))]
def invert_use_flag (flag):
- """Invertes a flag.
-
- >>> invert_use_flag("foo")
- -foo
- >>> invert_use_flag("-bar")
- bar
-
- @param flag: the flag
- @type flag: string
- @returns: inverted flag
- @rtype: string
- """
-
- if flag[0] == "-":
- return flag[1:]
- else:
- return "-"+flag
+ """Invertes a flag.
+
+ >>> invert_use_flag("foo")
+ -foo
+ >>> invert_use_flag("-bar")
+ bar
+
+ @param flag: the flag
+ @type flag: string
+ @returns: inverted flag
+ @rtype: string
+ """
+
+ if flag[0] == "-":
+ return flag[1:]
+ else:
+ return "-"+flag
def sort_use_flag_list (flaglist):
- """
- Sorts a list of useflags. If a use flag starts with "+" or "-" this one is ignored for the matter of sorting.
- This functions sorts the list itself - thus does not create a new one. But for convenience it returns the list too.
-
- @param flaglist: the list of useflags
- @type flaglist: string[]
-
- @returns: the sorted list (Note: it is the same as the one passed in)
- @rtype: string[]
- """
-
- def flag_key (flag):
- if flag.startswith(("+","-")):
- return flag[1:]
- else:
- return flag
-
- flaglist.sort(key = flag_key)
- return flaglist
+ """
+ Sorts a list of useflags. If a use flag starts with "+" or "-" this one is ignored for the matter of sorting.
+ This functions sorts the list itself - thus does not create a new one. But for convenience it returns the list too.
+
+ @param flaglist: the list of useflags
+ @type flaglist: string[]
+
+ @returns: the sorted list (Note: it is the same as the one passed in)
+ @rtype: string[]
+ """
+
+ def flag_key (flag):
+ if flag.startswith(("+","-")):
+ return flag[1:]
+ else:
+ return flag
+
+ flaglist.sort(key = flag_key)
+ return flaglist
def filter_defaults (flaglist):
- """
- Removes "+" and "-" from IUSE defaults.
+ """
+ Removes "+" and "-" from IUSE defaults.
- @param flaglist: the list of useflags
- @type flaglist: string<iterator>
+ @param flaglist: the list of useflags
+ @type flaglist: string<iterator>
- @returns: the "cleaned" list
- @rtype: string<iterator>
- """
+ @returns: the "cleaned" list
+ @rtype: string<iterator>
+ """
- for flag in flaglist:
- if flag.startswith(("+","-")):
- yield flag[1:]
- else:
- yield flag
+ for flag in flaglist:
+ if flag.startswith(("+","-")):
+ yield flag[1:]
+ else:
+ yield flag
def set_use_flag (pkg, flag):
- """Sets the useflag for a given package.
-
- @param pkg: the package
- @type pkg: string (cpv) or L{backend.Package}-object
- @param flag: the flag to set
- @type flag: string"""
-
- global useFlags, newUseFlags
-
- if not is_package(pkg):
- pkg = system.new_package(pkg) # assume cpv or gentoolkit.Package
-
- cpv = pkg.get_cpv()
- invFlag = invert_use_flag(flag)
-
- # if not saved in useFlags, get it by calling get_data() which calls grep()
- data = None
- if not cpv in useFlags:
- data = get_data(pkg, CONST.use_path())
- useFlags[cpv] = data
- else:
- data = useFlags[cpv]
-
- if not cpv in newUseFlags:
- newUseFlags[cpv] = []
-
- debug("data: %s", str(data))
- # add a useflag / delete one
- added = False
- for file, line, crit, flags in data:
- if pkg.matches(crit):
- # we have the inverted flag in the uselist/newuselist --> delete it
- if invFlag in flags or (file, line, invFlag, False) in newUseFlags[cpv] or (file, line, flag, True) in newUseFlags[cpv]:
- if added: del newUseFlags[-1] # we currently added it as an extra option - delete it
- added = True
- jumpOut = False
- for t in ((file, line, invFlag, False),(file, line, flag, True)):
- if t in newUseFlags[cpv]:
- newUseFlags[cpv].remove(t)
- jumpOut = True
- # break # don't break as both cases can be valid (see below)
- if not jumpOut:
- newUseFlags[cpv].append((file, line, invFlag, True))
-
- # we removed the inverted from package.use - but it is still enabled somewhere else
- # so set it explicitly here
- if invFlag in pkg.get_actual_use_flags():
- newUseFlags[cpv].append((file, line, flag, False))
- break
-
- # we want to duplicate the flag --> ignore
- elif flag in flags:
- added = True # emulate adding
- break
-
- # add as an extra flag
- else:
- if not added: newUseFlags[cpv].append((file, line, flag, False))
- added = True
-
- # create a new line
- if not added:
- path = CONST.use_path()
- if CONST.use_path_is_dir():
- path = os.path.join(CONST.use_path(), generate_path(cpv, CONFIG["usefile"]))
- try:
- newUseFlags[cpv].remove((path, -1, invFlag, False))
- except ValueError: # not in UseFlags
- newUseFlags[cpv].append((path, -1, flag, False))
-
- newUseFlags[cpv] = unique_array(newUseFlags[cpv])
- debug("newUseFlags: %s", str(newUseFlags))
+ """Sets the useflag for a given package.
+
+ @param pkg: the package
+ @type pkg: string (cpv) or L{backend.Package}-object
+ @param flag: the flag to set
+ @type flag: string"""
+
+ global useFlags, newUseFlags
+
+ if not is_package(pkg):
+ pkg = system.new_package(pkg) # assume cpv or gentoolkit.Package
+
+ cpv = pkg.get_cpv()
+ invFlag = invert_use_flag(flag)
+
+ # if not saved in useFlags, get it by calling get_data() which calls grep()
+ data = None
+ if not cpv in useFlags:
+ data = get_data(pkg, CONST.use_path())
+ useFlags[cpv] = data
+ else:
+ data = useFlags[cpv]
+
+ if not cpv in newUseFlags:
+ newUseFlags[cpv] = []
+
+ debug("data: %s", str(data))
+ # add a useflag / delete one
+ added = False
+ for file, line, crit, flags in data:
+ if pkg.matches(crit):
+ # we have the inverted flag in the uselist/newuselist --> delete it
+ if invFlag in flags or (file, line, invFlag, False) in newUseFlags[cpv] or (file, line, flag, True) in newUseFlags[cpv]:
+ if added: del newUseFlags[-1] # we currently added it as an extra option - delete it
+ added = True
+ jumpOut = False
+ for t in ((file, line, invFlag, False),(file, line, flag, True)):
+ if t in newUseFlags[cpv]:
+ newUseFlags[cpv].remove(t)
+ jumpOut = True
+ # break # don't break as both cases can be valid (see below)
+ if not jumpOut:
+ newUseFlags[cpv].append((file, line, invFlag, True))
+
+ # we removed the inverted from package.use - but it is still enabled somewhere else
+ # so set it explicitly here
+ if invFlag in pkg.get_actual_use_flags():
+ newUseFlags[cpv].append((file, line, flag, False))
+ break
+
+ # we want to duplicate the flag --> ignore
+ elif flag in flags:
+ added = True # emulate adding
+ break
+
+ # add as an extra flag
+ else:
+ if not added: newUseFlags[cpv].append((file, line, flag, False))
+ added = True
+
+ # create a new line
+ if not added:
+ path = CONST.use_path()
+ if CONST.use_path_is_dir():
+ path = os.path.join(CONST.use_path(), generate_path(cpv, CONFIG["usefile"]))
+ try:
+ newUseFlags[cpv].remove((path, -1, invFlag, False))
+ except ValueError: # not in UseFlags
+ newUseFlags[cpv].append((path, -1, flag, False))
+
+ newUseFlags[cpv] = unique_array(newUseFlags[cpv])
+ debug("newUseFlags: %s", str(newUseFlags))
def remove_new_use_flags (cpv):
- """Removes all new use-flags for a specific package.
-
- @param cpv: the package for which to remove the flags
- @type cpv: string (cpv) or L{backend.Package}-object"""
-
- if is_package(cpv):
- cpv = cpv.get_cpv()
-
- try:
- del newUseFlags[cpv]
- except KeyError:
- pass
+ """Removes all new use-flags for a specific package.
+
+ @param cpv: the package for which to remove the flags
+ @type cpv: string (cpv) or L{backend.Package}-object"""
+
+ if is_package(cpv):
+ cpv = cpv.get_cpv()
+
+ try:
+ del newUseFlags[cpv]
+ except KeyError:
+ pass
def get_new_use_flags (cpv):
- """Gets all the new use-flags for a specific package.
-
- @param cpv: the package from which to get the flags
- @type cpv: string (cpv) or L{backend.Package}-object
- @returns: list of flags
- @rtype: string[]"""
-
- if is_package(cpv):
- cpv = cpv.get_cpv()
-
- list2return = set()
- try:
- for file, line, flag, remove in newUseFlags[cpv]:
- if remove:
- list2return.add("~"+invert_use_flag(flag))
- else:
- list2return.add(flag)
- except KeyError:
- pass
-
- return list(list2return)
+ """Gets all the new use-flags for a specific package.
+
+ @param cpv: the package from which to get the flags
+ @type cpv: string (cpv) or L{backend.Package}-object
+ @returns: list of flags
+ @rtype: string[]"""
+
+ if is_package(cpv):
+ cpv = cpv.get_cpv()
+
+ list2return = set()
+ try:
+ for file, line, flag, remove in newUseFlags[cpv]:
+ if remove:
+ list2return.add("~"+invert_use_flag(flag))
+ else:
+ list2return.add(flag)
+ except KeyError:
+ pass
+
+ return list(list2return)
def write_use_flags ():
- """This writes our changed useflags into the file."""
- global newUseFlags, useFlags
-
- def insert (flag, list):
- """Shortcut for inserting a new flag right after the package-name."""
- list.insert(1,flag)
-
- def remove (flag, list):
- """Removes a flag."""
- try:
- list.remove(flag)
- except ValueError: # flag is given as flag\n
- list.remove(flag+"\n")
- list.append("\n") #re-insert the newline
-
- # no more flags there - comment it out
- if len(list) == 1 or list[1][0] in ("#","\n"):
- list[0] = "#"+list[0]
- insert("#removed by portato#",list)
-
- file_cache = {} # cache for having to read the file only once: name->[lines]
- for cpv in newUseFlags:
- flagsToAdd = [] # this is used for collecting the flags to be inserted in a _new_ line
-
- newUseFlags[cpv].sort(key = lambda x: x[3]) # now the flags are sorted in a manner, that removal comes after appending
-
- for file, line, flag, delete in newUseFlags[cpv]:
- line = int(line) # it is saved as a string so far!
- # add new line
- if line == -1:
- flagsToAdd.append(flag)
- # change a line
- else:
- if not file in file_cache:
- # read file
- with open(file, "r") as f:
- lines = []
- i = 1
- while i < line: # stop at the given line
- lines.append(f.readline())
- i += 1
- l = f.readline().split(" ")
-
- # delete or insert
- if delete:
- remove(flag,l)
- else:
- insert(flag,l)
- lines.append(" ".join(l))
-
- # read the rest
- lines.extend(f.readlines())
-
- file_cache[file] = lines
-
- else: # in cache
- l = file_cache[file][line-1].split(" ")
- if delete:
- remove(flag, l)
- else:
- insert(flag,l)
- file_cache[file][line-1] = " ".join(l)
-
- if flagsToAdd:
- # write new lines
- msg = "\n#portato update#\n"
- if CONFIG["usePerVersion"]: # add on a per-version-base
- msg += "=%s %s\n" % (cpv, ' '.join(flagsToAdd))
- else: # add on a per-package-base
- list = system.split_cpv(cpv)
- msg += "%s/%s %s\n" % (list[0], list[1], ' '.join(flagsToAdd))
- if not file in file_cache:
- f = open(file, "a")
- f.write(msg)
- f.close()
- else:
- file_cache[file].append(msg)
-
- # write to disk
- for file in file_cache.keys():
- f = open(file, "w")
- f.writelines(file_cache[file])
- f.close()
- # reset
- useFlags = {}
- newUseFlags = {}
- system.reload_settings()
+ """This writes our changed useflags into the file."""
+ global newUseFlags, useFlags
+
+ def insert (flag, list):
+ """Shortcut for inserting a new flag right after the package-name."""
+ list.insert(1,flag)
+
+ def remove (flag, list):
+ """Removes a flag."""
+ try:
+ list.remove(flag)
+ except ValueError: # flag is given as flag\n
+ list.remove(flag+"\n")
+ list.append("\n") #re-insert the newline
+
+ # no more flags there - comment it out
+ if len(list) == 1 or list[1][0] in ("#","\n"):
+ list[0] = "#"+list[0]
+ insert("#removed by portato#",list)
+
+ file_cache = {} # cache for having to read the file only once: name->[lines]
+ for cpv in newUseFlags:
+ flagsToAdd = [] # this is used for collecting the flags to be inserted in a _new_ line
+
+ newUseFlags[cpv].sort(key = lambda x: x[3]) # now the flags are sorted in a manner, that removal comes after appending
+
+ for file, line, flag, delete in newUseFlags[cpv]:
+ line = int(line) # it is saved as a string so far!
+ # add new line
+ if line == -1:
+ flagsToAdd.append(flag)
+ # change a line
+ else:
+ if not file in file_cache:
+ # read file
+ with open(file, "r") as f:
+ lines = []
+ i = 1
+ while i < line: # stop at the given line
+ lines.append(f.readline())
+ i += 1
+ l = f.readline().split(" ")
+
+ # delete or insert
+ if delete:
+ remove(flag,l)
+ else:
+ insert(flag,l)
+ lines.append(" ".join(l))
+
+ # read the rest
+ lines.extend(f.readlines())
+
+ file_cache[file] = lines
+
+ else: # in cache
+ l = file_cache[file][line-1].split(" ")
+ if delete:
+ remove(flag, l)
+ else:
+ insert(flag,l)
+ file_cache[file][line-1] = " ".join(l)
+
+ if flagsToAdd:
+ # write new lines
+ msg = "\n#portato update#\n"
+ if CONFIG["usePerVersion"]: # add on a per-version-base
+ msg += "=%s %s\n" % (cpv, ' '.join(flagsToAdd))
+ else: # add on a per-package-base
+ list = system.split_cpv(cpv)
+ msg += "%s/%s %s\n" % (list[0], list[1], ' '.join(flagsToAdd))
+ if not file in file_cache:
+ f = open(file, "a")
+ f.write(msg)
+ f.close()
+ else:
+ file_cache[file].append(msg)
+
+ # write to disk
+ for file in file_cache.keys():
+ f = open(file, "w")
+ f.writelines(file_cache[file])
+ f.close()
+ # reset
+ useFlags = {}
+ newUseFlags = {}
+ system.reload_settings()
### MASKING PART ###
new_masked = {}
new_unmasked = {}
def set_masked (pkg, masked = True):
- """Sets the masking status of the package.
-
- @param pkg: the package from which to get the flags
- @type pkg: string (cpv) or L{backend.Package}-object
- @param masked: if True: mask it; if False: unmask it
- @type masked: boolean"""
-
- global new_masked, newunmasked
-
- if not is_package(pkg):
- pkg = system.new_package(pkg)
-
- cpv = pkg.get_cpv()
-
- if not cpv in new_unmasked:
- new_unmasked[cpv] = []
- if not cpv in new_masked:
- new_masked[cpv] = []
-
- if masked:
- link_neq = new_masked
- link_eq = new_unmasked
- path = CONST.unmask_path()
- else:
- link_neq = new_unmasked
- link_eq = new_masked
- path = CONST.mask_path()
-
- copy = link_eq[cpv][:]
- for file, line in copy:
- if line == "-1":
- link_eq[cpv].remove((file, line))
-
- copy = link_neq[cpv][:]
- for file, line in copy:
- if line != "-1":
- link_neq[cpv].remove((file, line))
-
- if masked == pkg.is_masked():
- return
-
- data = get_data(pkg, path)
- debug("data: %s", str(data))
- done = False
- for file, line, crit, flags in data:
- if pkg.matches(crit):
- link_eq[cpv].append((file, line))
- done = True
-
- if done: return
-
- if masked:
- is_dir = CONST.mask_path_is_dir()
- path = CONST.mask_path()
- else:
- is_dir = CONST.unmask_path_is_dir()
- path = CONST.unmask_path()
-
- if is_dir:
- file = os.path.join(path, generate_path(cpv, CONFIG["maskfile"]))
- else:
- file = path
-
- link_neq[cpv].append((file, "-1"))
- link_neq[cpv] = unique_array(link_neq[cpv])
- debug("new_(un)masked: %s",str(link_neq))
+ """Sets the masking status of the package.
+
+ @param pkg: the package from which to get the flags
+ @type pkg: string (cpv) or L{backend.Package}-object
+ @param masked: if True: mask it; if False: unmask it
+ @type masked: boolean"""
+
+ global new_masked, newunmasked
+
+ if not is_package(pkg):
+ pkg = system.new_package(pkg)
+
+ cpv = pkg.get_cpv()
+
+ if not cpv in new_unmasked:
+ new_unmasked[cpv] = []
+ if not cpv in new_masked:
+ new_masked[cpv] = []
+
+ if masked:
+ link_neq = new_masked
+ link_eq = new_unmasked
+ path = CONST.unmask_path()
+ else:
+ link_neq = new_unmasked
+ link_eq = new_masked
+ path = CONST.mask_path()
+
+ copy = link_eq[cpv][:]
+ for file, line in copy:
+ if line == "-1":
+ link_eq[cpv].remove((file, line))
+
+ copy = link_neq[cpv][:]
+ for file, line in copy:
+ if line != "-1":
+ link_neq[cpv].remove((file, line))
+
+ if masked == pkg.is_masked():
+ return
+
+ data = get_data(pkg, path)
+ debug("data: %s", str(data))
+ done = False
+ for file, line, crit, flags in data:
+ if pkg.matches(crit):
+ link_eq[cpv].append((file, line))
+ done = True
+
+ if done: return
+
+ if masked:
+ is_dir = CONST.mask_path_is_dir()
+ path = CONST.mask_path()
+ else:
+ is_dir = CONST.unmask_path_is_dir()
+ path = CONST.unmask_path()
+
+ if is_dir:
+ file = os.path.join(path, generate_path(cpv, CONFIG["maskfile"]))
+ else:
+ file = path
+
+ link_neq[cpv].append((file, "-1"))
+ link_neq[cpv] = unique_array(link_neq[cpv])
+ debug("new_(un)masked: %s",str(link_neq))
def remove_new_masked (cpv):
- if is_package(cpv):
- cpv = cpv.get_cpv()
-
- try:
- del new_masked[cpv]
- except KeyError:
- pass
-
- try:
- del new_unmasked[cpv]
- except KeyError:
- pass
+ if is_package(cpv):
+ cpv = cpv.get_cpv()
+
+ try:
+ del new_masked[cpv]
+ except KeyError:
+ pass
+
+ try:
+ del new_unmasked[cpv]
+ except KeyError:
+ pass
def new_masking_status (cpv):
- if is_package(cpv):
- cpv = cpv.get_cpv()
-
- def get(list):
- ret = None
- if cpv in list and list[cpv] != []:
- for file, line in list[cpv]:
- _ret = (int(line) == -1)
- if ret is not None and _ret != ret:
- error(_("Conflicting values for masking status: %s"), list)
- else:
- ret = _ret
- return ret
-
- masked = get(new_masked)
- if masked is None:
- masked = get(new_unmasked)
- if masked is not None:
- masked = not masked # revert for new_unmasked
-
- if masked is not None:
- if masked: return "masked"
- else: return "unmasked"
- else:
- return None
+ if is_package(cpv):
+ cpv = cpv.get_cpv()
+
+ def get(list):
+ ret = None
+ if cpv in list and list[cpv] != []:
+ for file, line in list[cpv]:
+ _ret = (int(line) == -1)
+ if ret is not None and _ret != ret:
+ error(_("Conflicting values for masking status: %s"), list)
+ else:
+ ret = _ret
+ return ret
+
+ masked = get(new_masked)
+ if masked is None:
+ masked = get(new_unmasked)
+ if masked is not None:
+ masked = not masked # revert for new_unmasked
+
+ if masked is not None:
+ if masked: return "masked"
+ else: return "unmasked"
+ else:
+ return None
def is_locally_masked (pkg, changes = True):
- if not is_package(pkg):
- pkg = system.new_package(pkg) # assume it is a cpv or a gentoolkit.Package
+ if not is_package(pkg):
+ pkg = system.new_package(pkg) # assume it is a cpv or a gentoolkit.Package
- if changes:
- if new_masking_status(pkg) == "masked": # we masked it ourselves, but did not save it yet
- # but sometimes, new_masking_status() returns "masked" if a package's unmask is removed
- # then it is masked by the system but not locally (except rarely exotic cases)
- if pkg.get_cpv() in new_unmasked:
- if new_unmasked[pkg.get_cpv()]: return False # assume that there only exists one entry for this package
- # else new_masking_status should have printed an error
- return True
+ if changes:
+ if new_masking_status(pkg) == "masked": # we masked it ourselves, but did not save it yet
+ # but sometimes, new_masking_status() returns "masked" if a package's unmask is removed
+ # then it is masked by the system but not locally (except rarely exotic cases)
+ if pkg.get_cpv() in new_unmasked:
+ if new_unmasked[pkg.get_cpv()]: return False # assume that there only exists one entry for this package
+ # else new_masking_status should have printed an error
+ return True
- if new_masking_status(pkg) == "unmasked": # we unmasked it
- return False
-
- list = get_data(pkg, CONST.mask_path())
+ if new_masking_status(pkg) == "unmasked": # we unmasked it
+ return False
+
+ list = get_data(pkg, CONST.mask_path())
- if not list: return False
+ if not list: return False
- for file, line, crit, fl in list:
- if pkg.matches(crit):
- return True
+ for file, line, crit, fl in list:
+ if pkg.matches(crit):
+ return True
- return False
-
+ return False
+
def write_masked ():
- global new_unmasked, new_masked
- file_cache = {}
-
- def write(cpv, file, line):
- line = int(line)
- # add new line
- if line == -1:
- msg = "\n#portato update#\n"
- if CONFIG["maskPerVersion"]:
- msg += "=%s\n" % cpv
- else:
- list = system.split_cpv(cpv)
- msg += "%s/%s\n" % (list[0],list[1])
- if not file in file_cache:
- f = open(file, "a")
- f.write(msg)
- f.close()
- else:
- file_cache[file].append(msg)
- # change a line
- else:
- if not file in file_cache:
- # read file
- f = open(file, "r")
- lines = []
- i = 1
- while i < line: # stop at the given line
- lines.append(f.readline())
- i = i+1
- # delete
- l = f.readline()
- l = "#"+l[:-1]+" # removed by portato\n"
- lines.append(l)
-
- # read the rest
- lines.extend(f.readlines())
-
- file_cache[file] = lines
- f.close()
- else: # in cache
- l = file_cache[file][line-1]
- # delete:
- l = "#"+l[:-1]+" # removed by portato\n"
- file_cache[file][line-1] = l
-
-
- for cpv in new_masked:
- for file, line in new_masked[cpv]:
- write(cpv, file, line)
-
- for cpv in new_unmasked:
- for file, line in new_unmasked[cpv]:
- write(cpv, file, line)
-
- # write to disk
- for file in file_cache.keys():
- f = open(file, "w")
- f.writelines(file_cache[file])
- f.close()
- # reset
- new_masked = {}
- new_unmasked = {}
- system.reload_settings()
+ global new_unmasked, new_masked
+ file_cache = {}
+
+ def write(cpv, file, line):
+ line = int(line)
+ # add new line
+ if line == -1:
+ msg = "\n#portato update#\n"
+ if CONFIG["maskPerVersion"]:
+ msg += "=%s\n" % cpv
+ else:
+ list = system.split_cpv(cpv)
+ msg += "%s/%s\n" % (list[0],list[1])
+ if not file in file_cache:
+ f = open(file, "a")
+ f.write(msg)
+ f.close()
+ else:
+ file_cache[file].append(msg)
+ # change a line
+ else:
+ if not file in file_cache:
+ # read file
+ f = open(file, "r")
+ lines = []
+ i = 1
+ while i < line: # stop at the given line
+ lines.append(f.readline())
+ i = i+1
+ # delete
+ l = f.readline()
+ l = "#"+l[:-1]+" # removed by portato\n"
+ lines.append(l)
+
+ # read the rest
+ lines.extend(f.readlines())
+
+ file_cache[file] = lines
+ f.close()
+ else: # in cache
+ l = file_cache[file][line-1]
+ # delete:
+ l = "#"+l[:-1]+" # removed by portato\n"
+ file_cache[file][line-1] = l
+
+
+ for cpv in new_masked:
+ for file, line in new_masked[cpv]:
+ write(cpv, file, line)
+
+ for cpv in new_unmasked:
+ for file, line in new_unmasked[cpv]:
+ write(cpv, file, line)
+
+ # write to disk
+ for file in file_cache.keys():
+ f = open(file, "w")
+ f.writelines(file_cache[file])
+ f.close()
+ # reset
+ new_masked = {}
+ new_unmasked = {}
+ system.reload_settings()
### TESTING PART ###
newTesting = {}
arch = ""
def remove_new_testing (cpv):
- if is_package(cpv):
- cpv = cpv.get_cpv()
-
- try:
- del newTesting[cpv]
- except KeyError:
- pass
+ if is_package(cpv):
+ cpv = cpv.get_cpv()
+
+ try:
+ del newTesting[cpv]
+ except KeyError:
+ pass
def new_testing_status (cpv):
- if is_package(cpv):
- cpv = cpv.get_cpv()
+ if is_package(cpv):
+ cpv = cpv.get_cpv()
- if cpv in newTesting:
- for file, line in newTesting[cpv]:
- if line == "-1": return False
- else: return True
+ if cpv in newTesting:
+ for file, line in newTesting[cpv]:
+ if line == "-1": return False
+ else: return True
- return None
+ return None
def set_testing (pkg, enable):
- """Enables the package for installing when it is marked as testing (~ARCH).
- @param pkg: the package
- @type pkg: string (cpv) or L{backend.Package}-object
- @param enable: controls whether to enable (True) or disable (False) for test-installing
- @type enable: boolean"""
-
- global arch, newTesting
- if not is_package(pkg):
- pkg = system.new_package(pkg)
-
- arch = pkg.get_global_settings("ARCH")
- cpv = pkg.get_cpv()
- if not cpv in newTesting:
- newTesting[cpv] = []
-
- for file, line in newTesting[cpv]:
- if (enable and line != "-1") or (not enable and line == "-1"):
- newTesting[cpv].remove((file, line))
-
- if (enable and not pkg.is_testing()) or (not enable and pkg.is_testing()):
- return
-
- if not enable:
- test = get_data(pkg, CONST.testing_path())
- debug("data (test): %s", str(test))
- for file, line, crit, flags in test:
- if pkg.matches(crit) and flags[0] == "~"+arch:
- newTesting[cpv].append((file, line))
- else:
- if CONST.testing_path_is_dir():
- file = os.path.join(CONST.testing_path(), generate_path(cpv, CONFIG["testingfile"]))
- else:
- file = CONST.testing_path()
- newTesting[cpv].append((file, "-1"))
-
- newTesting[cpv] = unique_array(newTesting[cpv])
- debug("newTesting: %s",str(newTesting))
+ """Enables the package for installing when it is marked as testing (~ARCH).
+ @param pkg: the package
+ @type pkg: string (cpv) or L{backend.Package}-object
+ @param enable: controls whether to enable (True) or disable (False) for test-installing
+ @type enable: boolean"""
+
+ global arch, newTesting
+ if not is_package(pkg):
+ pkg = system.new_package(pkg)
+
+ arch = pkg.get_global_settings("ARCH")
+ cpv = pkg.get_cpv()
+ if not cpv in newTesting:
+ newTesting[cpv] = []
+
+ for file, line in newTesting[cpv]:
+ if (enable and line != "-1") or (not enable and line == "-1"):
+ newTesting[cpv].remove((file, line))
+
+ if (enable and not pkg.is_testing()) or (not enable and pkg.is_testing()):
+ return
+
+ if not enable:
+ test = get_data(pkg, CONST.testing_path())
+ debug("data (test): %s", str(test))
+ for file, line, crit, flags in test:
+ if pkg.matches(crit) and flags[0] == "~"+arch:
+ newTesting[cpv].append((file, line))
+ else:
+ if CONST.testing_path_is_dir():
+ file = os.path.join(CONST.testing_path(), generate_path(cpv, CONFIG["testingfile"]))
+ else:
+ file = CONST.testing_path()
+ newTesting[cpv].append((file, "-1"))
+
+ newTesting[cpv] = unique_array(newTesting[cpv])
+ debug("newTesting: %s",str(newTesting))
def write_testing ():
- global arch, newTesting
- file_cache = {}
-
- for cpv in newTesting:
- for file, line in newTesting[cpv]:
- line = int(line)
- # add new line
- if line == -1:
- msg = "\n#portato update#\n"
- if CONFIG["testingPerVersion"]:
- msg += "=%s ~%s\n" % (cpv, arch)
- else:
- list = system.split_cpv(cpv)
- msg += "%s/%s ~%s\n" % (list[0],list[1],arch)
- if not file in file_cache:
- f = open(file, "a")
- f.write(msg)
- f.close()
- else:
- file_cache[file].append(msg)
- # change a line
- else:
- if not file in file_cache:
- # read file
- f = open(file, "r")
- lines = []
- i = 1
- while i < line: # stop at the given line
- lines.append(f.readline())
- i = i+1
- # delete
- l = f.readline()
- l = "#"+l[:-1]+" # removed by portato\n"
- lines.append(l)
-
- # read the rest
- lines.extend(f.readlines())
-
- file_cache[file] = lines
- f.close()
- else: # in cache
- l = file_cache[file][line-1]
- # delete:
- l = "#"+l[:-1]+" # removed by portato\n"
- file_cache[file][line-1] = l
-
- # write to disk
- for file in file_cache.keys():
- f = open(file, "w")
- f.writelines(file_cache[file])
- f.close()
- # reset
- newTesting = {}
- system.reload_settings()
+ global arch, newTesting
+ file_cache = {}
+
+ for cpv in newTesting:
+ for file, line in newTesting[cpv]:
+ line = int(line)
+ # add new line
+ if line == -1:
+ msg = "\n#portato update#\n"
+ if CONFIG["testingPerVersion"]:
+ msg += "=%s ~%s\n" % (cpv, arch)
+ else:
+ list = system.split_cpv(cpv)
+ msg += "%s/%s ~%s\n" % (list[0],list[1],arch)
+ if not file in file_cache:
+ f = open(file, "a")
+ f.write(msg)
+ f.close()
+ else:
+ file_cache[file].append(msg)
+ # change a line
+ else:
+ if not file in file_cache:
+ # read file
+ f = open(file, "r")
+ lines = []
+ i = 1
+ while i < line: # stop at the given line
+ lines.append(f.readline())
+ i = i+1
+ # delete
+ l = f.readline()
+ l = "#"+l[:-1]+" # removed by portato\n"
+ lines.append(l)
+
+ # read the rest
+ lines.extend(f.readlines())
+
+ file_cache[file] = lines
+ f.close()
+ else: # in cache
+ l = file_cache[file][line-1]
+ # delete:
+ l = "#"+l[:-1]+" # removed by portato\n"
+ file_cache[file][line-1] = l
+
+ # write to disk
+ for file in file_cache.keys():
+ f = open(file, "w")
+ f.writelines(file_cache[file])
+ f.close()
+ # reset
+ newTesting = {}
+ system.reload_settings()
diff --git a/portato/backend/package.py b/portato/backend/package.py
index 6d73a42..e4b2082 100644
--- a/portato/backend/package.py
+++ b/portato/backend/package.py
@@ -18,396 +18,396 @@ from ..dependency import DependencyTree
from . import _Package, system, flags
class Package (_Package):
- """This is a class abstracting a normal package which can be installed."""
-
- def __init__ (self, cpv):
- """Constructor.
-
- @param cpv: The cpv which describes the package to create.
- @type cpv: string (cat/pkg-ver)"""
-
- self._cpv = cpv
-
- #
- # implemented
- #
-
- def set_testing(self, enable = True):
- """Sets the actual testing status of the package.
-
- @param enable: if True it is masked as stable; if False it is marked as testing
- @type enable: boolean"""
-
- flags.set_testing(self, enable)
-
- def remove_new_testing(self):
- """Removes possible changed testing status."""
-
- flags.remove_new_testing(self.get_cpv())
-
- def set_masked (self, masking = False):
- """Sets the masking status of the package.
-
- @param masking: if True: mask it; if False: unmask it
- @type masking: boolean"""
-
- flags.set_masked(self, masked = masking)
-
- def remove_new_masked (self):
- """Removes possible changed masking status."""
-
- flags.remove_new_masked(self.get_cpv())
-
- def is_locally_masked (self):
- """Checks whether the package is masked by the user.
-
- @returns: True if masked by the user; False if not
- @rtype: bool"""
-
- return flags.is_locally_masked(self)
-
- def get_new_use_flags (self):
- """Returns a list of the new useflags, i.e. these flags which are not written to the portage-system yet.
-
- @returns: list of flags or []
- @rtype: string[]"""
-
- return flags.get_new_use_flags(self)
-
- def get_actual_use_flags (self):
- """This returns all currently set use-flags including the new ones.
-
- @return: list of flags
- @rtype: string[]"""
-
- i_flags = self.get_global_settings("USE", installed = False).split()
- m_flags = system.get_global_settings("USE").split()
- for f in self.get_new_use_flags():
- removed = False
-
- if f[0] == "~":
- f = f[1:]
- removed = True
-
- invf = flags.invert_use_flag(f)
-
- if f[0] == '-':
- if invf in i_flags and not (removed and invf in m_flags):
- i_flags.remove(invf)
-
- elif f not in i_flags:
- if not (removed and invf in m_flags):
- i_flags.append(f)
-
- return i_flags
-
- def set_use_flag (self, flag):
- """Set a use-flag.
-
- @param flag: the flag to set
- @type flag: string"""
-
- flags.set_use_flag(self, flag)
-
- def remove_new_use_flags (self):
- """Remove all the new use-flags."""
-
- flags.remove_new_use_flags(self)
-
- def use_expanded (self, flag, suggest = None):
- """Tests whether a useflag is an expanded one. If it is, this method returns the USE_EXPAND-value.
-
- @param flag: the flag to check
- @type flag: string
- @param suggest: try this suggestion first
- @type suggest: string
- @returns: USE_EXPAND-value on success
- @rtype: string or None"""
-
- if suggest is not None:
- if flag.startswith(suggest.lower()):
- return suggest
-
- for exp in self.get_global_settings("USE_EXPAND").split():
- lexp = exp.lower()
- if flag.startswith(lexp):
- return exp
-
- return None
-
- def get_cpv(self):
- """Returns full Category/Package-Version string.
-
- @returns: the cpv
- @rtype: string"""
-
- return self._cpv
-
- def get_cp (self):
- """Returns the cp-string.
-
- @returns: category/package.
- @rtype: string"""
-
- return self.get_category()+"/"+self.get_name()
-
- def get_slot_cp (self):
- """Returns the current cp followed by a colon and the slot-number.
-
- @returns: cp:slot
- @rtype: string"""
-
- return ("%s:%s" % (self.get_cp(), self.get_package_settings("SLOT")))
-
- def get_package_path(self):
- """Returns the path to where the ChangeLog, Manifest, .ebuild files reside.
-
- @returns: path to the package files
- @rtype: string"""
-
- p = self.get_ebuild_path()
- sp = p.split("/")
- if sp:
- return "/".join(sp[:-1])
-
- def get_dependencies (self):
- """
- Returns the tree of dependencies that this package needs.
-
- @rtype: L{DependencyTree}
- """
- deps = " ".join(map(self.get_package_settings, ("RDEPEND", "PDEPEND", "DEPEND")))
- deps = paren_reduce(deps)
-
- tree = DependencyTree()
-
- def add (tree, deps):
- it = iter(deps)
- for dep in it:
- if hasattr(dep, "__iter__"):
- debug("Following dep is an unsupposed list: %s", dep)
- assert(len(dep) == 1)
- dep = dep[0]
- if dep.endswith("?"):
- ntree = tree.add_flag(dep[:-1])
- n = it.next()
- if not hasattr(n, "__iter__"):
- n = (n,)
- add(ntree, n)
-
- elif dep == "||":
- n = it.next() # skip
- if not hasattr(n, "__iter__"):
- n = [n]
- else:
- n = list(n)
-
- tree.add_or(n)
-
- else:
- tree.add(dep)
-
- add(tree, deps)
- return tree
-
- #
- # Not implemented
- #
-
- def get_name(self):
- """Returns base name of package, no category nor version.
-
- @returns: base-name
- @rtype: string"""
-
- raise NotImplementedError
-
- def get_version(self):
- """Returns version of package, with (optional) revision number.
-
- @returns: version-rev
- @rtype: string"""
-
- raise NotImplementedError
-
- def get_category(self):
- """Returns category of package.
-
- @returns: category
- @rtype: string"""
-
- raise NotImplementedError
-
- def is_installed(self):
- """Returns true if this package is installed (merged).
- @rtype: boolean"""
-
- raise NotImplementedError
-
- def is_in_overlay(self):
- """Returns true if the package is in an overlay.
- @rtype: boolean"""
-
- raise NotImplementedError
-
- def get_overlay_path(self):
- """Returns the path to the current overlay.
- @rtype: string"""
-
- raise NotImplementedError
-
- def is_in_system (self):
- """Returns False if the package could not be found in the portage system.
-
- @return: True if in portage system; else False
- @rtype: boolean"""
-
- raise NotImplementedError
-
- def is_missing_keyword(self):
- """Returns True if the package is missing the needed keyword.
-
- @return: True if keyword is missing; else False
- @rtype: boolean"""
-
- raise NotImplementedError
-
- def is_testing(self, use_keywords = True):
- """Checks whether a package is marked as testing.
-
- @param use_keywords: Controls whether possible keywords are taken into account or not.
- @type use_keywords: boolean
- @returns: True if the package is marked as testing; else False.
- @rtype: boolean"""
-
- raise NotImplementedError
-
- def is_masked (self, use_changed = True):
- """Returns True if either masked by package.mask or by profile.
-
- @param use_changed: Controls, whether changes applied to masking keywords are taken into account.
- @type use_changed: boolean
- @returns: True if masked / False otherwise
- @rtype: boolean"""
-
- raise NotImplementedError
-
- def get_masking_reason (self):
- """Returns the reason for masking the package. If this is not possible for the system, return None.
-
- @returns: the reason for masking the package
- @rtype: string"""
-
- def get_iuse_flags (self, installed = False, removeForced = True):
- """Returns a list of _all_ useflags for this package, i.e. all useflags you can set for this package.
-
- @param installed: do not take the ones stated in the ebuild, but the ones it has been installed with
- @type installed: boolean
- @param removeForced: remove forced flags (i.e. usemask / useforce) from the iuse flags as they cannot be set from the user
- @type removeForced: boolean
-
- @returns: list of use-flags
- @rtype: string[]"""
-
- raise NotImplementedError
-
- def get_matched_dep_packages (self, depvar):
- """This function looks for all dependencies which are resolved. In normal case it makes only sense for installed packages, but should work for uninstalled ones too.
-
- @param depvar: the dependency variables (RDEPEND, PDEPEND, DEPEND) to use
- @type depvar: string[]
-
- @returns: unique list of dependencies resolved (with elements like "<=net-im/foobar-1.2.3")
- @rtype: string[]
-
- @raises portato.DependencyCalcError: when an error occured during executing portage.dep_check()"""
-
- raise NotImplementedError
-
- def get_dep_packages (self, depvar = ["RDEPEND", "PDEPEND", "DEPEND"], with_criterions = False):
- """Returns a cpv-list of packages on which this package depends and which have not been installed yet. This does not check the dependencies in a recursive manner.
-
- @param depvar: the dependency variables (RDEPEND, PDEPEND, DEPEND) to use
- @type depvar: string[]
- @param with_criterions: return also the criterions
- @type with_criterions: boolean
-
- @returns: list of cpvs on which the package depend (and if wanted also the criterions)
- @rtype: string[] or (string, string)[]
-
- @raises portato.BlockedException: when a package in the dependency-list is blocked by an installed one
- @raises portato.PackageNotFoundException: when a package in the dependency list could not be found in the system
- @raises portato.DependencyCalcError: when an error occured during executing portage.dep_check()"""
-
- raise NotImplementedError
-
- def get_global_settings(self, key, installed = True):
- """Returns the value of a global setting, i.e. ARCH, USE, ROOT, DISTDIR etc.
-
- @param key: the setting to return
- @type key: string
- @param installed: get the installed settings or the ebuild settings
- @type installed: boolean
- @returns: the value of this setting
- @rtype: string"""
-
- raise NotImplementedError
-
- def get_ebuild_path(self):
- """Returns the complete path to the .ebuild file.
-
- @rtype: string"""
-
- raise NotImplementedError
-
- def get_files (self):
- """
- Returns an iterator over the installed files of a package.
- If the package is not installed, the iterator should be "empty".
-
- @returns: the installed files
- @rtype: string<iterator>
- """
-
- raise NotImplementedError
-
- def get_package_settings(self, var, installed = True):
- """Returns a package specific setting, such as DESCRIPTION, SRC_URI, IUSE ...
-
- @param var: the setting to get
- @type var: string
- @param installed: take the vartree or the porttree
- @type installed: boolean
-
- @returns: the value of the setting
- @rtype: string"""
-
- raise NotImplementedError
-
- def get_installed_use_flags(self):
- """Returns _all_ (not only the package-specific) useflags which were set at the installation time of the package.
-
- @returns: list of use flags
- @rtype: string[]"""
-
- raise NotImplementedError
-
- def compare_version(self, other):
- """Compares this package's version to another's CPV; returns -1, 0, 1.
-
- @param other: the other package
- @type other: Package
- @returns: -1, 0 or 1
- @rtype: int"""
-
- raise NotImplementedError
-
- def matches (self, criterion):
- """This checks, whether this package matches a specific versioning criterion - e.g.: "<=net-im/foobar-1.2".
-
- @param criterion: the criterion to match against
- @type criterion: string
- @returns: True if matches; False if not
- @rtype: boolean"""
-
- raise NotImplementedError
+ """This is a class abstracting a normal package which can be installed."""
+
+ def __init__ (self, cpv):
+ """Constructor.
+
+ @param cpv: The cpv which describes the package to create.
+ @type cpv: string (cat/pkg-ver)"""
+
+ self._cpv = cpv
+
+ #
+ # implemented
+ #
+
+ def set_testing(self, enable = True):
+ """Sets the actual testing status of the package.
+
+ @param enable: if True it is masked as stable; if False it is marked as testing
+ @type enable: boolean"""
+
+ flags.set_testing(self, enable)
+
+ def remove_new_testing(self):
+ """Removes possible changed testing status."""
+
+ flags.remove_new_testing(self.get_cpv())
+
+ def set_masked (self, masking = False):
+ """Sets the masking status of the package.
+
+ @param masking: if True: mask it; if False: unmask it
+ @type masking: boolean"""
+
+ flags.set_masked(self, masked = masking)
+
+ def remove_new_masked (self):
+ """Removes possible changed masking status."""
+
+ flags.remove_new_masked(self.get_cpv())
+
+ def is_locally_masked (self):
+ """Checks whether the package is masked by the user.
+
+ @returns: True if masked by the user; False if not
+ @rtype: bool"""
+
+ return flags.is_locally_masked(self)
+
+ def get_new_use_flags (self):
+ """Returns a list of the new useflags, i.e. these flags which are not written to the portage-system yet.
+
+ @returns: list of flags or []
+ @rtype: string[]"""
+
+ return flags.get_new_use_flags(self)
+
+ def get_actual_use_flags (self):
+ """This returns all currently set use-flags including the new ones.
+
+ @return: list of flags
+ @rtype: string[]"""
+
+ i_flags = self.get_global_settings("USE", installed = False).split()
+ m_flags = system.get_global_settings("USE").split()
+ for f in self.get_new_use_flags():
+ removed = False
+
+ if f[0] == "~":
+ f = f[1:]
+ removed = True
+
+ invf = flags.invert_use_flag(f)
+
+ if f[0] == '-':
+ if invf in i_flags and not (removed and invf in m_flags):
+ i_flags.remove(invf)
+
+ elif f not in i_flags:
+ if not (removed and invf in m_flags):
+ i_flags.append(f)
+
+ return i_flags
+
+ def set_use_flag (self, flag):
+ """Set a use-flag.
+
+ @param flag: the flag to set
+ @type flag: string"""
+
+ flags.set_use_flag(self, flag)
+
+ def remove_new_use_flags (self):
+ """Remove all the new use-flags."""
+
+ flags.remove_new_use_flags(self)
+
+ def use_expanded (self, flag, suggest = None):
+ """Tests whether a useflag is an expanded one. If it is, this method returns the USE_EXPAND-value.
+
+ @param flag: the flag to check
+ @type flag: string
+ @param suggest: try this suggestion first
+ @type suggest: string
+ @returns: USE_EXPAND-value on success
+ @rtype: string or None"""
+
+ if suggest is not None:
+ if flag.startswith(suggest.lower()):
+ return suggest
+
+ for exp in self.get_global_settings("USE_EXPAND").split():
+ lexp = exp.lower()
+ if flag.startswith(lexp):
+ return exp
+
+ return None
+
+ def get_cpv(self):
+ """Returns full Category/Package-Version string.
+
+ @returns: the cpv
+ @rtype: string"""
+
+ return self._cpv
+
+ def get_cp (self):
+ """Returns the cp-string.
+
+ @returns: category/package.
+ @rtype: string"""
+
+ return self.get_category()+"/"+self.get_name()
+
+ def get_slot_cp (self):
+ """Returns the current cp followed by a colon and the slot-number.
+
+ @returns: cp:slot
+ @rtype: string"""
+
+ return ("%s:%s" % (self.get_cp(), self.get_package_settings("SLOT")))
+
+ def get_package_path(self):
+ """Returns the path to where the ChangeLog, Manifest, .ebuild files reside.
+
+ @returns: path to the package files
+ @rtype: string"""
+
+ p = self.get_ebuild_path()
+ sp = p.split("/")
+ if sp:
+ return "/".join(sp[:-1])
+
+ def get_dependencies (self):
+ """
+ Returns the tree of dependencies that this package needs.
+
+ @rtype: L{DependencyTree}
+ """
+ deps = " ".join(map(self.get_package_settings, ("RDEPEND", "PDEPEND", "DEPEND")))
+ deps = paren_reduce(deps)
+
+ tree = DependencyTree()
+
+ def add (tree, deps):
+ it = iter(deps)
+ for dep in it:
+ if hasattr(dep, "__iter__"):
+ debug("Following dep is an unsupposed list: %s", dep)
+ assert(len(dep) == 1)
+ dep = dep[0]
+ if dep.endswith("?"):
+ ntree = tree.add_flag(dep[:-1])
+ n = it.next()
+ if not hasattr(n, "__iter__"):
+ n = (n,)
+ add(ntree, n)
+
+ elif dep == "||":
+ n = it.next() # skip
+ if not hasattr(n, "__iter__"):
+ n = [n]
+ else:
+ n = list(n)
+
+ tree.add_or(n)
+
+ else:
+ tree.add(dep)
+
+ add(tree, deps)
+ return tree
+
+ #
+ # Not implemented
+ #
+
+ def get_name(self):
+ """Returns base name of package, no category nor version.
+
+ @returns: base-name
+ @rtype: string"""
+
+ raise NotImplementedError
+
+ def get_version(self):
+ """Returns version of package, with (optional) revision number.
+
+ @returns: version-rev
+ @rtype: string"""
+
+ raise NotImplementedError
+
+ def get_category(self):
+ """Returns category of package.
+
+ @returns: category
+ @rtype: string"""
+
+ raise NotImplementedError
+
+ def is_installed(self):
+ """Returns true if this package is installed (merged).
+ @rtype: boolean"""
+
+ raise NotImplementedError
+
+ def is_in_overlay(self):
+ """Returns true if the package is in an overlay.
+ @rtype: boolean"""
+
+ raise NotImplementedError
+
+ def get_overlay_path(self):
+ """Returns the path to the current overlay.
+ @rtype: string"""
+
+ raise NotImplementedError
+
+ def is_in_system (self):
+ """Returns False if the package could not be found in the portage system.
+
+ @return: True if in portage system; else False
+ @rtype: boolean"""
+
+ raise NotImplementedError
+
+ def is_missing_keyword(self):
+ """Returns True if the package is missing the needed keyword.
+
+ @return: True if keyword is missing; else False
+ @rtype: boolean"""
+
+ raise NotImplementedError
+
+ def is_testing(self, use_keywords = True):
+ """Checks whether a package is marked as testing.
+
+ @param use_keywords: Controls whether possible keywords are taken into account or not.
+ @type use_keywords: boolean
+ @returns: True if the package is marked as testing; else False.
+ @rtype: boolean"""
+
+ raise NotImplementedError
+
+ def is_masked (self, use_changed = True):
+ """Returns True if either masked by package.mask or by profile.
+
+ @param use_changed: Controls, whether changes applied to masking keywords are taken into account.
+ @type use_changed: boolean
+ @returns: True if masked / False otherwise
+ @rtype: boolean"""
+
+ raise NotImplementedError
+
+ def get_masking_reason (self):
+ """Returns the reason for masking the package. If this is not possible for the system, return None.
+
+ @returns: the reason for masking the package
+ @rtype: string"""
+
+ def get_iuse_flags (self, installed = False, removeForced = True):
+ """Returns a list of _all_ useflags for this package, i.e. all useflags you can set for this package.
+
+ @param installed: do not take the ones stated in the ebuild, but the ones it has been installed with
+ @type installed: boolean
+ @param removeForced: remove forced flags (i.e. usemask / useforce) from the iuse flags as they cannot be set from the user
+ @type removeForced: boolean
+
+ @returns: list of use-flags
+ @rtype: string[]"""
+
+ raise NotImplementedError
+
+ def get_matched_dep_packages (self, depvar):
+ """This function looks for all dependencies which are resolved. In normal case it makes only sense for installed packages, but should work for uninstalled ones too.
+
+ @param depvar: the dependency variables (RDEPEND, PDEPEND, DEPEND) to use
+ @type depvar: string[]
+
+ @returns: unique list of dependencies resolved (with elements like "<=net-im/foobar-1.2.3")
+ @rtype: string[]
+
+ @raises portato.DependencyCalcError: when an error occured during executing portage.dep_check()"""
+
+ raise NotImplementedError
+
+ def get_dep_packages (self, depvar = ["RDEPEND", "PDEPEND", "DEPEND"], with_criterions = False):
+ """Returns a cpv-list of packages on which this package depends and which have not been installed yet. This does not check the dependencies in a recursive manner.
+
+ @param depvar: the dependency variables (RDEPEND, PDEPEND, DEPEND) to use
+ @type depvar: string[]
+ @param with_criterions: return also the criterions
+ @type with_criterions: boolean
+
+ @returns: list of cpvs on which the package depend (and if wanted also the criterions)
+ @rtype: string[] or (string, string)[]
+
+ @raises portato.BlockedException: when a package in the dependency-list is blocked by an installed one
+ @raises portato.PackageNotFoundException: when a package in the dependency list could not be found in the system
+ @raises portato.DependencyCalcError: when an error occured during executing portage.dep_check()"""
+
+ raise NotImplementedError
+
+ def get_global_settings(self, key, installed = True):
+ """Returns the value of a global setting, i.e. ARCH, USE, ROOT, DISTDIR etc.
+
+ @param key: the setting to return
+ @type key: string
+ @param installed: get the installed settings or the ebuild settings
+ @type installed: boolean
+ @returns: the value of this setting
+ @rtype: string"""
+
+ raise NotImplementedError
+
+ def get_ebuild_path(self):
+ """Returns the complete path to the .ebuild file.
+
+ @rtype: string"""
+
+ raise NotImplementedError
+
+ def get_files (self):
+ """
+ Returns an iterator over the installed files of a package.
+ If the package is not installed, the iterator should be "empty".
+
+ @returns: the installed files
+ @rtype: string<iterator>
+ """
+
+ raise NotImplementedError
+
+ def get_package_settings(self, var, installed = True):
+ """Returns a package specific setting, such as DESCRIPTION, SRC_URI, IUSE ...
+
+ @param var: the setting to get
+ @type var: string
+ @param installed: take the vartree or the porttree
+ @type installed: boolean
+
+ @returns: the value of the setting
+ @rtype: string"""
+
+ raise NotImplementedError
+
+ def get_installed_use_flags(self):
+ """Returns _all_ (not only the package-specific) useflags which were set at the installation time of the package.
+
+ @returns: list of use flags
+ @rtype: string[]"""
+
+ raise NotImplementedError
+
+ def compare_version(self, other):
+ """Compares this package's version to another's CPV; returns -1, 0, 1.
+
+ @param other: the other package
+ @type other: Package
+ @returns: -1, 0 or 1
+ @rtype: int"""
+
+ raise NotImplementedError
+
+ def matches (self, criterion):
+ """This checks, whether this package matches a specific versioning criterion - e.g.: "<=net-im/foobar-1.2".
+
+ @param criterion: the criterion to match against
+ @type criterion: string
+ @returns: True if matches; False if not
+ @rtype: boolean"""
+
+ raise NotImplementedError
diff --git a/portato/backend/portage/__init__.py b/portato/backend/portage/__init__.py
index be6cce6..02a4a82 100644
--- a/portato/backend/portage/__init__.py
+++ b/portato/backend/portage/__init__.py
@@ -17,8 +17,8 @@ from portage import VERSION as PV
VERSION = tuple(map(int, (x.split("_")[0] for x in PV.split("."))))
if VERSION >= (2, 2):
- from .system_22 import PortageSystem_22 as PortageSystem
- from .package_22 import PortagePackage_22 as PortagePackage
+ from .system_22 import PortageSystem_22 as PortageSystem
+ from .package_22 import PortagePackage_22 as PortagePackage
else:
- from .system import PortageSystem
- from .package import PortagePackage
+ from .system import PortageSystem
+ from .package import PortagePackage
diff --git a/portato/backend/portage/package.py b/portato/backend/portage/package.py
index 351b7e0..0270029 100644
--- a/portato/backend/portage/package.py
+++ b/portato/backend/portage/package.py
@@ -21,294 +21,294 @@ from ...helper import debug, error, unique_array
import portage
try:
- import portage.dep as portage_dep
+ import portage.dep as portage_dep
except ImportError:
- import portage_dep
+ import portage_dep
import os.path
class PortagePackage (Package):
- """This is a class abstracting a normal package which can be installed for the portage-system."""
-
- def __init__ (self, cpv):
- """Constructor.
-
- @param cpv: The cpv which describes the package to create.
- @type cpv: string (cat/pkg-ver)"""
-
- Package.__init__(self, cpv)
- self._scpv = system.split_cpv(self._cpv)
-
- if not self._scpv:
- raise ValueError("invalid cpv: %s" % cpv)
-
- self._settings = system.settings
- self._settingslock = system.settings.settingslock
- self._settings_installed = None
-
- self._trees = system.settings.trees
-
- self.forced_flags = set()
-
- with self._settingslock:
- self._init_settings(True)
- self.forced_flags.update(self._settings.settings.usemask)
- self.forced_flags.update(self._settings.settings.useforce)
-
- try:
- self._status = portage.getmaskingstatus(self.get_cpv(), settings = self._settings.settings)
- except KeyError: # package is not located in the system
- self._status = None
-
- if self._status and len(self._status) == 1 and self._status[0] == "corrupted":
- self._status = None
-
- def _init_settings (self, installed):
- inst = (installed and self.is_installed()) or (self.is_installed() and not self.is_in_system())
-
- if self._settings_installed is not None and self._settings_installed != inst:
- self._settings.settings.reset()
-
- self._settings_installed = inst
-
- if inst:
- dbapi = self._settings.vartree.dbapi
- else:
- dbapi = self._settings.porttree.dbapi
-
- self._settings.settings.setcpv(self.get_cpv(), mydb = dbapi)
-
- def get_name(self):
- return self._scpv[1]
-
- def get_version(self):
- v = self._scpv[2]
- if self._scpv[3] != "r0":
- v += "-" + self._scpv[3]
- return v
-
- def get_category(self):
- return self._scpv[0]
-
- def is_installed(self):
- return self._settings.vartree.dbapi.cpv_exists(self._cpv)
-
- def is_in_overlay(self):
- ovl = self.get_overlay_path()
- return ovl != self._settings.settings["PORTDIR"] and str(ovl) != "0"
-
- def get_overlay_path (self):
- dir,ovl = self._settings.porttree.dbapi.findname2(self._cpv)
- return ovl
-
- def is_in_system (self):
- return (self._status != None)
-
- def is_missing_keyword(self):
- return self._status and "missing keyword" in self._status
-
- def is_testing(self, use_keywords = True):
- testArch = "~" + self.get_global_settings("ARCH")
- if not use_keywords: # keywords are NOT taken into account
- return testArch in self.get_package_settings("KEYWORDS").split()
-
- else: # keywords are taken into account
- status = flags.new_testing_status(self.get_cpv())
- if status is None: # we haven't changed it in any way
- return self._status and testArch+" keyword" in self._status
- else:
- return status
-
- def is_masked (self, use_changed = True):
-
- if use_changed:
- status = flags.new_masking_status(self.get_cpv())
- if status != None: # we have locally changed it
- if status == "masked": return True
- elif status == "unmasked": return False
- else:
- error(_("BUG in flags.new_masking_status. It returns \'%s\'"), status)
- else: # we have not touched the status
- return self._status and ("profile" in self._status or "package.mask" in self._status)
-
- else: # we want the original portage value XXX: bug if masked by user AND by system
-
- # get the normal masked ones
- if self._status and ("profile" in self._status or "package.mask" in self._status):
- return not flags.is_locally_masked(self, changes = False) # assume that if it is locally masked, it is not masked by the system
- else: # more difficult: get the ones we unmasked, but are masked by the system
- try:
- masked = self._settings.settings.pmaskdict[self.get_cp()]
- except KeyError: # key error: not masked
- return False
-
- for cpv in masked:
- if self.matches(cpv):
- return not flags.is_locally_masked(self, changes = False) # assume that if it is locally masked, it is not masked by the system
-
- return False
-
- def get_masking_reason(self):
- reason = portage.getmaskingreason(self.get_cpv(), settings = self._settings.settings)
-
- if reason:
- return reason.strip()
- else:
- return reason
-
- def get_iuse_flags (self, installed = False, removeForced = True):
- if not self.is_in_system():
- installed = True
-
- iuse = flags.filter_defaults(self.get_package_settings("IUSE", installed = installed).split())
-
- iuse = set(iuse)
-
- if removeForced:
- return list(iuse.difference(self.forced_flags))
- else:
- return list(iuse)
-
- def get_matched_dep_packages (self, depvar):
- # change the useflags, because we have internally changed some, but not made them visible for portage
- actual = self.get_actual_use_flags()
-
- depstring = ""
- try:
- for d in depvar:
- depstring += self.get_package_settings(d, installed = False)+" "
- except KeyError: # not found in porttree - use vartree
- depstring = ""
- for d in depvar:
- depstring += self.get_package_settings(d, installed = True)+" "
-
- deps = portage.dep_check(depstring, None, self._settings.settings, myuse = actual, trees = self._trees)
-
- if not deps: # FIXME: what is the difference to [1, []] ?
- return []
-
- if deps[0] == 0: # error
- raise DependencyCalcError, deps[1]
-
- deps = deps[1]
-
- return [d for d in deps if d[0] != "!"]
-
- def get_dep_packages (self, depvar = ["RDEPEND", "PDEPEND", "DEPEND"], with_criterions = False, return_blocks = False):
- dep_pkgs = [] # the package list
-
- # change the useflags, because we have internally changed some, but not made them visible for portage
- actual = self.get_actual_use_flags()
-
- depstring = ""
- for d in depvar:
- depstring += self.get_package_settings(d, installed = False)+" "
-
- # let portage do the main stuff ;)
- # pay attention to any changes here
- deps = portage.dep_check (depstring, self._settings.vartree.dbapi, self._settings.settings, myuse = actual, trees = self._trees)
-
- if not deps: # FIXME: what is the difference to [1, []] ?
- return []
-
- if deps[0] == 0: # error
- raise DependencyCalcError, deps[1]
-
- deps = deps[1]
-
- def create_dep_pkgs_data (dep, pkg):
- """Returns the data to enter into the dep_pkgs list, which is either the package cpv or a tuple
- consisting of the cpv and the criterion."""
- if with_criterions:
- return (pkg.get_cpv(), dep)
- else:
- return pkg.get_cpv()
-
- for dep in deps:
- if dep[0] == '!': # blocking sth
- blocked = system.find_packages(dep, system.SET_INSTALLED)
- if len(blocked) == 1: # only exact one match allowed to be harmless
- if blocked[0].get_slot_cp() == self.get_slot_cp(): # blocks in the same slot are harmless
- continue
-
- if return_blocks:
- if with_criterions:
- dep_pkgs.append((dep, dep))
- else:
- dep_pkgs.append(dep)
- else:
- raise BlockedException, (self.get_cpv(), blocked[0].get_cpv())
-
- continue # finished with the blocking one -> next
-
- pkg = system.find_best_match(dep)
- if not pkg: # try to find masked ones
- pkgs = system.find_packages(dep, masked = True)
- if not pkgs:
- raise PackageNotFoundException, dep
-
- pkgs = system.sort_package_list(pkgs)
- pkgs.reverse()
- done = False
- for p in pkgs:
- if not p.is_masked():
- dep_pkgs.append(create_dep_pkgs_data(dep, p))
- done = True
- break
- if not done:
- dep_pkgs.append(create_dep_pkgs_data(dep, pkgs[0]))
- else:
- dep_pkgs.append(create_dep_pkgs_data(dep, pkg))
-
- return dep_pkgs
-
- def get_global_settings(self, key, installed = True):
- with self._settingslock:
- self._init_settings(installed)
- v = self._settings.settings[key]
-
- return v
-
- def get_ebuild_path(self):
- if self.is_in_system():
- return self._settings.porttree.dbapi.findname(self._cpv)
- else:
- return self._settings.vartree.dbapi.findname(self._cpv)
-
- def get_files (self):
- if self.is_installed():
- path = os.path.join(self.get_global_settings("ROOT"), portage.VDB_PATH, self.get_cpv(), "CONTENTS")
- with open(path) as f:
- for line in f:
- yield line.split()[1].strip()
-
- def get_package_settings(self, var, installed = True):
- if installed and self.is_installed():
- mytree = self._settings.vartree
- else:
- mytree = self._settings.porttree
-
- r = mytree.dbapi.aux_get(self._cpv,[var])
-
- return r[0]
-
- def get_installed_use_flags(self):
- if self.is_installed():
- return self.get_package_settings("USE", installed = True).split()
- else: return []
-
- def compare_version(self,other):
- v1 = self._scpv
- v2 = portage.catpkgsplit(other.get_cpv())
- # if category is different
- if v1[0] != v2[0]:
- return cmp(v1[0],v2[0])
- # if name is different
- elif v1[1] != v2[1]:
- return cmp(v1[1],v2[1])
- # Compare versions
- else:
- return portage.pkgcmp(v1[1:],v2[1:])
-
- def matches (self, criterion):
- return system.cpv_matches(self.get_cpv(), criterion)
+ """This is a class abstracting a normal package which can be installed for the portage-system."""
+
+ def __init__ (self, cpv):
+ """Constructor.
+
+ @param cpv: The cpv which describes the package to create.
+ @type cpv: string (cat/pkg-ver)"""
+
+ Package.__init__(self, cpv)
+ self._scpv = system.split_cpv(self._cpv)
+
+ if not self._scpv:
+ raise ValueError("invalid cpv: %s" % cpv)
+
+ self._settings = system.settings
+ self._settingslock = system.settings.settingslock
+ self._settings_installed = None
+
+ self._trees = system.settings.trees
+
+ self.forced_flags = set()
+
+ with self._settingslock:
+ self._init_settings(True)
+ self.forced_flags.update(self._settings.settings.usemask)
+ self.forced_flags.update(self._settings.settings.useforce)
+
+ try:
+ self._status = portage.getmaskingstatus(self.get_cpv(), settings = self._settings.settings)
+ except KeyError: # package is not located in the system
+ self._status = None
+
+ if self._status and len(self._status) == 1 and self._status[0] == "corrupted":
+ self._status = None
+
+ def _init_settings (self, installed):
+ inst = (installed and self.is_installed()) or (self.is_installed() and not self.is_in_system())
+
+ if self._settings_installed is not None and self._settings_installed != inst:
+ self._settings.settings.reset()
+
+ self._settings_installed = inst
+
+ if inst:
+ dbapi = self._settings.vartree.dbapi
+ else:
+ dbapi = self._settings.porttree.dbapi
+
+ self._settings.settings.setcpv(self.get_cpv(), mydb = dbapi)
+
+ def get_name(self):
+ return self._scpv[1]
+
+ def get_version(self):
+ v = self._scpv[2]
+ if self._scpv[3] != "r0":
+ v += "-" + self._scpv[3]
+ return v
+
+ def get_category(self):
+ return self._scpv[0]
+
+ def is_installed(self):
+ return self._settings.vartree.dbapi.cpv_exists(self._cpv)
+
+ def is_in_overlay(self):
+ ovl = self.get_overlay_path()
+ return ovl != self._settings.settings["PORTDIR"] and str(ovl) != "0"
+
+ def get_overlay_path (self):
+ dir,ovl = self._settings.porttree.dbapi.findname2(self._cpv)
+ return ovl
+
+ def is_in_system (self):
+ return (self._status != None)
+
+ def is_missing_keyword(self):
+ return self._status and "missing keyword" in self._status
+
+ def is_testing(self, use_keywords = True):
+ testArch = "~" + self.get_global_settings("ARCH")
+ if not use_keywords: # keywords are NOT taken into account
+ return testArch in self.get_package_settings("KEYWORDS").split()
+
+ else: # keywords are taken into account
+ status = flags.new_testing_status(self.get_cpv())
+ if status is None: # we haven't changed it in any way
+ return self._status and testArch+" keyword" in self._status
+ else:
+ return status
+
+ def is_masked (self, use_changed = True):
+
+ if use_changed:
+ status = flags.new_masking_status(self.get_cpv())
+ if status != None: # we have locally changed it
+ if status == "masked": return True
+ elif status == "unmasked": return False
+ else:
+ error(_("BUG in flags.new_masking_status. It returns \'%s\'"), status)
+ else: # we have not touched the status
+ return self._status and ("profile" in self._status or "package.mask" in self._status)
+
+ else: # we want the original portage value XXX: bug if masked by user AND by system
+
+ # get the normal masked ones
+ if self._status and ("profile" in self._status or "package.mask" in self._status):
+ return not flags.is_locally_masked(self, changes = False) # assume that if it is locally masked, it is not masked by the system
+ else: # more difficult: get the ones we unmasked, but are masked by the system
+ try:
+ masked = self._settings.settings.pmaskdict[self.get_cp()]
+ except KeyError: # key error: not masked
+ return False
+
+ for cpv in masked:
+ if self.matches(cpv):
+ return not flags.is_locally_masked(self, changes = False) # assume that if it is locally masked, it is not masked by the system
+
+ return False
+
+ def get_masking_reason(self):
+ reason = portage.getmaskingreason(self.get_cpv(), settings = self._settings.settings)
+
+ if reason:
+ return reason.strip()
+ else:
+ return reason
+
+ def get_iuse_flags (self, installed = False, removeForced = True):
+ if not self.is_in_system():
+ installed = True
+
+ iuse = flags.filter_defaults(self.get_package_settings("IUSE", installed = installed).split())
+
+ iuse = set(iuse)
+
+ if removeForced:
+ return list(iuse.difference(self.forced_flags))
+ else:
+ return list(iuse)
+
+ def get_matched_dep_packages (self, depvar):
+ # change the useflags, because we have internally changed some, but not made them visible for portage
+ actual = self.get_actual_use_flags()
+
+ depstring = ""
+ try:
+ for d in depvar:
+ depstring += self.get_package_settings(d, installed = False)+" "
+ except KeyError: # not found in porttree - use vartree
+ depstring = ""
+ for d in depvar:
+ depstring += self.get_package_settings(d, installed = True)+" "
+
+ deps = portage.dep_check(depstring, None, self._settings.settings, myuse = actual, trees = self._trees)
+
+ if not deps: # FIXME: what is the difference to [1, []] ?
+ return []
+
+ if deps[0] == 0: # error
+ raise DependencyCalcError, deps[1]
+
+ deps = deps[1]
+
+ return [d for d in deps if d[0] != "!"]
+
+ def get_dep_packages (self, depvar = ["RDEPEND", "PDEPEND", "DEPEND"], with_criterions = False, return_blocks = False):
+ dep_pkgs = [] # the package list
+
+ # change the useflags, because we have internally changed some, but not made them visible for portage
+ actual = self.get_actual_use_flags()
+
+ depstring = ""
+ for d in depvar:
+ depstring += self.get_package_settings(d, installed = False)+" "
+
+ # let portage do the main stuff ;)
+ # pay attention to any changes here
+ deps = portage.dep_check (depstring, self._settings.vartree.dbapi, self._settings.settings, myuse = actual, trees = self._trees)
+
+ if not deps: # FIXME: what is the difference to [1, []] ?
+ return []
+
+ if deps[0] == 0: # error
+ raise DependencyCalcError, deps[1]
+
+ deps = deps[1]
+
+ def create_dep_pkgs_data (dep, pkg):
+ """Returns the data to enter into the dep_pkgs list, which is either the package cpv or a tuple
+ consisting of the cpv and the criterion."""
+ if with_criterions:
+ return (pkg.get_cpv(), dep)
+ else:
+ return pkg.get_cpv()
+
+ for dep in deps:
+ if dep[0] == '!': # blocking sth
+ blocked = system.find_packages(dep, system.SET_INSTALLED)
+ if len(blocked) == 1: # only exact one match allowed to be harmless
+ if blocked[0].get_slot_cp() == self.get_slot_cp(): # blocks in the same slot are harmless
+ continue
+
+ if return_blocks:
+ if with_criterions:
+ dep_pkgs.append((dep, dep))
+ else:
+ dep_pkgs.append(dep)
+ else:
+ raise BlockedException, (self.get_cpv(), blocked[0].get_cpv())
+
+ continue # finished with the blocking one -> next
+
+ pkg = system.find_best_match(dep)
+ if not pkg: # try to find masked ones
+ pkgs = system.find_packages(dep, masked = True)
+ if not pkgs:
+ raise PackageNotFoundException, dep
+
+ pkgs = system.sort_package_list(pkgs)
+ pkgs.reverse()
+ done = False
+ for p in pkgs:
+ if not p.is_masked():
+ dep_pkgs.append(create_dep_pkgs_data(dep, p))
+ done = True
+ break
+ if not done:
+ dep_pkgs.append(create_dep_pkgs_data(dep, pkgs[0]))
+ else:
+ dep_pkgs.append(create_dep_pkgs_data(dep, pkg))
+
+ return dep_pkgs
+
+ def get_global_settings(self, key, installed = True):
+ with self._settingslock:
+ self._init_settings(installed)
+ v = self._settings.settings[key]
+
+ return v
+
+ def get_ebuild_path(self):
+ if self.is_in_system():
+ return self._settings.porttree.dbapi.findname(self._cpv)
+ else:
+ return self._settings.vartree.dbapi.findname(self._cpv)
+
+ def get_files (self):
+ if self.is_installed():
+ path = os.path.join(self.get_global_settings("ROOT"), portage.VDB_PATH, self.get_cpv(), "CONTENTS")
+ with open(path) as f:
+ for line in f:
+ yield line.split()[1].strip()
+
+ def get_package_settings(self, var, installed = True):
+ if installed and self.is_installed():
+ mytree = self._settings.vartree
+ else:
+ mytree = self._settings.porttree
+
+ r = mytree.dbapi.aux_get(self._cpv,[var])
+
+ return r[0]
+
+ def get_installed_use_flags(self):
+ if self.is_installed():
+ return self.get_package_settings("USE", installed = True).split()
+ else: return []
+
+ def compare_version(self,other):
+ v1 = self._scpv
+ v2 = portage.catpkgsplit(other.get_cpv())
+ # if category is different
+ if v1[0] != v2[0]:
+ return cmp(v1[0],v2[0])
+ # if name is different
+ elif v1[1] != v2[1]:
+ return cmp(v1[1],v2[1])
+ # Compare versions
+ else:
+ return portage.pkgcmp(v1[1:],v2[1:])
+
+ def matches (self, criterion):
+ return system.cpv_matches(self.get_cpv(), criterion)
diff --git a/portato/backend/portage/package_22.py b/portato/backend/portage/package_22.py
index 4fe03d9..1dae1fe 100644
--- a/portato/backend/portage/package_22.py
+++ b/portato/backend/portage/package_22.py
@@ -15,8 +15,8 @@ from __future__ import absolute_import, with_statement
from .package import PortagePackage
class PortagePackage_22 (PortagePackage):
- """
- The 2.2 specialization of the portage package.
- Currently this is identical to the normal one.
- """
- pass
+ """
+ The 2.2 specialization of the portage package.
+ Currently this is identical to the normal one.
+ """
+ pass
diff --git a/portato/backend/portage/sets.py b/portato/backend/portage/sets.py
index 24ceaca..8d8ef28 100644
--- a/portato/backend/portage/sets.py
+++ b/portato/backend/portage/sets.py
@@ -17,130 +17,130 @@ import itertools as itt
import portage
try:
- import portage.dep as portage_dep
+ import portage.dep as portage_dep
except ImportError:
- import portage_dep
+ import portage_dep
from .. import system
from ...helper import debug
class Set(object):
- def get_pkgs(self, key, is_regexp, masked, with_version, only_cpv):
- raise NotImplementedError
+ def get_pkgs(self, key, is_regexp, masked, with_version, only_cpv):
+ raise NotImplementedError
- def find (self, key, masked = False, with_version = True, only_cpv = False):
- if key is None: key = ""
-
- is_regexp = key == "" or ("*" in key and key[0] not in ("*","=","<",">","~","!"))
+ def find (self, key, masked = False, with_version = True, only_cpv = False):
+ if key is None: key = ""
+
+ is_regexp = key == "" or ("*" in key and key[0] not in ("*","=","<",">","~","!"))
- try:
- t = self.get_pkgs(key, is_regexp, masked, with_version, only_cpv)
- # catch the "ambigous package" Exception
- except ValueError, e:
- if isinstance(e[0], list):
- t = set()
- for cp in e[0]:
- t.update(self.get_pkgs(cp, is_regexp, masked, with_version, only_cpv))
- else:
- raise
+ try:
+ t = self.get_pkgs(key, is_regexp, masked, with_version, only_cpv)
+ # catch the "ambigous package" Exception
+ except ValueError, e:
+ if isinstance(e[0], list):
+ t = set()
+ for cp in e[0]:
+ t.update(self.get_pkgs(cp, is_regexp, masked, with_version, only_cpv))
+ else:
+ raise
- return t
+ return t
class FilterSet (Set):
- def get_list(self):
- raise NotImplementedError
-
- def get_pkgs (self, key, is_regexp, masked, with_version, only_cpv):
- t = set()
- for pkg in self.get_list():
- if is_regexp:
- if not re.match(key, pkg, re.I): continue
+ def get_list(self):
+ raise NotImplementedError
+
+ def get_pkgs (self, key, is_regexp, masked, with_version, only_cpv):
+ t = set()
+ for pkg in self.get_list():
+ if is_regexp:
+ if not re.match(key, pkg, re.I): continue
- if not with_version:
- t.add(portage_dep.dep_getkey(pkg))
-
- t.add(system.find_best_match(pkg, only_cpv = True))
+ if not with_version:
+ t.add(portage_dep.dep_getkey(pkg))
+
+ t.add(system.find_best_match(pkg, only_cpv = True))
- return t
+ return t
class PortageSet (FilterSet):
- def __init__ (self, name):
- FilterSet.__init__(self)
- debug("Loading portage set '%s'", name)
- self.portageSet = system.settings.setsconfig.getSets()[name]
+ def __init__ (self, name):
+ FilterSet.__init__(self)
+ debug("Loading portage set '%s'", name)
+ self.portageSet = system.settings.setsconfig.getSets()[name]
- def get_list(self):
- return itt.imap(str, self.portageSet.getAtoms())
+ def get_list(self):
+ return itt.imap(str, self.portageSet.getAtoms())
class SystemSet (FilterSet):
- def get_list(self):
- for cp in system.settings.settings.packages:
- if cp[0] == "*": yield cp[1:]
+ def get_list(self):
+ for cp in system.settings.settings.packages:
+ if cp[0] == "*": yield cp[1:]
class WorldSet (FilterSet):
- def get_list(self):
- with open(portage.WORLD_FILE) as f:
- for cp in f:
- cp = cp.strip()
- if cp and cp[0] != "#":
- yield cp
+ def get_list(self):
+ with open(portage.WORLD_FILE) as f:
+ for cp in f:
+ cp = cp.strip()
+ if cp and cp[0] != "#":
+ yield cp
class InstalledSet (Set):
- def get_pkgs (self, key, is_regexp, masked, with_version, only_cpv):
- if is_regexp:
- if with_version:
- t = system.settings.vartree.dbapi.cpv_all()
- else:
- t = system.settings.vartree.dbapi.cp_all()
+ def get_pkgs (self, key, is_regexp, masked, with_version, only_cpv):
+ if is_regexp:
+ if with_version:
+ t = system.settings.vartree.dbapi.cpv_all()
+ else:
+ t = system.settings.vartree.dbapi.cp_all()
- if key:
- t = filter(lambda x: re.match(key, x, re.I), t)
+ if key:
+ t = filter(lambda x: re.match(key, x, re.I), t)
- return set(t)
- else:
- return set(system.settings.vartree.dbapi.match(key))
+ return set(t)
+ else:
+ return set(system.settings.vartree.dbapi.match(key))
class TreeSet (Set):
- def get_pkgs (self, key, is_regexp, masked, with_version, only_cpv):
- if is_regexp:
- if with_version:
- t = system.settings.porttree.dbapi.cpv_all()
- else:
- t = system.settings.porttree.dbapi.cp_all()
+ def get_pkgs (self, key, is_regexp, masked, with_version, only_cpv):
+ if is_regexp:
+ if with_version:
+ t = system.settings.porttree.dbapi.cpv_all()
+ else:
+ t = system.settings.porttree.dbapi.cp_all()
- if key:
- t = filter(lambda x: re.match(key, x, re.I), t)
+ if key:
+ t = filter(lambda x: re.match(key, x, re.I), t)
- elif masked:
- t = system.settings.porttree.dbapi.xmatch("match-all", key)
- else:
- t = system.settings.porttree.dbapi.match(key)
+ elif masked:
+ t = system.settings.porttree.dbapi.xmatch("match-all", key)
+ else:
+ t = system.settings.porttree.dbapi.match(key)
- return set(t)
+ return set(t)
class AllSet (Set):
-
- def __init__ (self):
- Set.__init__(self)
- self.tree = TreeSet()
- self.installed = InstalledSet()
+
+ def __init__ (self):
+ Set.__init__(self)
+ self.tree = TreeSet()
+ self.installed = InstalledSet()
- def find (self, *args, **kwargs):
- return self.tree.find(*args, **kwargs) | self.installed.find(*args, **kwargs)
+ def find (self, *args, **kwargs):
+ return self.tree.find(*args, **kwargs) | self.installed.find(*args, **kwargs)
class UninstalledSet (Set):
- def __init__ (self):
- Set.__init__(self)
- self.all = AllSet()
- self.installed = InstalledSet()
+ def __init__ (self):
+ Set.__init__(self)
+ self.all = AllSet()
+ self.installed = InstalledSet()
- def find (self, *args, **kwargs):
- return self.all.find(*args, **kwargs) - self.installed.find(*args, **kwargs)
+ def find (self, *args, **kwargs):
+ return self.all.find(*args, **kwargs) - self.installed.find(*args, **kwargs)
diff --git a/portato/backend/portage/settings.py b/portato/backend/portage/settings.py
index 5d466ad..0e7eccb 100644
--- a/portato/backend/portage/settings.py
+++ b/portato/backend/portage/settings.py
@@ -17,42 +17,42 @@ import portage
from threading import Lock
class PortageSettings:
- """Encapsulation of the portage settings.
-
- @ivar settings: portage settings
- @ivar settingslock: a simple Lock
- @ivar trees: a dictionary of the trees
- @ivar porttree: shortcut to C{trees[root]["porttree"]}
- @ivar vartree: shortcut to C{trees[root]["vartree"]}
- @ivar virtuals: shortcut to C{trees[root]["virtuals"]}"""
-
- def __init__ (self):
- """Initializes the instance. Calls L{load()}."""
- self.settingslock = Lock()
- self.trees = None
- self.load()
-
- def load(self):
- """(Re)loads the portage settings and sets the variables."""
-
- kwargs = {}
- for k, envvar in (("config_root", "PORTAGE_CONFIGROOT"), ("target_root", "ROOT")):
- kwargs[k] = os.environ.get(envvar, None)
- self.trees = portage.create_trees(trees=self.trees, **kwargs)
-
- self.settings = self.trees["/"]["vartree"].settings
-
- for myroot in self.trees:
- if myroot != "/":
- self.settings = self.trees[myroot]["vartree"].settings
- break
-
- self.settings.unlock()
-
- root = self.settings["ROOT"]
-
- self.porttree = self.trees[root]["porttree"]
- self.vartree = self.trees[root]["vartree"]
- self.virtuals = self.trees[root]["virtuals"]
-
- portage.settings = None # we use our own one ...
+ """Encapsulation of the portage settings.
+
+ @ivar settings: portage settings
+ @ivar settingslock: a simple Lock
+ @ivar trees: a dictionary of the trees
+ @ivar porttree: shortcut to C{trees[root]["porttree"]}
+ @ivar vartree: shortcut to C{trees[root]["vartree"]}
+ @ivar virtuals: shortcut to C{trees[root]["virtuals"]}"""
+
+ def __init__ (self):
+ """Initializes the instance. Calls L{load()}."""
+ self.settingslock = Lock()
+ self.trees = None
+ self.load()
+
+ def load(self):
+ """(Re)loads the portage settings and sets the variables."""
+
+ kwargs = {}
+ for k, envvar in (("config_root", "PORTAGE_CONFIGROOT"), ("target_root", "ROOT")):
+ kwargs[k] = os.environ.get(envvar, None)
+ self.trees = portage.create_trees(trees=self.trees, **kwargs)
+
+ self.settings = self.trees["/"]["vartree"].settings
+
+ for myroot in self.trees:
+ if myroot != "/":
+ self.settings = self.trees[myroot]["vartree"].settings
+ break
+
+ self.settings.unlock()
+
+ root = self.settings["ROOT"]
+
+ self.porttree = self.trees[root]["porttree"]
+ self.vartree = self.trees[root]["vartree"]
+ self.virtuals = self.trees[root]["virtuals"]
+
+ portage.settings = None # we use our own one ...
diff --git a/portato/backend/portage/settings_22.py b/portato/backend/portage/settings_22.py
index a43d69e..5274b3b 100644
--- a/portato/backend/portage/settings_22.py
+++ b/portato/backend/portage/settings_22.py
@@ -16,12 +16,12 @@ import portage.sets
from .settings import PortageSettings
class PortageSettings_22 (PortageSettings):
- """Enhances the normal PortageSettings in ways, that it adds the setsconfig."""
+ """Enhances the normal PortageSettings in ways, that it adds the setsconfig."""
- def __init__ (self):
- PortageSettings.__init__(self)
+ def __init__ (self):
+ PortageSettings.__init__(self)
- def load (self):
- PortageSettings.load(self)
+ def load (self):
+ PortageSettings.load(self)
- self.setsconfig = portage.sets.load_default_config(self.settings, self.trees[self.settings["ROOT"]])
+ self.setsconfig = portage.sets.load_default_config(self.settings, self.trees[self.settings["ROOT"]])
diff --git a/portato/backend/portage/system.py b/portato/backend/portage/system.py
index 2394a10..e81951e 100644
--- a/portato/backend/portage/system.py
+++ b/portato/backend/portage/system.py
@@ -15,9 +15,9 @@ from __future__ import absolute_import, with_statement
import re, os, os.path
import portage
try:
- import portage.dep as portage_dep
+ import portage.dep as portage_dep
except ImportError:
- import portage_dep
+ import portage_dep
from collections import defaultdict
import itertools as itt
@@ -30,386 +30,386 @@ from ..system_interface import SystemInterface
from ...helper import debug, info, warning, unique_array
class PortageSystem (SystemInterface):
- """This class provides access to the portage-system."""
-
- # pre-compile the RE removing the ".svn" and "CVS" entries
- unwantedPkgsRE = re.compile(r".*(\.svn|CVS)$")
- withBdepsRE = re.compile(r"--with-bdeps\s*( |=)\s*y")
+ """This class provides access to the portage-system."""
+
+ # pre-compile the RE removing the ".svn" and "CVS" entries
+ unwantedPkgsRE = re.compile(r".*(\.svn|CVS)$")
+ withBdepsRE = re.compile(r"--with-bdeps\s*( |=)\s*y")
- def __init__ (self):
- """Constructor."""
- self.settings = PortageSettings()
- portage.WORLD_FILE = os.path.join(self.settings.settings["ROOT"],portage.WORLD_FILE)
-
- self.use_descs = {}
- self.local_use_descs = defaultdict(dict)
-
- self.setmap = {
- self.SET_ALL : syssets.AllSet,
- self.SET_INSTALLED : syssets.InstalledSet,
- self.SET_UNINSTALLED : syssets.UninstalledSet,
- self.SET_TREE : syssets.TreeSet,
- "world" : syssets.WorldSet,
- "system" : syssets.SystemSet
- }
-
- def has_set_support (self):
- return False
-
- def get_sets (self, description = False):
- if description:
- return (("world", "The world set."), ("system", "The system set."))
- else:
- return ("world", "system")
-
- def get_version (self):
- return "Portage %s" % portage.VERSION
-
- def new_package (self, cpv):
- return PortagePackage(cpv)
-
- def get_config_path (self):
- return portage.USER_CONFIG_PATH
-
- def get_merge_command (self):
- return ["/usr/bin/python", "/usr/bin/emerge"]
-
- def get_sync_command (self):
- return self.get_merge_command()+["--sync"]
-
- def get_oneshot_option (self):
- return ["--oneshot"]
-
- def get_newuse_option (self):
- return ["--newuse"]
-
- def get_deep_option (self):
- return ["--deep"]
-
- def get_update_option (self):
- return ["--update"]
-
- def get_pretend_option (self):
- return ["--pretend", "--verbose"]
-
- def get_unmerge_option (self):
- return ["--unmerge"]
-
- def get_environment (self):
- default_opts = self.get_global_settings("EMERGE_DEFAULT_OPTS")
- opts = dict(os.environ)
- opts.update(TERM = "xterm") # emulate terminal :)
- opts.update(PAGER = "less") # force less
-
- if default_opts:
- opt_list = default_opts.split()
- changed = False
-
- for option in ["--ask", "-a", "--pretend", "-p"]:
- if option in opt_list:
- opt_list.remove(option)
- changed = True
-
- if changed:
- opts.update(EMERGE_DEFAULT_OPTS = " ".join(opt_list))
-
- return opts
-
- def cpv_matches (self, cpv, criterion):
- if portage.match_from_list(criterion, [cpv]) == []:
- return False
- else:
- return True
-
- def with_bdeps(self):
- """Returns whether the "--with-bdeps" option is set to true.
-
- @returns: the value of --with-bdeps
- @rtype: boolean
- """
-
- settings = self.get_global_settings("EMERGE_DEFAULT_OPTS").split()
- for s in settings:
- if self.withBdepsRE.match(s):
- return True
-
- return False
-
- def find_lambda (self, name):
- """Returns the function needed by all the find_all_*-functions. Returns None if no name is given.
-
- @param name: name to build the function of
- @type name: string or RE
- @returns:
- 1. None if no name is given
- 2. a lambda function
- @rtype: function
- """
-
- if name != None:
- if isinstance(name, str):
- return lambda x: re.match(".*"+name+".*",x, re.I)
- else: # assume regular expression
- return lambda x: name.match(x)
- else:
- return lambda x: True
-
- def geneticize_list (self, list_of_packages, only_cpv = False):
- """Convertes a list of cpv's into L{backend.Package}s.
-
- @param list_of_packages: the list of packages
- @type list_of_packages: string[]
- @param only_cpv: do nothing - return the passed list
- @type only_cpv: boolean
- @returns: converted list
- @rtype: PortagePackage[]
- """
-
- if not only_cpv:
- return [self.new_package(x) for x in list_of_packages]
- elif not isinstance(list_of_packages, list):
- return list(list_of_packages)
- else:
- return list_of_packages
-
- def get_global_settings (self, key):
- self.settings.settings.reset()
- return self.settings.settings[key]
-
- def find_best (self, list, only_cpv = False):
- if only_cpv:
- return portage.best(list)
- else:
- return self.new_package(portage.best(list))
-
- def find_best_match (self, search_key, masked = False, only_installed = False, only_cpv = False):
- t = []
-
- if not only_installed:
- pkgSet = self.SET_TREE
- else:
- pkgSet = self.SET_INSTALLED
-
- t = self.find_packages(search_key, pkgSet = pkgSet, masked = masked, with_version = True, only_cpv = True)
-
- if VERSION >= (2,1,5):
- t += [pkg.get_cpv() for pkg in self.find_packages(search_key, self.SET_INSTALLED) if not (pkg.is_testing(True) or pkg.is_masked())]
- elif not only_installed: # no need to run twice
- t += self.find_packages(search_key, self.SET_INSTALLED, only_cpv=True)
-
- if t:
- t = unique_array(t)
- return self.find_best(t, only_cpv)
-
- return None
-
- def _get_set (self, pkgSet):
- pkgSet = pkgSet.lower()
- if pkgSet == "": pkgSet = self.SET_ALL
-
- return self.setmap[pkgSet]()
-
- def find_packages (self, key = "", pkgSet = SystemInterface.SET_ALL, masked = False, with_version = True, only_cpv = False):
- return self.geneticize_list(self._get_set(pkgSet).find(key, masked, with_version, only_cpv), only_cpv or not with_version)
-
- def list_categories (self, name = None):
- categories = self.settings.settings.categories
- return filter(self.find_lambda(name), categories)
-
- def split_cpv (self, cpv):
- cpv = portage.dep_getcpv(cpv)
- return portage.catpkgsplit(cpv)
-
- def sort_package_list(self, pkglist):
- pkglist.sort(PortagePackage.compare_version) # XXX: waaah ... direct package naming... =/
- return pkglist
-
- def reload_settings (self):
- self.settings.load()
-
- def get_new_packages (self, packages):
- """Gets a list of packages and returns the best choice for each in the portage tree.
-
- @param packages: the list of packages
- @type packages: string[]
- @returns: the list of packages
- @rtype: backend.Package[]
- """
-
- new_packages = []
-
- def append(crit, best, inst):
- if not best:
- return
-
- if not best.is_installed() and (best.is_masked() or best.is_testing(True)): # check to not update unnecessairily
- for i in inst:
- if i.matches(crit):
- debug("The installed %s matches %s. Discarding upgrade to masked version.", i.get_cpv(), crit)
- return
-
- new_packages.append(best)
-
- for p in packages:
- inst = self.find_packages(p, self.SET_INSTALLED)
-
- best_p = self.find_best_match(p)
- if best_p is None:
- best_p = self.find_best_match(p, masked = True)
- if best_p is None:
- warning(_("No best match for %s. It seems not to be in the tree anymore.") % p)
- continue
- else:
- debug("Best match for %s is masked" % p)
-
- if len(inst) > 1:
- myslots = set()
- for i in inst: # get the slots of the installed packages
- myslots.add(i.get_package_settings("SLOT"))
-
- myslots.add(best_p.get_package_settings("SLOT")) # add the slot of the best package in portage
- for slot in myslots:
- crit = "%s:%s" % (p, slot)
- append(crit, self.find_best_match(crit), inst)
- else:
- append(p, best_p, inst)
-
- return new_packages
-
- def get_updated_packages (self):
- packages = self.get_new_packages(self.find_packages(pkgSet = self.SET_INSTALLED, with_version = False))
- packages = [x for x in packages if x is not None and not x.is_installed()]
- return packages
-
- def update_world (self, sets = ("world", "system"), newuse = False, deep = False):
- packages = set()
- map(packages.add, itt.chain(*[self.find_packages(pkgSet = s, with_version = False) for s in sets]))
-
- states = [(["RDEPEND", "PDEPEND"], True)]
- if self.with_bdeps():
- states.append((["DEPEND"], True))
-
- checked = []
- updating = []
- raw_checked = {}
- def check (p, add_not_installed = True, prev_appended = False):
- """Checks whether a package is updated or not."""
-
- if p.get_slot_cp() in checked:
- return
- else:
- if (not p.is_installed()) and (not add_not_installed):
- # don't add these packages to checked as we may see them again
- # - and then we might have add_not_installed being True
- return
- else:
- checked.append(p.get_slot_cp())
-
- appended = False
- tempDeep = False
-
- if not p.is_installed():
- oldList = self.find_packages(p.get_slot_cp(), self.SET_INSTALLED)
- if oldList:
- old = oldList[0] # we should only have one package here - else it is a bug
- else:
- oldList = self.sort_package_list(self.find_packages(p.get_cp(), self.SET_INSTALLED))
- if not oldList:
- info(_("Found a not installed dependency: %s.") % p.get_cpv())
- oldList = [p]
-
- old = oldList[-1]
-
- updating.append((p, old))
- appended = True
- p = old
-
- if newuse and p.is_installed() and p.is_in_system(): # there is no use to check newuse for a package which is not existing in portage anymore :)
-
- new_iuse = set(p.get_iuse_flags(installed = False)) # IUSE in the ebuild
- old_iuse = set(p.get_iuse_flags(installed = True)) # IUSE in the vardb
-
- # add forced flags, as they might trigger a rebuild
- new_iuse_f = set(p.get_iuse_flags(installed = False, removeForced = False))
- old_iuse_f = set(p.get_iuse_flags(installed = True, removeForced = False))
-
- if new_iuse.symmetric_difference(old_iuse): # difference between IUSE (w/o forced)
- tempDeep = True
- if not appended:
- updating.append((p,p))
- appended = True
-
- else: # check for difference between the _set_ useflags (w/ forced)
- if new_iuse_f.intersection(p.get_actual_use_flags()).symmetric_difference(old_iuse_f.intersection(p.get_installed_use_flags())):
- tempDeep = True
- if not appended:
- updating.append((p,p))
- appended = True
-
- if deep or tempDeep:
- if (appended or prev_appended) and len(states) < 2:
- real_states = states + [("PDEPEND", True), ("DEPEND", False)]
- else:
- real_states = states
- for state in real_states:
- for i in p.get_matched_dep_packages(state[0]):
- if i not in raw_checked or raw_checked[i] == False:
- raw_checked.update({i : state[1]})
- bm = self.get_new_packages([i])
- if not bm:
- warning(_("Bug? No best match could be found for '%(package)s'. Needed by: '%(cpv)s'."), {"package" : i, "cpv": p.get_cpv()})
- else:
- for pkg in bm:
- if not pkg: continue
- check(pkg, state[1], appended) # XXX: should be 'or'ed with prev_appended?
-
- for p in self.get_new_packages(packages):
- if not p: continue # if a masked package is installed we have "None" here
- check(p, True)
-
- return updating
-
- def get_use_desc (self, flag, package = None):
- # In the first run the dictionaries 'use_descs' and 'local_use_descs' are filled.
-
- # fill cache if needed
- if not self.use_descs and not self.local_use_descs:
- for dir in [self.settings.settings["PORTDIR"]] + self.settings.settings["PORTDIR_OVERLAY"].split():
-
- # read use.desc
- try:
- f = open(os.path.join(dir, "profiles/use.desc"))
- for line in f:
- line = line.strip()
- if line and line[0] != '#':
- fields = [x.strip() for x in line.split(" - ",1)]
- if len(fields) == 2:
- self.use_descs[fields[0]] = fields[1]
- except IOError:
- pass
- finally:
- f.close()
-
- # read use.local.desc
- try:
- f = open(os.path.join(dir, "profiles/use.local.desc"))
- for line in f:
- line = line.strip()
- if line and line[0] != '#':
- fields = [x.strip() for x in line.split(":",1)]
- if len(fields) == 2:
- subfields = [x.strip() for x in fields[1].split(" - ",1)]
- if len(subfields) == 2:
- self.local_use_descs[fields[0]].update([subfields])
- except IOError:
- pass
- finally:
- f.close()
-
- # start
- desc = self.use_descs.get(flag, "")
- if package is not None:
- if package in self.local_use_descs:
- desc = self.local_use_descs[package].get(flag, desc)
-
- return desc
+ def __init__ (self):
+ """Constructor."""
+ self.settings = PortageSettings()
+ portage.WORLD_FILE = os.path.join(self.settings.settings["ROOT"],portage.WORLD_FILE)
+
+ self.use_descs = {}
+ self.local_use_descs = defaultdict(dict)
+
+ self.setmap = {
+ self.SET_ALL : syssets.AllSet,
+ self.SET_INSTALLED : syssets.InstalledSet,
+ self.SET_UNINSTALLED : syssets.UninstalledSet,
+ self.SET_TREE : syssets.TreeSet,
+ "world" : syssets.WorldSet,
+ "system" : syssets.SystemSet
+ }
+
+ def has_set_support (self):
+ return False
+
+ def get_sets (self, description = False):
+ if description:
+ return (("world", "The world set."), ("system", "The system set."))
+ else:
+ return ("world", "system")
+
+ def get_version (self):
+ return "Portage %s" % portage.VERSION
+
+ def new_package (self, cpv):
+ return PortagePackage(cpv)
+
+ def get_config_path (self):
+ return portage.USER_CONFIG_PATH
+
+ def get_merge_command (self):
+ return ["/usr/bin/python", "/usr/bin/emerge"]
+
+ def get_sync_command (self):
+ return self.get_merge_command()+["--sync"]
+
+ def get_oneshot_option (self):
+ return ["--oneshot"]
+
+ def get_newuse_option (self):
+ return ["--newuse"]
+
+ def get_deep_option (self):
+ return ["--deep"]
+
+ def get_update_option (self):
+ return ["--update"]
+
+ def get_pretend_option (self):
+ return ["--pretend", "--verbose"]
+
+ def get_unmerge_option (self):
+ return ["--unmerge"]
+
+ def get_environment (self):
+ default_opts = self.get_global_settings("EMERGE_DEFAULT_OPTS")
+ opts = dict(os.environ)
+ opts.update(TERM = "xterm") # emulate terminal :)
+ opts.update(PAGER = "less") # force less
+
+ if default_opts:
+ opt_list = default_opts.split()
+ changed = False
+
+ for option in ["--ask", "-a", "--pretend", "-p"]:
+ if option in opt_list:
+ opt_list.remove(option)
+ changed = True
+
+ if changed:
+ opts.update(EMERGE_DEFAULT_OPTS = " ".join(opt_list))
+
+ return opts
+
+ def cpv_matches (self, cpv, criterion):
+ if portage.match_from_list(criterion, [cpv]) == []:
+ return False
+ else:
+ return True
+
+ def with_bdeps(self):
+ """Returns whether the "--with-bdeps" option is set to true.
+
+ @returns: the value of --with-bdeps
+ @rtype: boolean
+ """
+
+ settings = self.get_global_settings("EMERGE_DEFAULT_OPTS").split()
+ for s in settings:
+ if self.withBdepsRE.match(s):
+ return True
+
+ return False
+
+ def find_lambda (self, name):
+ """Returns the function needed by all the find_all_*-functions. Returns None if no name is given.
+
+ @param name: name to build the function of
+ @type name: string or RE
+ @returns:
+ 1. None if no name is given
+ 2. a lambda function
+ @rtype: function
+ """
+
+ if name != None:
+ if isinstance(name, str):
+ return lambda x: re.match(".*"+name+".*",x, re.I)
+ else: # assume regular expression
+ return lambda x: name.match(x)
+ else:
+ return lambda x: True
+
+ def geneticize_list (self, list_of_packages, only_cpv = False):
+ """Convertes a list of cpv's into L{backend.Package}s.
+
+ @param list_of_packages: the list of packages
+ @type list_of_packages: string[]
+ @param only_cpv: do nothing - return the passed list
+ @type only_cpv: boolean
+ @returns: converted list
+ @rtype: PortagePackage[]
+ """
+
+ if not only_cpv:
+ return [self.new_package(x) for x in list_of_packages]
+ elif not isinstance(list_of_packages, list):
+ return list(list_of_packages)
+ else:
+ return list_of_packages
+
+ def get_global_settings (self, key):
+ self.settings.settings.reset()
+ return self.settings.settings[key]
+
+ def find_best (self, list, only_cpv = False):
+ if only_cpv:
+ return portage.best(list)
+ else:
+ return self.new_package(portage.best(list))
+
+ def find_best_match (self, search_key, masked = False, only_installed = False, only_cpv = False):
+ t = []
+
+ if not only_installed:
+ pkgSet = self.SET_TREE
+ else:
+ pkgSet = self.SET_INSTALLED
+
+ t = self.find_packages(search_key, pkgSet = pkgSet, masked = masked, with_version = True, only_cpv = True)
+
+ if VERSION >= (2,1,5):
+ t += [pkg.get_cpv() for pkg in self.find_packages(search_key, self.SET_INSTALLED) if not (pkg.is_testing(True) or pkg.is_masked())]
+ elif not only_installed: # no need to run twice
+ t += self.find_packages(search_key, self.SET_INSTALLED, only_cpv=True)
+
+ if t:
+ t = unique_array(t)
+ return self.find_best(t, only_cpv)
+
+ return None
+
+ def _get_set (self, pkgSet):
+ pkgSet = pkgSet.lower()
+ if pkgSet == "": pkgSet = self.SET_ALL
+
+ return self.setmap[pkgSet]()
+
+ def find_packages (self, key = "", pkgSet = SystemInterface.SET_ALL, masked = False, with_version = True, only_cpv = False):
+ return self.geneticize_list(self._get_set(pkgSet).find(key, masked, with_version, only_cpv), only_cpv or not with_version)
+
+ def list_categories (self, name = None):
+ categories = self.settings.settings.categories
+ return filter(self.find_lambda(name), categories)
+
+ def split_cpv (self, cpv):
+ cpv = portage.dep_getcpv(cpv)
+ return portage.catpkgsplit(cpv)
+
+ def sort_package_list(self, pkglist):
+ pkglist.sort(PortagePackage.compare_version) # XXX: waaah ... direct package naming... =/
+ return pkglist
+
+ def reload_settings (self):
+ self.settings.load()
+
+ def get_new_packages (self, packages):
+ """Gets a list of packages and returns the best choice for each in the portage tree.
+
+ @param packages: the list of packages
+ @type packages: string[]
+ @returns: the list of packages
+ @rtype: backend.Package[]
+ """
+
+ new_packages = []
+
+ def append(crit, best, inst):
+ if not best:
+ return
+
+ if not best.is_installed() and (best.is_masked() or best.is_testing(True)): # check to not update unnecessairily
+ for i in inst:
+ if i.matches(crit):
+ debug("The installed %s matches %s. Discarding upgrade to masked version.", i.get_cpv(), crit)
+ return
+
+ new_packages.append(best)
+
+ for p in packages:
+ inst = self.find_packages(p, self.SET_INSTALLED)
+
+ best_p = self.find_best_match(p)
+ if best_p is None:
+ best_p = self.find_best_match(p, masked = True)
+ if best_p is None:
+ warning(_("No best match for %s. It seems not to be in the tree anymore.") % p)
+ continue
+ else:
+ debug("Best match for %s is masked" % p)
+
+ if len(inst) > 1:
+ myslots = set()
+ for i in inst: # get the slots of the installed packages
+ myslots.add(i.get_package_settings("SLOT"))
+
+ myslots.add(best_p.get_package_settings("SLOT")) # add the slot of the best package in portage
+ for slot in myslots:
+ crit = "%s:%s" % (p, slot)
+ append(crit, self.find_best_match(crit), inst)
+ else:
+ append(p, best_p, inst)
+
+ return new_packages
+
+ def get_updated_packages (self):
+ packages = self.get_new_packages(self.find_packages(pkgSet = self.SET_INSTALLED, with_version = False))
+ packages = [x for x in packages if x is not None and not x.is_installed()]
+ return packages
+
+ def update_world (self, sets = ("world", "system"), newuse = False, deep = False):
+ packages = set()
+ map(packages.add, itt.chain(*[self.find_packages(pkgSet = s, with_version = False) for s in sets]))
+
+ states = [(["RDEPEND", "PDEPEND"], True)]
+ if self.with_bdeps():
+ states.append((["DEPEND"], True))
+
+ checked = []
+ updating = []
+ raw_checked = {}
+ def check (p, add_not_installed = True, prev_appended = False):
+ """Checks whether a package is updated or not."""
+
+ if p.get_slot_cp() in checked:
+ return
+ else:
+ if (not p.is_installed()) and (not add_not_installed):
+ # don't add these packages to checked as we may see them again
+ # - and then we might have add_not_installed being True
+ return
+ else:
+ checked.append(p.get_slot_cp())
+
+ appended = False
+ tempDeep = False
+
+ if not p.is_installed():
+ oldList = self.find_packages(p.get_slot_cp(), self.SET_INSTALLED)
+ if oldList:
+ old = oldList[0] # we should only have one package here - else it is a bug
+ else:
+ oldList = self.sort_package_list(self.find_packages(p.get_cp(), self.SET_INSTALLED))
+ if not oldList:
+ info(_("Found a not installed dependency: %s.") % p.get_cpv())
+ oldList = [p]
+
+ old = oldList[-1]
+
+ updating.append((p, old))
+ appended = True
+ p = old
+
+ if newuse and p.is_installed() and p.is_in_system(): # there is no use to check newuse for a package which is not existing in portage anymore :)
+
+ new_iuse = set(p.get_iuse_flags(installed = False)) # IUSE in the ebuild
+ old_iuse = set(p.get_iuse_flags(installed = True)) # IUSE in the vardb
+
+ # add forced flags, as they might trigger a rebuild
+ new_iuse_f = set(p.get_iuse_flags(installed = False, removeForced = False))
+ old_iuse_f = set(p.get_iuse_flags(installed = True, removeForced = False))
+
+ if new_iuse.symmetric_difference(old_iuse): # difference between IUSE (w/o forced)
+ tempDeep = True
+ if not appended:
+ updating.append((p,p))
+ appended = True
+
+ else: # check for difference between the _set_ useflags (w/ forced)
+ if new_iuse_f.intersection(p.get_actual_use_flags()).symmetric_difference(old_iuse_f.intersection(p.get_installed_use_flags())):
+ tempDeep = True
+ if not appended:
+ updating.append((p,p))
+ appended = True
+
+ if deep or tempDeep:
+ if (appended or prev_appended) and len(states) < 2:
+ real_states = states + [("PDEPEND", True), ("DEPEND", False)]
+ else:
+ real_states = states
+ for state in real_states:
+ for i in p.get_matched_dep_packages(state[0]):
+ if i not in raw_checked or raw_checked[i] == False:
+ raw_checked.update({i : state[1]})
+ bm = self.get_new_packages([i])
+ if not bm:
+ warning(_("Bug? No best match could be found for '%(package)s'. Needed by: '%(cpv)s'."), {"package" : i, "cpv": p.get_cpv()})
+ else:
+ for pkg in bm:
+ if not pkg: continue
+ check(pkg, state[1], appended) # XXX: should be 'or'ed with prev_appended?
+
+ for p in self.get_new_packages(packages):
+ if not p: continue # if a masked package is installed we have "None" here
+ check(p, True)
+
+ return updating
+
+ def get_use_desc (self, flag, package = None):
+ # In the first run the dictionaries 'use_descs' and 'local_use_descs' are filled.
+
+ # fill cache if needed
+ if not self.use_descs and not self.local_use_descs:
+ for dir in [self.settings.settings["PORTDIR"]] + self.settings.settings["PORTDIR_OVERLAY"].split():
+
+ # read use.desc
+ try:
+ f = open(os.path.join(dir, "profiles/use.desc"))
+ for line in f:
+ line = line.strip()
+ if line and line[0] != '#':
+ fields = [x.strip() for x in line.split(" - ",1)]
+ if len(fields) == 2:
+ self.use_descs[fields[0]] = fields[1]
+ except IOError:
+ pass
+ finally:
+ f.close()
+
+ # read use.local.desc
+ try:
+ f = open(os.path.join(dir, "profiles/use.local.desc"))
+ for line in f:
+ line = line.strip()
+ if line and line[0] != '#':
+ fields = [x.strip() for x in line.split(":",1)]
+ if len(fields) == 2:
+ subfields = [x.strip() for x in fields[1].split(" - ",1)]
+ if len(subfields) == 2:
+ self.local_use_descs[fields[0]].update([subfields])
+ except IOError:
+ pass
+ finally:
+ f.close()
+
+ # start
+ desc = self.use_descs.get(flag, "")
+ if package is not None:
+ if package in self.local_use_descs:
+ desc = self.local_use_descs[package].get(flag, desc)
+
+ return desc
diff --git a/portato/backend/portage/system_22.py b/portato/backend/portage/system_22.py
index a329ab0..d5b605f 100644
--- a/portato/backend/portage/system_22.py
+++ b/portato/backend/portage/system_22.py
@@ -24,41 +24,41 @@ from . import sets as syssets
class PortageSystem_22 (PortageSystem):
- def __init__ (self):
- self.settings = PortageSettings_22()
- portage.WORLD_FILE = os.path.join(self.settings.settings["ROOT"],portage.WORLD_FILE)
+ def __init__ (self):
+ self.settings = PortageSettings_22()
+ portage.WORLD_FILE = os.path.join(self.settings.settings["ROOT"],portage.WORLD_FILE)
- self.use_descs = {}
- self.local_use_descs = defaultdict(dict)
+ self.use_descs = {}
+ self.local_use_descs = defaultdict(dict)
- self.setmap = {
- self.SET_ALL : syssets.AllSet,
- self.SET_INSTALLED : syssets.InstalledSet,
- self.SET_UNINSTALLED : syssets.UninstalledSet,
- self.SET_TREE : syssets.TreeSet
- }
+ self.setmap = {
+ self.SET_ALL : syssets.AllSet,
+ self.SET_INSTALLED : syssets.InstalledSet,
+ self.SET_UNINSTALLED : syssets.UninstalledSet,
+ self.SET_TREE : syssets.TreeSet
+ }
- def get_update_option (self):
- return ["--update", "--oneshot"] # --oneshot to not record the used sets in world file
+ def get_update_option (self):
+ return ["--update", "--oneshot"] # --oneshot to not record the used sets in world file
- def has_set_support (self):
- return True
+ def has_set_support (self):
+ return True
- def get_sets (self, description = False):
- if description:
- return ((name, set.description) for name, set in self.settings.setsconfig.getSets().iteritems())
- else:
- return tuple(self.settings.setsconfig.getSets())
+ def get_sets (self, description = False):
+ if description:
+ return ((name, set.description) for name, set in self.settings.setsconfig.getSets().iteritems())
+ else:
+ return tuple(self.settings.setsconfig.getSets())
- def _get_set (self, pkgSet):
- pkgSet = pkgSet.lower()
- if pkgSet == "": pkgSet = self.SET_ALL
+ def _get_set (self, pkgSet):
+ pkgSet = pkgSet.lower()
+ if pkgSet == "": pkgSet = self.SET_ALL
- s = self.setmap.get(pkgSet, None)
- if s is None:
- return syssets.PortageSet(pkgSet)
- else:
- return s()
+ s = self.setmap.get(pkgSet, None)
+ if s is None:
+ return syssets.PortageSet(pkgSet)
+ else:
+ return s()
- def new_package (self, cpv):
- return PortagePackage_22(cpv)
+ def new_package (self, cpv):
+ return PortagePackage_22(cpv)
diff --git a/portato/backend/system_interface.py b/portato/backend/system_interface.py
index 1cb0ed1..6f13042 100644
--- a/portato/backend/system_interface.py
+++ b/portato/backend/system_interface.py
@@ -11,282 +11,282 @@
# Written by René 'Necoro' Neumann <necoro@necoro.net>
class SystemInterface (object):
-
- SET_ALL = "__portato_all__"
- SET_TREE = "__portato_tree__"
- SET_INSTALLED = "__portato_installed__"
- SET_UNINSTALLED = "__portato_uninstalled__"
-
- def has_set_support (self):
- """Signals, whether this backend supports sets.
-
- @rtype: boolean
- """
- raise NotImplementedError
-
- def get_sets (self):
- """Returns all supported sets in tuples consisting of name and description.
- If sets aren't supported, at least "world" and "system" have to be returned.
-
- @rtype: iter(string, string)
- """
- raise NotImplementedError
-
- def get_version (self):
- """Returns the version of the used backend.
-
- @rtype: string
- """
- raise NotImplementedError
-
- def split_cpv (self, cpv):
- """Splits a cpv into all its parts.
-
- @param cpv: the cpv to split
- @type cpv: string
- @returns: the splitted cpv
- @rtype: string[]
- """
-
- raise NotImplementedError
-
- def cpv_matches (self, cpv, criterion):
- """Checks whether a cpv matches a specific criterion.
-
- @param cpv: cpv to check
- @type cpv: string
- @param criterion: criterion to check against
- @type criterion: string
- @returns: match result
- @rtype: boolean
- """
-
- raise NotImplementedError
-
- def find_best(self, list, only_cpv = False):
- """Returns the best package out of a list of packages.
-
- @param list: the list of packages to select from
- @type list: string[]
- @param only_cpv: do not return package but only the cpv
- @type only_cpv: boolean
-
- @returns: the best package
- @rtype: backend.Package or string
- """
-
- raise NotImplementedError
-
- def find_best_match (self, search_key, masked = False, only_installed = False, only_cpv = False):
- """Finds the best match in the portage tree. It does not find masked packages!
-
- @param search_key: the key to find in the portage tree
- @type search_key: string
- @param masked: if True, also look for masked packages
- @type masked: boolean
- @param only_installed: if True, only installed packages are searched
- @type only_installed: boolean
- @param only_cpv: do not return package but only the cpv
- @type only_cpv: boolean
-
- @returns: the package found or None
- @rtype: backend.Package or string
- """
-
- raise NotImplementedError
-
- def find_packages (self, key, pkgSet = SET_ALL, masked = False, with_version = True, only_cpv = False):
- """This returns a list of packages matching the key.
- As key, it is allowed to use basic regexps (".*") and the normal package specs. But not a combination
- of them.
-
- @param key: the key to look for
- @type key: string
- @param all: the package set to use
- @type all: string
- @param masked: if True, also look for masked packages
- @type masked: boolean
- @param with_version: if True, return CPVs - else CP
- @type with_version: boolean
- @param only_cpv: do not return package but only the cpv. if with_version is False, this is ignored
- @type only_cpv: boolean
-
- @returns: list of found packages
- @rtype: backend.Package[] or string[]
- """
-
- raise NotImplementedError
-
- def list_categories (self, name = None):
- """Finds all categories matching a name or all if no name is specified.
-
- @param name: the name to look for - it is expanded to .*name.* ; if None, all categories are returned
- @type name: string or None
- @returns: all categories found
- @rtype: string[]
- """
-
- raise NotImplementedError
-
- def sort_package_list(self, pkglist):
- """Sorts a package list in the same manner portage does.
-
- @param pkglist: list to sort
- @type pkglist: Packages[]
- """
-
- raise NotImplementedError
-
- def reload_settings (self):
- """Reloads portage."""
-
- raise NotImplementedError
-
- def update_world (self, newuse = False, deep = False):
- """Calculates the packages to get updated in an update world.
-
- @param newuse: Checks if a use-flag has a different state then to install time.
- @type newuse: boolean
- @param deep: Not only check world packages but also there dependencies.
- @type deep: boolean
- @returns: a list of the tuple (new_package, old_package)
- @rtype: (backend.Package, backend.Package)[]
- """
-
- raise NotImplementedError
-
- def get_updated_packages (self):
- """Returns the packages for which a newer package is available in the portage tree and installable (thus not masked).
- This differs from update_world as it takes all installed packages into account but ignores changed useflags.
-
- @returns: the list of new packages
- @rtype: backend.Package[]
- """
-
- raise NotImplementedError
-
- def get_use_desc (self, flag, package = None):
- """Returns the description of a specific useflag or None if no desc was found.
- If a package is given (in the <cat>/<name> format) the local use descriptions are searched too.
-
- @param flag: flag to get the description for
- @type flag: string
- @param package: name of a package: if given local use descriptions are searched too
- @type package: cp-string
- @returns: found description
- @rtype: string
- """
-
- raise NotImplementedError
-
- def get_global_settings(self, key):
- """Returns the value of a global setting, i.e. ARCH, USE, ROOT, DISTDIR etc.
-
- @param key: the setting to return
- @type key: string
- @returns: the value of this setting
- @rtype: string
- """
+
+ SET_ALL = "__portato_all__"
+ SET_TREE = "__portato_tree__"
+ SET_INSTALLED = "__portato_installed__"
+ SET_UNINSTALLED = "__portato_uninstalled__"
+
+ def has_set_support (self):
+ """Signals, whether this backend supports sets.
+
+ @rtype: boolean
+ """
+ raise NotImplementedError
+
+ def get_sets (self):
+ """Returns all supported sets in tuples consisting of name and description.
+ If sets aren't supported, at least "world" and "system" have to be returned.
+
+ @rtype: iter(string, string)
+ """
+ raise NotImplementedError
+
+ def get_version (self):
+ """Returns the version of the used backend.
+
+ @rtype: string
+ """
+ raise NotImplementedError
+
+ def split_cpv (self, cpv):
+ """Splits a cpv into all its parts.
+
+ @param cpv: the cpv to split
+ @type cpv: string
+ @returns: the splitted cpv
+ @rtype: string[]
+ """
+
+ raise NotImplementedError
+
+ def cpv_matches (self, cpv, criterion):
+ """Checks whether a cpv matches a specific criterion.
+
+ @param cpv: cpv to check
+ @type cpv: string
+ @param criterion: criterion to check against
+ @type criterion: string
+ @returns: match result
+ @rtype: boolean
+ """
+
+ raise NotImplementedError
+
+ def find_best(self, list, only_cpv = False):
+ """Returns the best package out of a list of packages.
+
+ @param list: the list of packages to select from
+ @type list: string[]
+ @param only_cpv: do not return package but only the cpv
+ @type only_cpv: boolean
+
+ @returns: the best package
+ @rtype: backend.Package or string
+ """
+
+ raise NotImplementedError
+
+ def find_best_match (self, search_key, masked = False, only_installed = False, only_cpv = False):
+ """Finds the best match in the portage tree. It does not find masked packages!
+
+ @param search_key: the key to find in the portage tree
+ @type search_key: string
+ @param masked: if True, also look for masked packages
+ @type masked: boolean
+ @param only_installed: if True, only installed packages are searched
+ @type only_installed: boolean
+ @param only_cpv: do not return package but only the cpv
+ @type only_cpv: boolean
+
+ @returns: the package found or None
+ @rtype: backend.Package or string
+ """
+
+ raise NotImplementedError
+
+ def find_packages (self, key, pkgSet = SET_ALL, masked = False, with_version = True, only_cpv = False):
+ """This returns a list of packages matching the key.
+ As key, it is allowed to use basic regexps (".*") and the normal package specs. But not a combination
+ of them.
+
+ @param key: the key to look for
+ @type key: string
+ @param all: the package set to use
+ @type all: string
+ @param masked: if True, also look for masked packages
+ @type masked: boolean
+ @param with_version: if True, return CPVs - else CP
+ @type with_version: boolean
+ @param only_cpv: do not return package but only the cpv. if with_version is False, this is ignored
+ @type only_cpv: boolean
+
+ @returns: list of found packages
+ @rtype: backend.Package[] or string[]
+ """
+
+ raise NotImplementedError
+
+ def list_categories (self, name = None):
+ """Finds all categories matching a name or all if no name is specified.
+
+ @param name: the name to look for - it is expanded to .*name.* ; if None, all categories are returned
+ @type name: string or None
+ @returns: all categories found
+ @rtype: string[]
+ """
+
+ raise NotImplementedError
+
+ def sort_package_list(self, pkglist):
+ """Sorts a package list in the same manner portage does.
+
+ @param pkglist: list to sort
+ @type pkglist: Packages[]
+ """
+
+ raise NotImplementedError
+
+ def reload_settings (self):
+ """Reloads portage."""
+
+ raise NotImplementedError
+
+ def update_world (self, newuse = False, deep = False):
+ """Calculates the packages to get updated in an update world.
+
+ @param newuse: Checks if a use-flag has a different state then to install time.
+ @type newuse: boolean
+ @param deep: Not only check world packages but also there dependencies.
+ @type deep: boolean
+ @returns: a list of the tuple (new_package, old_package)
+ @rtype: (backend.Package, backend.Package)[]
+ """
+
+ raise NotImplementedError
+
+ def get_updated_packages (self):
+ """Returns the packages for which a newer package is available in the portage tree and installable (thus not masked).
+ This differs from update_world as it takes all installed packages into account but ignores changed useflags.
+
+ @returns: the list of new packages
+ @rtype: backend.Package[]
+ """
+
+ raise NotImplementedError
+
+ def get_use_desc (self, flag, package = None):
+ """Returns the description of a specific useflag or None if no desc was found.
+ If a package is given (in the <cat>/<name> format) the local use descriptions are searched too.
+
+ @param flag: flag to get the description for
+ @type flag: string
+ @param package: name of a package: if given local use descriptions are searched too
+ @type package: cp-string
+ @returns: found description
+ @rtype: string
+ """
+
+ raise NotImplementedError
+
+ def get_global_settings(self, key):
+ """Returns the value of a global setting, i.e. ARCH, USE, ROOT, DISTDIR etc.
+
+ @param key: the setting to return
+ @type key: string
+ @returns: the value of this setting
+ @rtype: string
+ """
- raise NotImplementedError
+ raise NotImplementedError
- def new_package (self, cpv):
- """Returns an instance of the appropriate Package-Subclass.
+ def new_package (self, cpv):
+ """Returns an instance of the appropriate Package-Subclass.
- @param cpv: the cpv to create the package from
- @type cpv: string
- @returns: a new Package-object.
- @rtype: Package
- """
+ @param cpv: the cpv to create the package from
+ @type cpv: string
+ @returns: a new Package-object.
+ @rtype: Package
+ """
- raise NotImplementedError
+ raise NotImplementedError
- def get_config_path (self):
- """Returns the actual path to the config files.
-
- @returns: the path, e.g. /etc/portage
- @rtype: string
- """
+ def get_config_path (self):
+ """Returns the actual path to the config files.
+
+ @returns: the path, e.g. /etc/portage
+ @rtype: string
+ """
- raise NotImplementedError
+ raise NotImplementedError
- def get_sync_command (self):
- """Returns the command(s) to run for syncing. This can be overridden by the user.
+ def get_sync_command (self):
+ """Returns the command(s) to run for syncing. This can be overridden by the user.
- @returns: command to run
- @rtype: string[]
- """
+ @returns: command to run
+ @rtype: string[]
+ """
- raise NotImplementedError
+ raise NotImplementedError
- def get_merge_command (self):
- """Returns the command(s) to run for the merging.
+ def get_merge_command (self):
+ """Returns the command(s) to run for the merging.
- @returns: command to run
- @rtype: string[]
- """
+ @returns: command to run
+ @rtype: string[]
+ """
- raise NotImplementedError
+ raise NotImplementedError
- def get_oneshot_option (self):
- """Returns the options to append for marking a merge as "oneshot".
+ def get_oneshot_option (self):
+ """Returns the options to append for marking a merge as "oneshot".
- @returns: option(s) to append
- @rtype: string[]
- """
+ @returns: option(s) to append
+ @rtype: string[]
+ """
- raise NotImplementedError
+ raise NotImplementedError
- def get_newuse_option (self):
- """Returns the options to append for marking a merge as "newuse".
+ def get_newuse_option (self):
+ """Returns the options to append for marking a merge as "newuse".
- @returns: option(s) to append
- @rtype: string[]
- """
+ @returns: option(s) to append
+ @rtype: string[]
+ """
- raise NotImplementedError
+ raise NotImplementedError
- def get_deep_option (self):
- """Returns the options to append for marking a merge as "deep".
+ def get_deep_option (self):
+ """Returns the options to append for marking a merge as "deep".
- @returns: option(s) to append
- @rtype: string[]
- """
+ @returns: option(s) to append
+ @rtype: string[]
+ """
- raise NotImplementedError
+ raise NotImplementedError
- def get_update_option (self):
- """Returns the options to append for marking a merge as "update".
+ def get_update_option (self):
+ """Returns the options to append for marking a merge as "update".
- @returns: option(s) to append
- @rtype: string[]
- """
+ @returns: option(s) to append
+ @rtype: string[]
+ """
- raise NotImplementedError
+ raise NotImplementedError
- def get_pretend_option (self):
- """Returns the options to append for marking a merge as "pretend".
+ def get_pretend_option (self):
+ """Returns the options to append for marking a merge as "pretend".
- @returns: option(s) to append
- @rtype: string[]
- """
+ @returns: option(s) to append
+ @rtype: string[]
+ """
- raise NotImplementedError
+ raise NotImplementedError
- def get_unmerge_option (self):
- """Returns the options to append for marking a merge as "unmerge".
+ def get_unmerge_option (self):
+ """Returns the options to append for marking a merge as "unmerge".
- @returns: option(s) to append
- @rtype: string[]
- """
+ @returns: option(s) to append
+ @rtype: string[]
+ """
- raise NotImplementedError
+ raise NotImplementedError
- def get_environment (self):
- """Returns a dictionary of environment variables to set prior to do an emerge.
+ def get_environment (self):
+ """Returns a dictionary of environment variables to set prior to do an emerge.
- @returns: environment variables
- @rtype: dict{string : string}
- """
+ @returns: environment variables
+ @rtype: dict{string : string}
+ """
- raise NotImplementedError
+ raise NotImplementedError
diff --git a/portato/config_parser.py b/portato/config_parser.py
index 6515d1b..e3f78db 100644
--- a/portato/config_parser.py
+++ b/portato/config_parser.py
@@ -19,25 +19,25 @@ Thus it keeps comments and structuring of the file.
:Variables:
- DELIMITER : string[]
- list of delimiters allowed
+ DELIMITER : string[]
+ list of delimiters allowed
- COMMENT : string []
- comment marks allowed
+ COMMENT : string []
+ comment marks allowed
- TRUE
- Regular expression for all TRUE values allowed.
- Currently supported are the values (case insensitive): true, 1, on, wahr, ja, yes.
+ TRUE
+ Regular expression for all TRUE values allowed.
+ Currently supported are the values (case insensitive): true, 1, on, wahr, ja, yes.
- FALSE
- Regular expression for all FALSE values allowed.
- Currently supported are the values (case insensitive): false, 0, off, falsch, nein, no.
+ FALSE
+ Regular expression for all FALSE values allowed.
+ Currently supported are the values (case insensitive): false, 0, off, falsch, nein, no.
- SECTION
- Regular expression allowing the recognition of a section header.
+ SECTION
+ Regular expression allowing the recognition of a section header.
- EXPRESSION
- Regular expression defining a normal option-value pair.
+ EXPRESSION
+ Regular expression defining a normal option-value pair.
"""
from __future__ import absolute_import, with_statement
@@ -58,473 +58,473 @@ SECTION = re.compile("\s*\[(?P<name>\w(\w|[-_])*)\]\s*")
EXPRESSION = re.compile(r"\s*(?P<key>\w(\w|[-_])*)\s*[:=]\s*(?P<value>.*)\s*")
class KeyNotFoundException (KeyError):
- """
- Exception signaling, that a specific key could not be found in the configuration.
- """
- pass
+ """
+ Exception signaling, that a specific key could not be found in the configuration.
+ """
+ pass
class SectionNotFoundException (KeyError):
- """
- Exception signaling, that a section could not be found in the configuration.
- """
- pass
+ """
+ Exception signaling, that a section could not be found in the configuration.
+ """
+ pass
class Value (object):
- """
- Class defining a value of a key.
-
- :IVariables:
-
- value
- The specific value.
-
- old
- The old value
-
- line : int
- The line in the config file.
-
- boolean : boolean
- The boolean meaning of this value. Set this to ``None`` if this is not a boolean.
-
- changed : boolean
- Set to True if the value has been changed.
- """
-
-
- def __init__ (self, value, line, bool = None):
- """
- Constructor.
-
- :Parameters:
-
- value : string
- the value
-
- line : int
- the line in the config file
-
- bool : boolean
- The boolean meaning of the value. Set this to ``None`` if this is not a 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.
-
- :rtype: string
- """
-
- return self.__value
-
- def is_bool (self):
- """
- Returns whether the actual value has a boolean meaning.
-
- :rtype: boolean
- """
-
- return (self.boolean != None)
-
- def __str__ (self):
- return str(self.__value)
-
- def __repr__ (self):
- return self.__str__()
-
- value = property(get,set)
-
+ """
+ Class defining a value of a key.
+
+ :IVariables:
+
+ value
+ The specific value.
+
+ old
+ The old value
+
+ line : int
+ The line in the config file.
+
+ boolean : boolean
+ The boolean meaning of this value. Set this to ``None`` if this is not a boolean.
+
+ changed : boolean
+ Set to True if the value has been changed.
+ """
+
+
+ def __init__ (self, value, line, bool = None):
+ """
+ Constructor.
+
+ :Parameters:
+
+ value : string
+ the value
+
+ line : int
+ the line in the config file
+
+ bool : boolean
+ The boolean meaning of the value. Set this to ``None`` if this is not a 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.
+
+ :rtype: string
+ """
+
+ return self.__value
+
+ def is_bool (self):
+ """
+ Returns whether the actual value has a boolean meaning.
+
+ :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 parser class.
-
- :CVariables:
-
- true_false : string -> string
- A mapping from the truth values to their opposits.
-
- :IVariables:
-
- file : string
- the file to scan
- cache : string[]
- caches the content of the file
- vars : string -> (string -> `Value`)
- the found options grouped by section
- pos : int -> (int, int)
- the positions of the values grouped by lineno
- """
-
- # 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.writelock = Lock()
- self.__initialize()
-
- def __initialize (self):
- """Private method which initializes our dictionaries."""
-
- self.vars = {"MAIN": {}}
- self.cache = [] # file cache
- self.pos = {} # stores the positions of the matches
- self.sections = {"MAIN" : -1} # the line with the section header
-
- def _invert (self, val):
- """
- Invertes a given boolean.
-
- :param val: value to invert
- :type val: string
- :returns: inverted value
- :rtype: string
-
- :see: `true_false`
- """
-
- return self.true_false[val.lower()]
-
- def parse (self):
- """
- Parses the file.
- """
-
- # read into cache
- with open(self.file, "r") as f:
- self.cache = f.readlines()
-
- # 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("name").upper()
- self.sections[sec] = count
- if sec != section:
- self.vars[sec] = {}
- section = sec
- continue
-
- # look for an expression
- match = EXPRESSION.search(line)
- if match:
- val = match.group("value")
-
- # find the boolean value
- bool = None
- if TRUE.match(val):
- bool = True
- elif FALSE.match(val):
- bool = False
-
- # insert
- key = match.group("key").lower()
- self.vars[section][key] = Value(val, count, bool = bool)
- self.pos[count] = match.span("value")
- else: # neither comment nor empty nor expression nor section => error
- error(_("Unrecognized line in configuration: %s"), line)
-
- def _access (self, key, section):
- """
- Private method for accessing the saved variables.
-
- :Parameters:
-
- key : string
- the key
- section : string
- the section
-
- :returns: the value wanted
- :rtype: `Value`
-
- :Exceptions:
-
- - `KeyNotFoundException` : Raised if the specified key could not be found.
- - `SectionNotFoundException` : Raised if the specified section could not be found.
- """
-
- try:
- sectiondict = self.vars[section]
- except KeyError:
- raise SectionNotFoundException("Section '%s' not found in file '%s'." % (section, self.file))
-
- try:
- return sectiondict[key]
- except KeyError:
- raise KeyNotFoundException("Key '%s' not found in section '%s' in file '%s'." % (key, section, self.file))
-
- def get (self, key, section = "MAIN"):
- """
- Returns the value of a given key in a section.
-
- :Parameters:
-
- key : string
- the key
- section : string
- the section
-
- :returns: value
- :rtype: string
-
- :Exceptions:
-
- - `KeyNotFoundException` : Raised if the specified key could not be found.
- - `SectionNotFoundException` : Raised if the specified section could not be found.
- """
-
- section = section.upper()
- key = key.lower()
- return self._access(key, section).value
-
- def get_boolean (self, key, section = "MAIN"):
- """
- Returns the boolean value of a given key in a section.
-
- :Parameters:
-
- key : string
- the key
- section : string
- the section
-
- :returns: value
- :rtype: boolean
-
- :Exceptions:
-
- - `KeyNotFoundException` : Raised if the specified key could not be found.
- - `SectionNotFoundException` : Raised if the specified section could not be found.
- - `ValueError` : Raised if the key accessed is not a boolean.
- """
-
- section = section.upper()
- key = key.lower()
-
- val = self._access(key, section)
-
- if val.is_bool():
- return val.boolean
-
- raise ValueError, "\"%s\" is not a boolean. (%s)" % (key, val.value)
-
- def set (self, key, value = "", section = "MAIN"):
- """
- Sets a new value of a given key in a section.
-
- :Parameters:
-
- key : string
- the key
- value : string
- the new value
- section : string
- the section
-
- :Exceptions:
-
- - `KeyNotFoundException` : Raised if the specified key could not be found.
- - `SectionNotFoundException` : Raised if the specified section could not be found.
- """
-
- section = section.upper()
- key = key.lower()
-
- self._access(key, section).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).
-
- :Parameters:
-
- key : string
- the key
- value : boolean
- the new value
- section : string
- the section
-
- :Exceptions:
-
- - `KeyNotFoundException` : Raised if the specified key could not be found.
- - `SectionNotFoundException` : Raised if the specified section could not be found.
- - `ValueError` : if the old/new value is not a boolean
- """
-
- section = section.upper()
- key = key.lower()
-
- if not isinstance(value, bool):
- raise ValueError, "Passed value must be a boolean."
-
- val = self._access(key, section)
- 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 add_section (self, section, comment = None, with_blankline = True):
- """
- Adds a section to a the current configuration. If this section already exists, it does nothing.
-
- :Parameters:
-
- comment : string
- An additional comment to place above this section. '\\n' in the comment is interpreted correctly.
-
- with_blankline : boolean
- Add an additional blank line above the section.
- """
- section = section.upper()
-
- if section in self.vars:
- return
-
- if with_blankline and len(self.cache) > 0:
- self.cache.append("\n")
-
- if comment:
- if isinstance(comment, basestring):
- comment = comment.split("\n")
-
- # add newlines to comment at the beginning and the end
- comment.insert(0, "")
- comment.append("")
-
- for c in comment:
- self.cache.append("# %s\n" % c)
-
- self.vars[section] = {}
- self.sections[section] = len(self.cache)
- self.cache.append("[%s]\n" % section)
-
- def add (self, key, value, section = "MAIN", comment = None, with_blankline = True):
- """
- Adds a key to the specified section. If the key already exists, it acts the same as `set`.
-
- :Parameters:
-
- key : string
- The key to add.
- section : string
- The section where to add the key to.
- comment : string
- An additional comment for the key. '\\n' is correctly handled.
- with_blankline : boolean
- Add an additional blank line in front of the value.
-
- :raises SectionNotFoundException: if the section specified was not found
- """
-
- section = section.upper()
- key = key.lower()
-
- try:
- if key in self.vars[section]:
- return self.set(key, value, section)
- except KeyError:
- raise SectionNotFoundException("Section '%s' not found in file '%s'." % (section, self.file))
-
- self.write()
-
- # find line# to add
- if self.vars[section]:
- mline = max((x.line for x in self.vars[section].itervalues())) + 1
- else: # no value inside the section at the moment
- mline = self.sections[section] + 1
-
- if with_blankline and mline > 0:
- self.cache.insert(mline, "\n")
- mline += 1
-
- if comment:
- if isinstance(comment, basestring):
- comment = comment.split("\n")
-
- for c in comment:
- self.cache.insert(mline, "; %s\n" % c)
- mline += 1
-
- self.cache.insert(mline, "%s = %s\n" % (key, value))
-
- self.write()
-
- def write (self):
- """
- Writes the configuration file.
- """
-
- if not self.cache:
- return
-
- with self.writelock:
- for sec in self.vars:
- for val in self.vars[sec].itervalues():
- 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 value (\n in normal cases)
-
- if not val.old and part1.endswith("\n"): # empty original value
- part1 = part1[:-1] # strip \n
- part3 = part3 + "\n"
-
- self.cache[val.line] = part1 + part2 + part3
-
- # write
- with open(self.file, "w") as f:
- f.writelines(self.cache)
-
- # reload
- self.__initialize()
- self.parse()
+ """
+ The parser class.
+
+ :CVariables:
+
+ true_false : string -> string
+ A mapping from the truth values to their opposits.
+
+ :IVariables:
+
+ file : string
+ the file to scan
+ cache : string[]
+ caches the content of the file
+ vars : string -> (string -> `Value`)
+ the found options grouped by section
+ pos : int -> (int, int)
+ the positions of the values grouped by lineno
+ """
+
+ # 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.writelock = Lock()
+ self.__initialize()
+
+ def __initialize (self):
+ """Private method which initializes our dictionaries."""
+
+ self.vars = {"MAIN": {}}
+ self.cache = [] # file cache
+ self.pos = {} # stores the positions of the matches
+ self.sections = {"MAIN" : -1} # the line with the section header
+
+ def _invert (self, val):
+ """
+ Invertes a given boolean.
+
+ :param val: value to invert
+ :type val: string
+ :returns: inverted value
+ :rtype: string
+
+ :see: `true_false`
+ """
+
+ return self.true_false[val.lower()]
+
+ def parse (self):
+ """
+ Parses the file.
+ """
+
+ # read into cache
+ with open(self.file, "r") as f:
+ self.cache = f.readlines()
+
+ # 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("name").upper()
+ self.sections[sec] = count
+ if sec != section:
+ self.vars[sec] = {}
+ section = sec
+ continue
+
+ # look for an expression
+ match = EXPRESSION.search(line)
+ if match:
+ val = match.group("value")
+
+ # find the boolean value
+ bool = None
+ if TRUE.match(val):
+ bool = True
+ elif FALSE.match(val):
+ bool = False
+
+ # insert
+ key = match.group("key").lower()
+ self.vars[section][key] = Value(val, count, bool = bool)
+ self.pos[count] = match.span("value")
+ else: # neither comment nor empty nor expression nor section => error
+ error(_("Unrecognized line in configuration: %s"), line)
+
+ def _access (self, key, section):
+ """
+ Private method for accessing the saved variables.
+
+ :Parameters:
+
+ key : string
+ the key
+ section : string
+ the section
+
+ :returns: the value wanted
+ :rtype: `Value`
+
+ :Exceptions:
+
+ - `KeyNotFoundException` : Raised if the specified key could not be found.
+ - `SectionNotFoundException` : Raised if the specified section could not be found.
+ """
+
+ try:
+ sectiondict = self.vars[section]
+ except KeyError:
+ raise SectionNotFoundException("Section '%s' not found in file '%s'." % (section, self.file))
+
+ try:
+ return sectiondict[key]
+ except KeyError:
+ raise KeyNotFoundException("Key '%s' not found in section '%s' in file '%s'." % (key, section, self.file))
+
+ def get (self, key, section = "MAIN"):
+ """
+ Returns the value of a given key in a section.
+
+ :Parameters:
+
+ key : string
+ the key
+ section : string
+ the section
+
+ :returns: value
+ :rtype: string
+
+ :Exceptions:
+
+ - `KeyNotFoundException` : Raised if the specified key could not be found.
+ - `SectionNotFoundException` : Raised if the specified section could not be found.
+ """
+
+ section = section.upper()
+ key = key.lower()
+ return self._access(key, section).value
+
+ def get_boolean (self, key, section = "MAIN"):
+ """
+ Returns the boolean value of a given key in a section.
+
+ :Parameters:
+
+ key : string
+ the key
+ section : string
+ the section
+
+ :returns: value
+ :rtype: boolean
+
+ :Exceptions:
+
+ - `KeyNotFoundException` : Raised if the specified key could not be found.
+ - `SectionNotFoundException` : Raised if the specified section could not be found.
+ - `ValueError` : Raised if the key accessed is not a boolean.
+ """
+
+ section = section.upper()
+ key = key.lower()
+
+ val = self._access(key, section)
+
+ if val.is_bool():
+ return val.boolean
+
+ raise ValueError, "\"%s\" is not a boolean. (%s)" % (key, val.value)
+
+ def set (self, key, value = "", section = "MAIN"):
+ """
+ Sets a new value of a given key in a section.
+
+ :Parameters:
+
+ key : string
+ the key
+ value : string
+ the new value
+ section : string
+ the section
+
+ :Exceptions:
+
+ - `KeyNotFoundException` : Raised if the specified key could not be found.
+ - `SectionNotFoundException` : Raised if the specified section could not be found.
+ """
+
+ section = section.upper()
+ key = key.lower()
+
+ self._access(key, section).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).
+
+ :Parameters:
+
+ key : string
+ the key
+ value : boolean
+ the new value
+ section : string
+ the section
+
+ :Exceptions:
+
+ - `KeyNotFoundException` : Raised if the specified key could not be found.
+ - `SectionNotFoundException` : Raised if the specified section could not be found.
+ - `ValueError` : if the old/new value is not a boolean
+ """
+
+ section = section.upper()
+ key = key.lower()
+
+ if not isinstance(value, bool):
+ raise ValueError, "Passed value must be a boolean."
+
+ val = self._access(key, section)
+ 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 add_section (self, section, comment = None, with_blankline = True):
+ """
+ Adds a section to a the current configuration. If this section already exists, it does nothing.
+
+ :Parameters:
+
+ comment : string
+ An additional comment to place above this section. '\\n' in the comment is interpreted correctly.
+
+ with_blankline : boolean
+ Add an additional blank line above the section.
+ """
+ section = section.upper()
+
+ if section in self.vars:
+ return
+
+ if with_blankline and len(self.cache) > 0:
+ self.cache.append("\n")
+
+ if comment:
+ if isinstance(comment, basestring):
+ comment = comment.split("\n")
+
+ # add newlines to comment at the beginning and the end
+ comment.insert(0, "")
+ comment.append("")
+
+ for c in comment:
+ self.cache.append("# %s\n" % c)
+
+ self.vars[section] = {}
+ self.sections[section] = len(self.cache)
+ self.cache.append("[%s]\n" % section)
+
+ def add (self, key, value, section = "MAIN", comment = None, with_blankline = True):
+ """
+ Adds a key to the specified section. If the key already exists, it acts the same as `set`.
+
+ :Parameters:
+
+ key : string
+ The key to add.
+ section : string
+ The section where to add the key to.
+ comment : string
+ An additional comment for the key. '\\n' is correctly handled.
+ with_blankline : boolean
+ Add an additional blank line in front of the value.
+
+ :raises SectionNotFoundException: if the section specified was not found
+ """
+
+ section = section.upper()
+ key = key.lower()
+
+ try:
+ if key in self.vars[section]:
+ return self.set(key, value, section)
+ except KeyError:
+ raise SectionNotFoundException("Section '%s' not found in file '%s'." % (section, self.file))
+
+ self.write()
+
+ # find line# to add
+ if self.vars[section]:
+ mline = max((x.line for x in self.vars[section].itervalues())) + 1
+ else: # no value inside the section at the moment
+ mline = self.sections[section] + 1
+
+ if with_blankline and mline > 0:
+ self.cache.insert(mline, "\n")
+ mline += 1
+
+ if comment:
+ if isinstance(comment, basestring):
+ comment = comment.split("\n")
+
+ for c in comment:
+ self.cache.insert(mline, "; %s\n" % c)
+ mline += 1
+
+ self.cache.insert(mline, "%s = %s\n" % (key, value))
+
+ self.write()
+
+ def write (self):
+ """
+ Writes the configuration file.
+ """
+
+ if not self.cache:
+ return
+
+ with self.writelock:
+ for sec in self.vars:
+ for val in self.vars[sec].itervalues():
+ 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 value (\n in normal cases)
+
+ if not val.old and part1.endswith("\n"): # empty original value
+ part1 = part1[:-1] # strip \n
+ part3 = part3 + "\n"
+
+ self.cache[val.line] = part1 + part2 + part3
+
+ # write
+ with open(self.file, "w") as f:
+ f.writelines(self.cache)
+
+ # reload
+ self.__initialize()
+ self.parse()
diff --git a/portato/dependency.py b/portato/dependency.py
index cce7360..ef06e41 100644
--- a/portato/dependency.py
+++ b/portato/dependency.py
@@ -23,176 +23,176 @@ from .backend import system
class Dependency (object):
- """
- A simple dependency as it also is noted inside ebuilds.
+ """
+ A simple dependency as it also is noted inside ebuilds.
- :IVariables:
+ :IVariables:
- dep : string
- The dependency string. It is immutable.
+ dep : string
+ The dependency string. It is immutable.
- satisfied : boolean
- Is this dependency satisfied?
- """
+ satisfied : boolean
+ Is this dependency satisfied?
+ """
- def __init__ (self, dep):
- """
- Creates a dependency out of a dep string.
+ def __init__ (self, dep):
+ """
+ Creates a dependency out of a dep string.
- :param dep: dependency string
- :type dep: string
- """
- self._dep = dep
+ :param dep: dependency string
+ :type dep: string
+ """
+ self._dep = dep
- def is_satisfied (self):
- """
- Checks if this dependency is satisfied.
+ def is_satisfied (self):
+ """
+ Checks if this dependency is satisfied.
- :rtype: boolean
- """
- return system.find_best_match(self.dep, only_cpv = True, only_installed = True) is not None
+ :rtype: boolean
+ """
+ return system.find_best_match(self.dep, only_cpv = True, only_installed = True) is not None
- def __cmp__ (self, b):
- return cmp(self.dep, b.dep)
+ def __cmp__ (self, b):
+ return cmp(self.dep, b.dep)
- def __hash__ (self):
- return hash(self.dep)
+ def __hash__ (self):
+ return hash(self.dep)
- def __str__ (self):
- return "<Dependency '%s'>" % self.dep
+ def __str__ (self):
+ return "<Dependency '%s'>" % self.dep
- __repr__ = __str__
+ __repr__ = __str__
- @property
- def dep (self):
- return self._dep
+ @property
+ def dep (self):
+ return self._dep
- satisfied = property(is_satisfied)
+ satisfied = property(is_satisfied)
class OrDependency (Dependency):
- """
- Dependency representing an "or".
-
- :note: Order is important. ``|| ( a b )`` != ``|| ( b a )``
-
- :IVariables:
-
- dep : tuple(`Dependency`,...)
- The dependencies. The tuple and the dependencies are immutable.
- """
-
- def __init__ (self, deps):
- """
- Creates an or-dependency out of a list (or tuple) of deps.
-
- :param deps: The or'ed dependencies.
- :type deps: iter<string>
- """
-
- _dep = []
- for dep in deps:
- if not hasattr(dep, "__iter__"):
- assert not dep.endswith("?")
- _dep.append(Dependency(dep))
- else:
- _dep.append(AllOfDependency(dep))
-
- self._dep = tuple(_dep)
-
- def __str__ (self):
- return "<|| %s>" % str(self.dep)
-
- __repr__ = __str__
+ """
+ Dependency representing an "or".
+
+ :note: Order is important. ``|| ( a b )`` != ``|| ( b a )``
+
+ :IVariables:
+
+ dep : tuple(`Dependency`,...)
+ The dependencies. The tuple and the dependencies are immutable.
+ """
+
+ def __init__ (self, deps):
+ """
+ Creates an or-dependency out of a list (or tuple) of deps.
+
+ :param deps: The or'ed dependencies.
+ :type deps: iter<string>
+ """
+
+ _dep = []
+ for dep in deps:
+ if not hasattr(dep, "__iter__"):
+ assert not dep.endswith("?")
+ _dep.append(Dependency(dep))
+ else:
+ _dep.append(AllOfDependency(dep))
+
+ self._dep = tuple(_dep)
+
+ def __str__ (self):
+ return "<|| %s>" % str(self.dep)
+
+ __repr__ = __str__
class AllOfDependency (Dependency):
- """
- Dependency representing a set of packages inside "or".
- If the or is: ``|| (a ( b c ) )`` the `AllOfDependency` would be the ``( b c )``.
+ """
+ Dependency representing a set of packages inside "or".
+ If the or is: ``|| (a ( b c ) )`` the `AllOfDependency` would be the ``( b c )``.
- :IVariables:
+ :IVariables:
- dep : tuple(`Dependency`,...)
- The dependencies . The tuple and the deps are immutable.
- """
+ dep : tuple(`Dependency`,...)
+ The dependencies . The tuple and the deps are immutable.
+ """
- def __init__ (self, deps):
- """
- Creates an or-dependency out of a list (or tuple) of deps.
+ def __init__ (self, deps):
+ """
+ Creates an or-dependency out of a list (or tuple) of deps.
- :param deps: The dependencies.
- :type deps: iter<string>
- """
+ :param deps: The dependencies.
+ :type deps: iter<string>
+ """
- self._dep = tuple(Dependency(dep) for dep in deps)
+ self._dep = tuple(Dependency(dep) for dep in deps)
- def __str__ (self):
- return "<ALL %s>" % str(self.dep)
-
- __repr__ = __str__
+ def __str__ (self):
+ return "<ALL %s>" % str(self.dep)
+
+ __repr__ = __str__
class DependencyTree (object):
- """
- The DependencyTree shows all dependencies for a package and shows which useflags want which dependencies.
+ """
+ The DependencyTree shows all dependencies for a package and shows which useflags want which dependencies.
- :IVariables:
+ :IVariables:
- deps : set(`Dependency`)
- The list of dependencies which are not dependent on a useflag.
+ deps : set(`Dependency`)
+ The list of dependencies which are not dependent on a useflag.
- flags : string -> `DependencyTree`
- Holds the additional dependency trees per useflag.
- """
+ flags : string -> `DependencyTree`
+ Holds the additional dependency trees per useflag.
+ """
- def __init__ (self):
+ def __init__ (self):
- self.deps = set()
- self.flags = {}
+ self.deps = set()
+ self.flags = {}
- def add (self, dep, *moredeps):
- """
- Adds one or more normal dependencies to the tree.
+ def add (self, dep, *moredeps):
+ """
+ Adds one or more normal dependencies to the tree.
- :Parameters:
+ :Parameters:
- dep : string
- A dependency string.
+ dep : string
+ A dependency string.
- moredeps
- More parameters are allowed :)
- """
- self.deps.add(Dependency(dep))
+ moredeps
+ More parameters are allowed :)
+ """
+ self.deps.add(Dependency(dep))
- for dep in moredeps:
- self.deps.add(Dependency(dep))
+ for dep in moredeps:
+ self.deps.add(Dependency(dep))
- def add_or (self, orlist):
- """
- Adds a list of dependencies, which are or'ed.
+ def add_or (self, orlist):
+ """
+ Adds a list of dependencies, which are or'ed.
- :param orlist: the dependency list
- :type orlist: iter<string>
- """
- self.deps.add(OrDependency(orlist))
+ :param orlist: the dependency list
+ :type orlist: iter<string>
+ """
+ self.deps.add(OrDependency(orlist))
- def add_flag (self, flag):
- """
- Adds a new useflag to this tree.
- For convenience the newly created sub-tree is returned.
+ def add_flag (self, flag):
+ """
+ Adds a new useflag to this tree.
+ For convenience the newly created sub-tree is returned.
- :param flag: the new flag
- :rtype: `DependencyTree`
- """
- if not flag in self.flags:
- self.flags[flag] = DependencyTree()
+ :param flag: the new flag
+ :rtype: `DependencyTree`
+ """
+ if not flag in self.flags:
+ self.flags[flag] = DependencyTree()
- return self.get_flag_tree(flag)
+ return self.get_flag_tree(flag)
- def get_flag_tree (self, flag):
- """
- Returns the sub-tree of a specific tree.
+ def get_flag_tree (self, flag):
+ """
+ Returns the sub-tree of a specific tree.
- :raises KeyError: if the flag is not (yet) in this tree
- :rtype: `DependencyTree`
- """
- return self.flags[flag]
+ :raises KeyError: if the flag is not (yet) in this tree
+ :rtype: `DependencyTree`
+ """
+ return self.flags[flag]
diff --git a/portato/gui/__init__.py b/portato/gui/__init__.py
index 0df890c..5bbe4c8 100644
--- a/portato/gui/__init__.py
+++ b/portato/gui/__init__.py
@@ -16,13 +16,13 @@ from .. import get_listener
from .exception_handling import register_ex_handler
def run ():
- from .windows.splash import SplashScreen
- s = SplashScreen(_("Loading Backend"))
+ from .windows.splash import SplashScreen
+ s = SplashScreen(_("Loading Backend"))
- register_ex_handler()
- s.show()
-
- from .windows.main import MainWindow
- m = MainWindow(s)
- s.hide()
- m.main()
+ register_ex_handler()
+ s.show()
+
+ from .windows.main import MainWindow
+ m = MainWindow(s)
+ s.hide()
+ m.main()
diff --git a/portato/gui/dialogs.py b/portato/gui/dialogs.py
index bf7acc7..f178d2b 100644
--- a/portato/gui/dialogs.py
+++ b/portato/gui/dialogs.py
@@ -14,95 +14,95 @@ import gtk
from ..helper import error
def mail_failure_dialog(e):
- dialog = gtk.MessageDialog(None, gtk.DIALOG_MODAL, gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, _("Mail could not be sent"))
- dialog.format_secondary_text(_("The error was: %s") % e)
- ret = dialog.run()
- dialog.destroy()
- return ret
+ dialog = gtk.MessageDialog(None, gtk.DIALOG_MODAL, gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, _("Mail could not be sent"))
+ dialog.format_secondary_text(_("The error was: %s") % e)
+ ret = dialog.run()
+ dialog.destroy()
+ return ret
def queue_not_empty_dialog():
- dialog = gtk.MessageDialog(None, gtk.DIALOG_MODAL, gtk.MESSAGE_QUESTION, gtk.BUTTONS_NONE, _("Do you really want to quit?"))
- dialog.format_secondary_text(_("There are some packages in the emerge queue and/or an emerge process is running."))
- dialog.add_buttons(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_CLOSE, gtk.RESPONSE_CLOSE)
- ret = dialog.run()
- dialog.destroy()
- return ret
+ dialog = gtk.MessageDialog(None, gtk.DIALOG_MODAL, gtk.MESSAGE_QUESTION, gtk.BUTTONS_NONE, _("Do you really want to quit?"))
+ dialog.format_secondary_text(_("There are some packages in the emerge queue and/or an emerge process is running."))
+ dialog.add_buttons(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_CLOSE, gtk.RESPONSE_CLOSE)
+ ret = dialog.run()
+ dialog.destroy()
+ return ret
def io_ex_dialog (io_ex):
- string = io_ex.strerror
- if io_ex.filename:
- string = string+": "+io_ex.filename
-
- error(string)
- dialog = gtk.MessageDialog(None, gtk.DIALOG_MODAL, gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, string)
- ret = dialog.run()
- dialog.destroy()
- return ret
+ string = io_ex.strerror
+ if io_ex.filename:
+ string = string+": "+io_ex.filename
+
+ error(string)
+ dialog = gtk.MessageDialog(None, gtk.DIALOG_MODAL, gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, string)
+ ret = dialog.run()
+ dialog.destroy()
+ return ret
def blocked_dialog (blocked, blocks):
- dialog = gtk.MessageDialog(None, gtk.DIALOG_MODAL, gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, _("%(blocked)s is blocked by %(blocks)s.") % {"blocked":blocked, "blocks" : blocks})
- dialog.format_secondary_text(_("Please unmerge the blocking package."))
- ret = dialog.run()
- dialog.destroy()
- return ret
+ dialog = gtk.MessageDialog(None, gtk.DIALOG_MODAL, gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, _("%(blocked)s is blocked by %(blocks)s.") % {"blocked":blocked, "blocks" : blocks})
+ dialog.format_secondary_text(_("Please unmerge the blocking package."))
+ ret = dialog.run()
+ dialog.destroy()
+ return ret
def not_root_dialog ():
- errorMB = gtk.MessageDialog(None, gtk.DIALOG_MODAL, gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, _("You are not root."))
- ret = errorMB.run()
- errorMB.destroy()
- return ret
+ errorMB = gtk.MessageDialog(None, gtk.DIALOG_MODAL, gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, _("You are not root."))
+ ret = errorMB.run()
+ errorMB.destroy()
+ return ret
def unmask_dialog (cpv):
- dialog = gtk.MessageDialog(None, gtk.DIALOG_MODAL, gtk.MESSAGE_QUESTION, gtk.BUTTONS_YES_NO, _("%s seems to be masked.") % cpv )
- dialog.format_secondary_text(_("Do you want to unmask it and its dependencies?"))
- ret = dialog.run()
- dialog.destroy()
- return ret
+ dialog = gtk.MessageDialog(None, gtk.DIALOG_MODAL, gtk.MESSAGE_QUESTION, gtk.BUTTONS_YES_NO, _("%s seems to be masked.") % cpv )
+ dialog.format_secondary_text(_("Do you want to unmask it and its dependencies?"))
+ ret = dialog.run()
+ dialog.destroy()
+ return ret
def nothing_found_dialog ():
- dialog = gtk.MessageDialog(None, gtk.DIALOG_MODAL, gtk.MESSAGE_INFO, gtk.BUTTONS_OK, _("Package not found!"))
- ret = dialog.run()
- dialog.destroy()
- return ret
+ dialog = gtk.MessageDialog(None, gtk.DIALOG_MODAL, gtk.MESSAGE_INFO, gtk.BUTTONS_OK, _("Package not found!"))
+ ret = dialog.run()
+ dialog.destroy()
+ return ret
def changed_flags_dialog (what = "flags"):
- check = gtk.CheckButton(_("Do not show this dialog again."))
- hintMB = gtk.MessageDialog(None, gtk.DIALOG_MODAL, gtk.MESSAGE_INFO, gtk.BUTTONS_OK, _("Changed %s") % what)
- hintMB.format_secondary_text(_("Portato will write these changes into the appropriate files.\nPlease backup them if you think it is necessairy."))
- hintMB.vbox.add(check)
- hintMB.vbox.show_all()
- ret = hintMB.run()
- hintMB.destroy()
+ check = gtk.CheckButton(_("Do not show this dialog again."))
+ hintMB = gtk.MessageDialog(None, gtk.DIALOG_MODAL, gtk.MESSAGE_INFO, gtk.BUTTONS_OK, _("Changed %s") % what)
+ hintMB.format_secondary_text(_("Portato will write these changes into the appropriate files.\nPlease backup them if you think it is necessairy."))
+ hintMB.vbox.add(check)
+ hintMB.vbox.show_all()
+ ret = hintMB.run()
+ hintMB.destroy()
- return ret, check.get_active()
+ return ret, check.get_active()
def remove_deps_dialog ():
- infoMB = gtk.MessageDialog(None, gtk.DIALOG_MODAL, gtk.MESSAGE_INFO, gtk.BUTTONS_OK, _("You cannot remove dependencies. :)"))
- ret = infoMB.run()
- infoMB.destroy()
- return ret
+ infoMB = gtk.MessageDialog(None, gtk.DIALOG_MODAL, gtk.MESSAGE_INFO, gtk.BUTTONS_OK, _("You cannot remove dependencies. :)"))
+ ret = infoMB.run()
+ infoMB.destroy()
+ return ret
def remove_updates_dialog():
- askMB = gtk.MessageDialog(None, gtk.DIALOG_MODAL, gtk.MESSAGE_QUESTION, gtk.BUTTONS_YES_NO, _("This is the updates queue. You cannot remove single elements.\nDo you want to clear the whole queue instead?"))
- ret = askMB.run()
- askMB.destroy()
- return ret
+ askMB = gtk.MessageDialog(None, gtk.DIALOG_MODAL, gtk.MESSAGE_QUESTION, gtk.BUTTONS_YES_NO, _("This is the updates queue. You cannot remove single elements.\nDo you want to clear the whole queue instead?"))
+ ret = askMB.run()
+ askMB.destroy()
+ return ret
def remove_queue_dialog ():
- askMB = gtk.MessageDialog(None, gtk.DIALOG_MODAL, gtk.MESSAGE_QUESTION, gtk.BUTTONS_YES_NO, _("Do you really want to clear the whole queue?"))
- ret = askMB.run()
- askMB.destroy()
- return ret
+ askMB = gtk.MessageDialog(None, gtk.DIALOG_MODAL, gtk.MESSAGE_QUESTION, gtk.BUTTONS_YES_NO, _("Do you really want to clear the whole queue?"))
+ ret = askMB.run()
+ askMB.destroy()
+ return ret
def file_chooser_dialog (title, parent):
- fc = gtk.FileChooserDialog(title = title, parent = parent, action = gtk.FILE_CHOOSER_ACTION_SAVE, buttons = (gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT, gtk.STOCK_OK, gtk.RESPONSE_ACCEPT))
- fc.set_do_overwrite_confirmation(True)
- ret = fc.run()
+ fc = gtk.FileChooserDialog(title = title, parent = parent, action = gtk.FILE_CHOOSER_ACTION_SAVE, buttons = (gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT, gtk.STOCK_OK, gtk.RESPONSE_ACCEPT))
+ fc.set_do_overwrite_confirmation(True)
+ ret = fc.run()
- if ret == gtk.RESPONSE_ACCEPT:
- ret = fc.get_filename()
- else:
- ret = None
+ if ret == gtk.RESPONSE_ACCEPT:
+ ret = fc.get_filename()
+ else:
+ ret = None
- fc.destroy()
- return ret
+ fc.destroy()
+ return ret
diff --git a/portato/gui/exception_handling.py b/portato/gui/exception_handling.py
index dae95ed..df555de 100644
--- a/portato/gui/exception_handling.py
+++ b/portato/gui/exception_handling.py
@@ -24,108 +24,108 @@ from .windows.mailinfo import MailInfoWindow
from .utils import GtkThread
class UncaughtExceptionDialog(gtk.MessageDialog):
- """Original idea by Gustavo Carneiro - original code: http://www.daa.com.au/pipermail/pygtk/attachments/20030828/2d304204/gtkexcepthook.py."""
-
- def __init__(self, type, value, tb, thread = None):
-
- super(UncaughtExceptionDialog,self).__init__(parent=None, flags=0, type=gtk.MESSAGE_WARNING, buttons=gtk.BUTTONS_NONE, message_format=_("A programming error has been detected during the execution of this program."))
- self.set_title(_("Bug Detected"))
- self.format_secondary_text(_("It probably isn't fatal, but should be reported to the developers nonetheless."))
-
- self.add_button(_("Show Details"), 1)
- self.add_button(_("Send..."), 3)
- self.add_button(gtk.STOCK_SAVE_AS, 2)
- self.add_button(gtk.STOCK_CLOSE, gtk.RESPONSE_CLOSE)
-
- # Details
- self.textview = gtk.TextView()
- self.textview.set_editable(False)
- self.textview.modify_font(pango.FontDescription("Monospace"))
-
- self.sw = gtk.ScrolledWindow();
- self.sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
- self.sw.add(self.textview)
-
- self.tbFrame = gtk.Frame()
- self.tbFrame.set_shadow_type(gtk.SHADOW_IN)
- self.tbFrame.add(self.sw)
- self.tbFrame.set_border_width(6)
-
- self.vbox.add(self.tbFrame)
-
- textbuffer = self.textview.get_buffer()
- self.text = get_trace(type, value, tb)
- if thread:
- self.text = _("Exception in thread \"%(thread)s\":\n%(trace)s") % {"thread": thread, "trace": self.text}
- textbuffer.set_text(self.text)
- self.textview.set_size_request(gtk.gdk.screen_width()/2, gtk.gdk.screen_height()/3)
-
- self.details = self.tbFrame
- self.set_position(gtk.WIN_POS_CENTER)
- self.set_gravity(gtk.gdk.GRAVITY_CENTER)
-
- def run (self):
- while True:
- resp = super(UncaughtExceptionDialog, self).run()
- if resp == 1:
- self.details.show_all()
- self.set_response_sensitive(1, False)
- elif resp == 2:
- debug("Want to save")
- file = file_chooser_dialog(_("Save traceback..."), self)
- if file:
- debug("Save to %s", file)
-
- try:
- with open(file, "w") as f:
- f.writelines(self.text)
- except IOError, e:
- io_ex_dialog(e)
-
- else:
- debug("Nothing to save")
- elif resp == 3:
- debug("Send bug per mail")
- self.destroy()
- MailInfoWindow(None, self.text)
- return
- else:
- break
- self.destroy()
+ """Original idea by Gustavo Carneiro - original code: http://www.daa.com.au/pipermail/pygtk/attachments/20030828/2d304204/gtkexcepthook.py."""
+
+ def __init__(self, type, value, tb, thread = None):
+
+ super(UncaughtExceptionDialog,self).__init__(parent=None, flags=0, type=gtk.MESSAGE_WARNING, buttons=gtk.BUTTONS_NONE, message_format=_("A programming error has been detected during the execution of this program."))
+ self.set_title(_("Bug Detected"))
+ self.format_secondary_text(_("It probably isn't fatal, but should be reported to the developers nonetheless."))
+
+ self.add_button(_("Show Details"), 1)
+ self.add_button(_("Send..."), 3)
+ self.add_button(gtk.STOCK_SAVE_AS, 2)
+ self.add_button(gtk.STOCK_CLOSE, gtk.RESPONSE_CLOSE)
+
+ # Details
+ self.textview = gtk.TextView()
+ self.textview.set_editable(False)
+ self.textview.modify_font(pango.FontDescription("Monospace"))
+
+ self.sw = gtk.ScrolledWindow();
+ self.sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+ self.sw.add(self.textview)
+
+ self.tbFrame = gtk.Frame()
+ self.tbFrame.set_shadow_type(gtk.SHADOW_IN)
+ self.tbFrame.add(self.sw)
+ self.tbFrame.set_border_width(6)
+
+ self.vbox.add(self.tbFrame)
+
+ textbuffer = self.textview.get_buffer()
+ self.text = get_trace(type, value, tb)
+ if thread:
+ self.text = _("Exception in thread \"%(thread)s\":\n%(trace)s") % {"thread": thread, "trace": self.text}
+ textbuffer.set_text(self.text)
+ self.textview.set_size_request(gtk.gdk.screen_width()/2, gtk.gdk.screen_height()/3)
+
+ self.details = self.tbFrame
+ self.set_position(gtk.WIN_POS_CENTER)
+ self.set_gravity(gtk.gdk.GRAVITY_CENTER)
+
+ def run (self):
+ while True:
+ resp = super(UncaughtExceptionDialog, self).run()
+ if resp == 1:
+ self.details.show_all()
+ self.set_response_sensitive(1, False)
+ elif resp == 2:
+ debug("Want to save")
+ file = file_chooser_dialog(_("Save traceback..."), self)
+ if file:
+ debug("Save to %s", file)
+
+ try:
+ with open(file, "w") as f:
+ f.writelines(self.text)
+ except IOError, e:
+ io_ex_dialog(e)
+
+ else:
+ debug("Nothing to save")
+ elif resp == 3:
+ debug("Send bug per mail")
+ self.destroy()
+ MailInfoWindow(None, self.text)
+ return
+ else:
+ break
+ self.destroy()
def convert (version):
- """Converts a version given as int-tuple to a normal version string."""
- return ".".join(map(str, version))
+ """Converts a version given as int-tuple to a normal version string."""
+ return ".".join(map(str, version))
def get_version_infos():
- from ..constants import VERSION
- from ..backend import system
-
- return "\n".join((
- "Portato version: %s" % VERSION,
- "Python version: %s" % sys.version,
- "Used backend: %s" % system.get_version(),
- "pygtk: %s (using GTK+: %s)" % (convert(gtk.pygtk_version), convert(gtk.gtk_version)),
- "pygobject: %s (using GLib: %s)" % (convert(gobject.pygobject_version), convert(gobject.glib_version))))
+ from ..constants import VERSION
+ from ..backend import system
+
+ return "\n".join((
+ "Portato version: %s" % VERSION,
+ "Python version: %s" % sys.version,
+ "Used backend: %s" % system.get_version(),
+ "pygtk: %s (using GTK+: %s)" % (convert(gtk.pygtk_version), convert(gtk.gtk_version)),
+ "pygobject: %s (using GLib: %s)" % (convert(gobject.pygobject_version), convert(gobject.glib_version))))
def get_trace(type, value, tb):
- trace = StringIO()
- traceback.print_exception(type, value, tb, None, trace)
- traceStr = trace.getvalue()
- trace.close()
- return traceStr + "\n" + get_version_infos()
-
+ trace = StringIO()
+ traceback.print_exception(type, value, tb, None, trace)
+ traceStr = trace.getvalue()
+ trace.close()
+ return traceStr + "\n" + get_version_infos()
+
def register_ex_handler():
-
- def handler(type, val, tb, thread = None):
- def run_dialog():
- UncaughtExceptionDialog(type, val, tb, thread).run()
-
- if thread:
- error(_("Exception in thread \"%(thread)s\":\n%(trace)s"), {"thread": thread, "trace": get_trace(type, val, tb)})
- else:
- error(_("Exception:\n%s"), get_trace(type, val, tb))
-
- gobject.idle_add(run_dialog)
-
- sys.excepthook = handler
+
+ def handler(type, val, tb, thread = None):
+ def run_dialog():
+ UncaughtExceptionDialog(type, val, tb, thread).run()
+
+ if thread:
+ error(_("Exception in thread \"%(thread)s\":\n%(trace)s"), {"thread": thread, "trace": get_trace(type, val, tb)})
+ else:
+ error(_("Exception:\n%s"), get_trace(type, val, tb))
+
+ gobject.idle_add(run_dialog)
+
+ sys.excepthook = handler
diff --git a/portato/gui/queue.py b/portato/gui/queue.py
index b5fb736..5ff600f 100644
--- a/portato/gui/queue.py
+++ b/portato/gui/queue.py
@@ -31,620 +31,620 @@ from .updater import Updater
from .wrapper import GtkConsole, GtkTree
class EmergeQueue:
- """This class manages the emerge queue."""
-
- def __init__ (self, tree = None, console = None, db = None, title_update = None, threadClass = threading.Thread):
- """Constructor.
-
- @param tree: Tree to append all the items to.
- @type tree: GtkTree
- @param console: Output is shown here.
- @type console: GtkConsole
- @param db: A database instance.
- @type db: Database
- @param title_update: A function, which will be called whenever there is a title update.
- @type title_update: function(string)"""
-
- # the different queues
- self.mergequeue = [] # for emerge
- self.unmergequeue = [] # for emerge -C
- self.oneshotmerge = [] # for emerge --oneshot
-
- # the emerge process
- self.process = None
- self.threadQueue = WaitingQueue(threadClass = threadClass)
- self.pty = None
-
- # dictionaries with data about the packages in the queue
- self.iters = {"install" : {}, "uninstall" : {}, "update" : {}} # iterator in the tree
- self.deps = {"install" : {}, "update" : {}} # all the deps of the package
- self.blocks = {"install" : OrderedDict(), "update" : OrderedDict()}
-
- # member vars
- self.tree = tree
- if self.tree and not isinstance(self.tree, GtkTree): raise TypeError, "tree passed is not a GtkTree-object"
-
- self.console = console
- if self.console and not isinstance(self.console, GtkConsole): raise TypeError, "console passed is not a GtkConsole-object"
-
- self.db = db
- self.title_update = title_update
- self.threadClass = threadClass
-
- if self.console:
- self.pty = pty.openpty()
- self.console.set_pty(self.pty[0])
-
- def _get_pkg_from_cpv (self, cpv, unmask = False):
- """Gets a L{backend.Package}-object from a cpv.
-
- @param cpv: the cpv to get the package for
- @type cpv: string (cpv)
- @param unmask: if True we will look for masked packages if we cannot find unmasked ones
- @type unmask: boolean
- @return: created package
- @rtype: backend.Package
-
- @raises backend.PackageNotFoundException: If no package could be found - normally it is existing but masked."""
-
- # for the beginning: let us create a package object - but it is not guaranteed, that it actually exists in portage
- pkg = system.new_package(cpv)
- masked = not (pkg.is_masked() or pkg.is_testing(use_keywords=True)) # we are setting this to True in case we have unmasked it already, but portage does not know this
-
- # and now try to find it in portage
- pkg = system.find_packages("="+cpv, masked = masked)
-
- if pkg: # gotcha
- pkg = pkg[0]
-
- elif unmask: # no pkg returned, but we are allowed to unmask it
- pkg = system.find_packages("="+cpv, masked = True)
-
- if not pkg:
- raise backend.PackageNotFoundException(cpv) # also not found
- else:
- pkg = pkg[0]
-
- if pkg.is_testing(use_keywords = True):
- pkg.set_testing(True)
- if pkg.is_masked():
- pkg.set_masked()
-
- else: # no pkg returned - and we are not allowed to unmask
- raise backend.PackageNotFoundException(cpv)
-
- return pkg
-
- def update_tree (self, it, cpv, unmask = False, oneshot = False, type = "install"):
- """This updates the tree recursivly, or? Isn't it? Bjorn!
-
- @param it: iterator where to append
- @type it: Iterator
- @param cpv: The package to append.
- @type cpv: string (cat/pkg-ver)
- @param unmask: True if we are allowed to look for masked packages
- @type unmask: boolean
- @param oneshot: True if we want to emerge is oneshot
- @type oneshot: boolean
- @param type: the type of the updating
- @type type: string
-
- @raises backend.BlockedException: When occured during dependency-calculation.
- @raises backend.PackageNotFoundException: If no package could be found - normally it is existing but masked."""
-
- if cpv in self.deps[type]:
- return # in list already and therefore it's already in the tree too
-
- # try to find an already installed instance
- update = False
- downgrade = False
- uVersion = None
- changedUse = []
- try:
- pkg = self._get_pkg_from_cpv(cpv, unmask)
- if not pkg.is_installed():
- old = system.find_packages(pkg.get_slot_cp(), system.SET_INSTALLED)
- if old:
- old = old[0] # assume we have only one there
- cmp = pkg.compare_version(old)
- if cmp > 0:
- update = True
- elif cmp < 0:
- downgrade = True
-
- uVersion = old.get_version()
-
- old_iuse = set(old.get_iuse_flags())
- new_iuse = set(pkg.get_iuse_flags())
-
- for i in old_iuse.difference(new_iuse):
- changedUse.append("-"+i)
-
- for i in new_iuse.difference(old_iuse):
- changedUse.append("+"+i)
- else:
- old_iuse = set(pkg.get_iuse_flags(installed = True))
- new_iuse = set(pkg.get_iuse_flags(installed = False))
-
- for i in old_iuse.difference(new_iuse):
- changedUse.append("-"+i)
-
- for i in new_iuse.difference(old_iuse):
- changedUse.append("+"+i)
-
- except backend.PackageNotFoundException, e: # package not found / package is masked -> delete current tree and re-raise the exception
- if type == "update": # remove complete tree
- self.remove_with_children(self.tree.first_iter(it), removeNewFlags = False)
-
- elif type == "install": # remove only the intentionally added package
- top = self.tree.first_iter(it)
- parent = self.tree.parent_iter(it)
-
- if parent:
- while not self.tree.iter_equal(top, parent):
- parent = self.tree.parent_iter(parent)
- it = self.tree.parent_iter(it)
-
- self.remove_with_children(it, removeNewFlags = False)
-
- if not self.tree.iter_has_children(top): # remove completely if nothing left
- self.remove(top)
- raise
-
- # add iter
- subIt = self.tree.append(it, self.tree.build_append_value(cpv, oneshot = oneshot, update = update, downgrade = downgrade, version = uVersion, useChange = changedUse))
- self.iters[type][cpv] = subIt
-
- # get dependencies
- deps = pkg.get_dep_packages(return_blocks = True)
- self.deps[type][cpv] = deps
-
- for d in deps:
- if d[0] == "!": # block
- dep = d[1:]
- if not dep in self.blocks[type]:
- self.blocks[type][dep] = set()
-
- self.blocks[type][dep].add(cpv)
- else: # recursive call
- self.update_tree(subIt, d, unmask, type = type)
-
- def append (self, cpv, type = "install", update = False, forceUpdate = False, unmask = False, oneshot = False):
- """Appends a cpv either to the merge queue or to the unmerge-queue.
- Also updates the tree-view.
-
- @param cpv: Package to add
- @type cpv: string (cat/pkg-ver)
- @param type: The type of this append process. Possible values are "install", "uninstall", "update".
- @type type: string
- @param update: Set to True if a package is going to be updated (e.g. if the use-flags changed).
- @type update: boolean
- @param forceUpdate: Set to True if the update should be forced.
- @type forceUpdate: boolean
- @param unmask: True if we are allowed to look for masked packages
- @type unmask: boolean
- @param oneshot: True if this package should not be added to the world-file.
- @type oneshot: boolean
-
- @raises portato.backend.PackageNotFoundException: if trying to add a package which does not exist"""
-
- if type in ("install", "update"): # emerge
- if update:
- pkg = self._get_pkg_from_cpv(cpv, unmask)
- deps = pkg.get_dep_packages(return_blocks = True)
-
- if not forceUpdate and cpv in self.deps[type] and deps == self.deps[type][cpv]:
- return # nothing changed - return
- else:
- hasBeenInQueue = (cpv in self.mergequeue or cpv in self.oneshotmerge)
- parentIt = self.tree.parent_iter(self.iters[type][cpv])
-
- # delete it out of the tree - but NOT the changed flags
- self.remove_with_children(self.iters[type][cpv], removeNewFlags = False)
-
- if hasBeenInQueue: # package has been in queue before
- self._queue_append(cpv, oneshot)
-
- self.update_tree(parentIt, cpv, unmask, oneshot = oneshot, type = type)
- else: # not update
- if type == "install":
- if self.tree:
- self.update_tree(self.tree.get_emerge_it(), cpv, unmask, type = type, oneshot = oneshot)
- self._queue_append(cpv, oneshot)
- elif type == "update" and self.tree:
- self.update_tree(self.tree.get_update_it(), cpv, unmask, type = type, oneshot = oneshot)
-
- # handle blocks
- if self.blocks[type]:
- # check whether anything blocks something in the queue
- for block in self.blocks[type]:
- for c in self.iters[type]:
- if system.cpv_matches(c, block):
- blocked = ", ".join(self.blocks[type][block])
- warning("'%s' is blocked by: %s", c, blocked)
- self.remove_with_children(self.iters[type][c], False)
- raise BlockedException(c, blocked)
-
- #
- # check whether we block a version that we are going to replace nevertheless
- #
-
- # get the blocks that block an installed package
- inst = []
- for block in self.blocks[type]:
- pkgs = system.find_packages(block, system.SET_INSTALLED)
- if pkgs:
- inst.append((pkgs, block))
-
- # the slot-cp's of the packages in the queue
- slots = {}
- for c in self.iters[type]:
- slots[system.new_package(c).get_slot_cp()] = cpv
-
- # check the installed blocks against the slot-cp's
- for pkgs, block in inst[:]:
- done = False
- for pkg in pkgs:
- done = False
- if pkg.get_slot_cp() in slots:
- debug("Block '%s' can be ignored, because the blocking package is going to be replaced with '%s'.", block, slots[pkg.get_slot_cp()])
- done = True
- if done:
- inst.remove((pkgs,block))
-
- if inst: # there is still something left to block
- for pkgs, block in inst:
- blocked = ", ".join(self.blocks[type][block])
- warning("'%s' blocks the installation of: %s", pkgs[0].get_cpv(), blocked)
- self.remove_with_children(self.iters[type][cpv], False)
- raise BlockedException(blocked, pkgs[0].get_cpv())
-
- else: # unmerge
- self.unmergequeue.append(cpv)
- if self.tree: # update tree
- self.iters["uninstall"].update({cpv: self.tree.append(self.tree.get_unmerge_it(), self.tree.build_append_value(cpv))})
-
- def _queue_append (self, cpv, oneshot = False):
- """Convenience function appending a cpv either to self.mergequeue or to self.oneshotmerge.
-
- @param cpv: cpv to add
- @type cpv: string (cpv)
- @param oneshot: True if this package should not be added to the world-file.
- @type oneshot: boolean"""
-
- if not oneshot:
- if cpv not in self.mergequeue:
- self.mergequeue.append(cpv)
- else:
- if cpv not in self.oneshotmerge:
- self.oneshotmerge.append(cpv)
-
- def doEmerge (self, options, packages, it, *args, **kwargs):
- top = None
- if self.tree and it:
- for v in it.itervalues():
- self.tree.set_in_progress(v)
- top = self.tree.first_iter(v)
- break
-
- self.threadQueue.put(self.__emerge, options, packages, it, top, *args, **kwargs)
-
- def __emerge (self, options, packages, it, top, command = None):
- """Calls emerge and updates the terminal.
-
- @param options: options to send to emerge
- @type options: string[]
- @param packages: packages to emerge
- @type packages: string[]
- @param it: Iterators which point to these entries whose children will be removed after completion.
- @type it: dict(string -> Iterator)
- @param top: The top iterator
- @type top: Iterator
- @param command: the command to execute - default is "/usr/bin/python /usr/bin/emerge"
- @type command: string[]"""
-
- @plugin.hook("emerge", packages = packages, command = command, console = self.console, title_update = self.title_update)
- def sub_emerge(command):
- if command is None:
- command = system.get_merge_command()
-
- # open tty
- if self.console:
- self.console.reset()
-
- def pre ():
- os.setsid() # new session
- if self.console:
- import fcntl, termios
- fcntl.ioctl(self.pty[1], termios.TIOCSCTTY, 0) # set pty-slave as session tty
- os.dup2(self.pty[1], 0)
- os.dup2(self.pty[1], 1)
- os.dup2(self.pty[1], 2)
-
- # get all categories that are being touched during the emerge process
- cats = set(map(lambda x: x.split("/")[0], it.iterkeys()))
-
- # start emerge
- self.process = Popen(command+options+packages, shell = False, env = system.get_environment(), preexec_fn = pre)
-
- # remove packages from queue
- if self.tree and it and not self.tree.is_in_unmerge(top):
- self.up = Updater(self, it, self.threadClass)
- else:
- self.up = None
-
- # update title
- if self.console:
- old_title = self.console.get_window_title()
- while self.process and self.process.poll() is None:
- if self.title_update :
- title = self.console.get_window_title()
- if title != old_title:
- self.title_update(title)
- old_title = title
- time.sleep(0.5)
-
- if self.up:
- self.up.stop()
- if it:
- self.tree.set_in_progress(top, False)
- else:
- self.remove(top)
- elif self.tree and it:
- self.remove_with_children(top)
-
- if self.title_update: self.title_update(None)
-
- if self.process is None: # someone resetted this
- self.threadQueue.next()
- return
- else:
- ret = self.process.returncode
- self.process = None
- self.threadQueue.next()
-
- @plugin.hook("after_emerge", packages = packages, retcode = ret)
- def update_packages():
- if self.db:
- for cat in cats:
- self.db.reload(cat)
- debug("Category %s refreshed", cat)
-
- update_packages()
-
- sub_emerge(command)
-
- def emerge (self, force = False, options = None):
- """Emerges everything in the merge-queue.
-
- @param force: If False, '-pv' is send to emerge. Default: False.
- @type force: boolean
- @param options: Additional options to send to the emerge command
- @type options: string[]"""
-
- def prepare(queue):
- """Prepares the list of iterators and the list of packages."""
- list = []
- its = {}
- for k in queue:
- list += ["="+k]
- if self.tree:
- its.update({k : self.iters["install"][k]})
-
- return list, its
-
- if self.tree:
- ownit = self.iters["install"]
- else:
- ownit = {}
-
- # oneshot-queue
- if self.oneshotmerge:
- # prepare package-list for oneshot
- list, its = prepare(self.oneshotmerge)
- if not self.mergequeue :# the other one does not exist - remove completely
- its = ownit
-
- s = system.get_oneshot_option()
- if not force: s += system.get_pretend_option()
- if options is not None: s += options
-
- self.doEmerge(s, list, its, caller = self.emerge)
-
- # normal queue
- if self.mergequeue:
- # prepare package-list
- list, its = prepare(self.mergequeue)
- if not self.oneshotmerge: # the other one does not exist - remove completely
- its = ownit
-
- s = []
- if not force: s = system.get_pretend_option()
- if options is not None: s += options
-
- self.doEmerge(s, list, its, caller = self.emerge)
-
- def unmerge (self, force = False, options = None):
- """Unmerges everything in the umerge-queue.
-
- @param force: If False, '-pv' is send to emerge. Default: False.
- @type force: boolean
- @param options: Additional options to send to the emerge command
- @type options: string[]"""
-
- if len(self.unmergequeue) == 0: return # nothing in queue
-
- list = self.unmergequeue[:] # copy the unmerge-queue
-
- # set options
- s = system.get_unmerge_option()
- if not force: s += system.get_pretend_option()
- if options is not None: s += options
-
- if self.tree:
- it = self.iters["uninstall"]
- else:
- it = {}
-
- self.doEmerge(s,list, it, caller = self.unmerge)
-
- def update_world(self, sets = ("world",), force = False, newuse = False, deep = False, options = None):
- """Does an update world. newuse and deep are the arguments handed to emerge.
-
- @param sets: The sets to update.
- @type sets: string[]
- @param force: If False, '-pv' is send to emerge. Default: False.
- @type force: boolean
- @param newuse: If True, append newuse options
- @type newuse: boolean
- @param deep: If True, append deep options
- @type deep: boolean
- @param options: Additional options to send to the emerge command
- @type options: string[]"""
-
- opts = system.get_update_option()
-
- if newuse: opts += system.get_newuse_option()
- if deep: opts += system.get_deep_option()
- if not force: opts += system.get_pretend_option()
- if options is not None: opts += options
-
- if self.tree:
- it = self.iters["update"]
- else:
- it = {}
-
- self.doEmerge(opts, list(sets), it, caller = self.update_world)
-
- def sync (self, command = None):
- """Calls "emerge --sync".
-
- @param command: command to execute to sync. If None "emerge --sync" is taken.
- @type command: string[]"""
-
- if command is None:
- command = system.get_sync_command()
-
- try:
- while True:
- idx = command.index("&&")
- self.doEmerge([],[],{}, command[:idx], caller = self.sync)
- command = command[idx+1:]
- except ValueError: # no && in command
- self.doEmerge([],[],{}, command, caller = self.sync)
-
- def kill_emerge (self):
- """Kills the emerge process."""
- if self.process is not None:
- self.threadQueue.clear() # remove all pending emerge threads
- try:
- pgid = os.getpgid(self.process.pid)
- os.killpg(pgid, signal.SIGTERM)
- debug("Process should be terminated")
- if self.process.poll() is None:
- os.killpg(pgid, signal.SIGKILL)
- debug("Process should be killed")
- except AttributeError:
- debug("AttributeError occured ==> process not exisiting - ignore")
- except OSError:
- debug("OSError occured ==> process already stopped - ignore")
-
- self.process = None
-
- def stop_emerge (self):
- if self.process is not None:
- os.killpg(os.getpgid(self.process.pid), signal.SIGSTOP)
- debug("Process should be stopped")
-
- def continue_emerge (self):
- if self.process is not None:
- os.killpg(os.getpgid(self.process.pid), signal.SIGCONT)
- debug("Process should continue")
-
- def remove_with_children (self, it, removeNewFlags = True):
- """Convenience function which removes all children of an iterator and than the iterator itself.
-
- @param it: The iter which to remove.
- @type it: Iterator
- @param removeNewFlags: True if new flags should be removed; False otherwise. Default: True.
- @type removeNewFlags: boolean"""
-
- self.remove_children(it, removeNewFlags)
- self.remove(it, removeNewFlags)
-
- def remove_children (self, parentIt, removeNewFlags = True):
- """Removes all children of a given parent TreeIter recursivly.
-
- @param parentIt: The iter from which to remove all children.
- @type parentIt: Iterator
- @param removeNewFlags: True if new flags should be removed; False otherwise. Default: True.
- @type removeNewFlags: boolean"""
-
- childIt = self.tree.first_child_iter(parentIt)
-
- while childIt:
- if (self.tree.iter_has_children(childIt)): # recursive call
- self.remove_children(childIt, removeNewFlags)
- temp = childIt
- childIt = self.tree.next_iter(childIt)
- self.remove(temp, removeNewFlags)
-
- def remove (self, it, removeNewFlags = True):
- """Removes a specific item in the tree. This does not remove the top-entries.
-
- @param it: Iterator which points to the entry we are going to remove.
- @type it: Iterator
- @param removeNewFlags: True if new flags should be removed; False otherwise. Default: True.
- @type removeNewFlags: boolean"""
-
- def __remove (type, cpv):
- del self.iters[type][cpv]
- try:
- del self.deps[type][cpv]
- except KeyError: # this seems to be removed due to a BlockedException - so no deps here atm ;)
- debug("Catched KeyError => %s seems not to be in self.deps. Should be no harm in normal cases.", cpv)
-
- for key in self.blocks[type].keys():
- if cpv in self.blocks[type][key]:
- self.blocks[type][key].remove(cpv)
-
- if not self.blocks[type][key]: # list is empty -> remove the whole key
- del self.blocks[type][key]
-
- if removeNewFlags: # remove the changed flags
- flags.remove_new_use_flags(cpv)
- flags.remove_new_masked(cpv)
- flags.remove_new_testing(cpv)
-
- if self.tree.iter_has_parent(it):
- cpv = self.tree.get_value(it, self.tree.get_cpv_column())
- if self.tree.is_in_emerge(it): # Emerge
-
- __remove("install", cpv)
-
- try:
- self.mergequeue.remove(cpv)
- except ValueError: # this is a dependency - ignore
- try:
- self.oneshotmerge.remove(cpv)
- except ValueError:
- debug("Catched ValueError => %s seems not to be in merge-queue. Should be no harm.", cpv)
-
- elif self.tree.is_in_unmerge(it): # in Unmerge
- del self.iters["uninstall"][cpv]
- self.unmergequeue.remove(cpv)
-
- elif self.tree.is_in_update(it):
- __remove("update", cpv)
-
-
- self.tree.remove(it)
-
- def is_empty (self):
- """Checks whether the current queue is empty and not working. Therefore it looks, whether the queues are empty,
- and the process is not running.
-
- @returns: True if everything is empty and the process is not running.
- @rtype: bool"""
-
- return not (self.process or any(map(len, self.iters.itervalues())))
+ """This class manages the emerge queue."""
+
+ def __init__ (self, tree = None, console = None, db = None, title_update = None, threadClass = threading.Thread):
+ """Constructor.
+
+ @param tree: Tree to append all the items to.
+ @type tree: GtkTree
+ @param console: Output is shown here.
+ @type console: GtkConsole
+ @param db: A database instance.
+ @type db: Database
+ @param title_update: A function, which will be called whenever there is a title update.
+ @type title_update: function(string)"""
+
+ # the different queues
+ self.mergequeue = [] # for emerge
+ self.unmergequeue = [] # for emerge -C
+ self.oneshotmerge = [] # for emerge --oneshot
+
+ # the emerge process
+ self.process = None
+ self.threadQueue = WaitingQueue(threadClass = threadClass)
+ self.pty = None
+
+ # dictionaries with data about the packages in the queue
+ self.iters = {"install" : {}, "uninstall" : {}, "update" : {}} # iterator in the tree
+ self.deps = {"install" : {}, "update" : {}} # all the deps of the package
+ self.blocks = {"install" : OrderedDict(), "update" : OrderedDict()}
+
+ # member vars
+ self.tree = tree
+ if self.tree and not isinstance(self.tree, GtkTree): raise TypeError, "tree passed is not a GtkTree-object"
+
+ self.console = console
+ if self.console and not isinstance(self.console, GtkConsole): raise TypeError, "console passed is not a GtkConsole-object"
+
+ self.db = db
+ self.title_update = title_update
+ self.threadClass = threadClass
+
+ if self.console:
+ self.pty = pty.openpty()
+ self.console.set_pty(self.pty[0])
+
+ def _get_pkg_from_cpv (self, cpv, unmask = False):
+ """Gets a L{backend.Package}-object from a cpv.
+
+ @param cpv: the cpv to get the package for
+ @type cpv: string (cpv)
+ @param unmask: if True we will look for masked packages if we cannot find unmasked ones
+ @type unmask: boolean
+ @return: created package
+ @rtype: backend.Package
+
+ @raises backend.PackageNotFoundException: If no package could be found - normally it is existing but masked."""
+
+ # for the beginning: let us create a package object - but it is not guaranteed, that it actually exists in portage
+ pkg = system.new_package(cpv)
+ masked = not (pkg.is_masked() or pkg.is_testing(use_keywords=True)) # we are setting this to True in case we have unmasked it already, but portage does not know this
+
+ # and now try to find it in portage
+ pkg = system.find_packages("="+cpv, masked = masked)
+
+ if pkg: # gotcha
+ pkg = pkg[0]
+
+ elif unmask: # no pkg returned, but we are allowed to unmask it
+ pkg = system.find_packages("="+cpv, masked = True)
+
+ if not pkg:
+ raise backend.PackageNotFoundException(cpv) # also not found
+ else:
+ pkg = pkg[0]
+
+ if pkg.is_testing(use_keywords = True):
+ pkg.set_testing(True)
+ if pkg.is_masked():
+ pkg.set_masked()
+
+ else: # no pkg returned - and we are not allowed to unmask
+ raise backend.PackageNotFoundException(cpv)
+
+ return pkg
+
+ def update_tree (self, it, cpv, unmask = False, oneshot = False, type = "install"):
+ """This updates the tree recursivly, or? Isn't it? Bjorn!
+
+ @param it: iterator where to append
+ @type it: Iterator
+ @param cpv: The package to append.
+ @type cpv: string (cat/pkg-ver)
+ @param unmask: True if we are allowed to look for masked packages
+ @type unmask: boolean
+ @param oneshot: True if we want to emerge is oneshot
+ @type oneshot: boolean
+ @param type: the type of the updating
+ @type type: string
+
+ @raises backend.BlockedException: When occured during dependency-calculation.
+ @raises backend.PackageNotFoundException: If no package could be found - normally it is existing but masked."""
+
+ if cpv in self.deps[type]:
+ return # in list already and therefore it's already in the tree too
+
+ # try to find an already installed instance
+ update = False
+ downgrade = False
+ uVersion = None
+ changedUse = []
+ try:
+ pkg = self._get_pkg_from_cpv(cpv, unmask)
+ if not pkg.is_installed():
+ old = system.find_packages(pkg.get_slot_cp(), system.SET_INSTALLED)
+ if old:
+ old = old[0] # assume we have only one there
+ cmp = pkg.compare_version(old)
+ if cmp > 0:
+ update = True
+ elif cmp < 0:
+ downgrade = True
+
+ uVersion = old.get_version()
+
+ old_iuse = set(old.get_iuse_flags())
+ new_iuse = set(pkg.get_iuse_flags())
+
+ for i in old_iuse.difference(new_iuse):
+ changedUse.append("-"+i)
+
+ for i in new_iuse.difference(old_iuse):
+ changedUse.append("+"+i)
+ else:
+ old_iuse = set(pkg.get_iuse_flags(installed = True))
+ new_iuse = set(pkg.get_iuse_flags(installed = False))
+
+ for i in old_iuse.difference(new_iuse):
+ changedUse.append("-"+i)
+
+ for i in new_iuse.difference(old_iuse):
+ changedUse.append("+"+i)
+
+ except backend.PackageNotFoundException, e: # package not found / package is masked -> delete current tree and re-raise the exception
+ if type == "update": # remove complete tree
+ self.remove_with_children(self.tree.first_iter(it), removeNewFlags = False)
+
+ elif type == "install": # remove only the intentionally added package
+ top = self.tree.first_iter(it)
+ parent = self.tree.parent_iter(it)
+
+ if parent:
+ while not self.tree.iter_equal(top, parent):
+ parent = self.tree.parent_iter(parent)
+ it = self.tree.parent_iter(it)
+
+ self.remove_with_children(it, removeNewFlags = False)
+
+ if not self.tree.iter_has_children(top): # remove completely if nothing left
+ self.remove(top)
+ raise
+
+ # add iter
+ subIt = self.tree.append(it, self.tree.build_append_value(cpv, oneshot = oneshot, update = update, downgrade = downgrade, version = uVersion, useChange = changedUse))
+ self.iters[type][cpv] = subIt
+
+ # get dependencies
+ deps = pkg.get_dep_packages(return_blocks = True)
+ self.deps[type][cpv] = deps
+
+ for d in deps:
+ if d[0] == "!": # block
+ dep = d[1:]
+ if not dep in self.blocks[type]:
+ self.blocks[type][dep] = set()
+
+ self.blocks[type][dep].add(cpv)
+ else: # recursive call
+ self.update_tree(subIt, d, unmask, type = type)
+
+ def append (self, cpv, type = "install", update = False, forceUpdate = False, unmask = False, oneshot = False):
+ """Appends a cpv either to the merge queue or to the unmerge-queue.
+ Also updates the tree-view.
+
+ @param cpv: Package to add
+ @type cpv: string (cat/pkg-ver)
+ @param type: The type of this append process. Possible values are "install", "uninstall", "update".
+ @type type: string
+ @param update: Set to True if a package is going to be updated (e.g. if the use-flags changed).
+ @type update: boolean
+ @param forceUpdate: Set to True if the update should be forced.
+ @type forceUpdate: boolean
+ @param unmask: True if we are allowed to look for masked packages
+ @type unmask: boolean
+ @param oneshot: True if this package should not be added to the world-file.
+ @type oneshot: boolean
+
+ @raises portato.backend.PackageNotFoundException: if trying to add a package which does not exist"""
+
+ if type in ("install", "update"): # emerge
+ if update:
+ pkg = self._get_pkg_from_cpv(cpv, unmask)
+ deps = pkg.get_dep_packages(return_blocks = True)
+
+ if not forceUpdate and cpv in self.deps[type] and deps == self.deps[type][cpv]:
+ return # nothing changed - return
+ else:
+ hasBeenInQueue = (cpv in self.mergequeue or cpv in self.oneshotmerge)
+ parentIt = self.tree.parent_iter(self.iters[type][cpv])
+
+ # delete it out of the tree - but NOT the changed flags
+ self.remove_with_children(self.iters[type][cpv], removeNewFlags = False)
+
+ if hasBeenInQueue: # package has been in queue before
+ self._queue_append(cpv, oneshot)
+
+ self.update_tree(parentIt, cpv, unmask, oneshot = oneshot, type = type)
+ else: # not update
+ if type == "install":
+ if self.tree:
+ self.update_tree(self.tree.get_emerge_it(), cpv, unmask, type = type, oneshot = oneshot)
+ self._queue_append(cpv, oneshot)
+ elif type == "update" and self.tree:
+ self.update_tree(self.tree.get_update_it(), cpv, unmask, type = type, oneshot = oneshot)
+
+ # handle blocks
+ if self.blocks[type]:
+ # check whether anything blocks something in the queue
+ for block in self.blocks[type]:
+ for c in self.iters[type]:
+ if system.cpv_matches(c, block):
+ blocked = ", ".join(self.blocks[type][block])
+ warning("'%s' is blocked by: %s", c, blocked)
+ self.remove_with_children(self.iters[type][c], False)
+ raise BlockedException(c, blocked)
+
+ #
+ # check whether we block a version that we are going to replace nevertheless
+ #
+
+ # get the blocks that block an installed package
+ inst = []
+ for block in self.blocks[type]:
+ pkgs = system.find_packages(block, system.SET_INSTALLED)
+ if pkgs:
+ inst.append((pkgs, block))
+
+ # the slot-cp's of the packages in the queue
+ slots = {}
+ for c in self.iters[type]:
+ slots[system.new_package(c).get_slot_cp()] = cpv
+
+ # check the installed blocks against the slot-cp's
+ for pkgs, block in inst[:]:
+ done = False
+ for pkg in pkgs:
+ done = False
+ if pkg.get_slot_cp() in slots:
+ debug("Block '%s' can be ignored, because the blocking package is going to be replaced with '%s'.", block, slots[pkg.get_slot_cp()])
+ done = True
+ if done:
+ inst.remove((pkgs,block))
+
+ if inst: # there is still something left to block
+ for pkgs, block in inst:
+ blocked = ", ".join(self.blocks[type][block])
+ warning("'%s' blocks the installation of: %s", pkgs[0].get_cpv(), blocked)
+ self.remove_with_children(self.iters[type][cpv], False)
+ raise BlockedException(blocked, pkgs[0].get_cpv())
+
+ else: # unmerge
+ self.unmergequeue.append(cpv)
+ if self.tree: # update tree
+ self.iters["uninstall"].update({cpv: self.tree.append(self.tree.get_unmerge_it(), self.tree.build_append_value(cpv))})
+
+ def _queue_append (self, cpv, oneshot = False):
+ """Convenience function appending a cpv either to self.mergequeue or to self.oneshotmerge.
+
+ @param cpv: cpv to add
+ @type cpv: string (cpv)
+ @param oneshot: True if this package should not be added to the world-file.
+ @type oneshot: boolean"""
+
+ if not oneshot:
+ if cpv not in self.mergequeue:
+ self.mergequeue.append(cpv)
+ else:
+ if cpv not in self.oneshotmerge:
+ self.oneshotmerge.append(cpv)
+
+ def doEmerge (self, options, packages, it, *args, **kwargs):
+ top = None
+ if self.tree and it:
+ for v in it.itervalues():
+ self.tree.set_in_progress(v)
+ top = self.tree.first_iter(v)
+ break
+
+ self.threadQueue.put(self.__emerge, options, packages, it, top, *args, **kwargs)
+
+ def __emerge (self, options, packages, it, top, command = None):
+ """Calls emerge and updates the terminal.
+
+ @param options: options to send to emerge
+ @type options: string[]
+ @param packages: packages to emerge
+ @type packages: string[]
+ @param it: Iterators which point to these entries whose children will be removed after completion.
+ @type it: dict(string -> Iterator)
+ @param top: The top iterator
+ @type top: Iterator
+ @param command: the command to execute - default is "/usr/bin/python /usr/bin/emerge"
+ @type command: string[]"""
+
+ @plugin.hook("emerge", packages = packages, command = command, console = self.console, title_update = self.title_update)
+ def sub_emerge(command):
+ if command is None:
+ command = system.get_merge_command()
+
+ # open tty
+ if self.console:
+ self.console.reset()
+
+ def pre ():
+ os.setsid() # new session
+ if self.console:
+ import fcntl, termios
+ fcntl.ioctl(self.pty[1], termios.TIOCSCTTY, 0) # set pty-slave as session tty
+ os.dup2(self.pty[1], 0)
+ os.dup2(self.pty[1], 1)
+ os.dup2(self.pty[1], 2)
+
+ # get all categories that are being touched during the emerge process
+ cats = set(map(lambda x: x.split("/")[0], it.iterkeys()))
+
+ # start emerge
+ self.process = Popen(command+options+packages, shell = False, env = system.get_environment(), preexec_fn = pre)
+
+ # remove packages from queue
+ if self.tree and it and not self.tree.is_in_unmerge(top):
+ self.up = Updater(self, it, self.threadClass)
+ else:
+ self.up = None
+
+ # update title
+ if self.console:
+ old_title = self.console.get_window_title()
+ while self.process and self.process.poll() is None:
+ if self.title_update :
+ title = self.console.get_window_title()
+ if title != old_title:
+ self.title_update(title)
+ old_title = title
+ time.sleep(0.5)
+
+ if self.up:
+ self.up.stop()
+ if it:
+ self.tree.set_in_progress(top, False)
+ else:
+ self.remove(top)
+ elif self.tree and it:
+ self.remove_with_children(top)
+
+ if self.title_update: self.title_update(None)
+
+ if self.process is None: # someone resetted this
+ self.threadQueue.next()
+ return
+ else:
+ ret = self.process.returncode
+ self.process = None
+ self.threadQueue.next()
+
+ @plugin.hook("after_emerge", packages = packages, retcode = ret)
+ def update_packages():
+ if self.db:
+ for cat in cats:
+ self.db.reload(cat)
+ debug("Category %s refreshed", cat)
+
+ update_packages()
+
+ sub_emerge(command)
+
+ def emerge (self, force = False, options = None):
+ """Emerges everything in the merge-queue.
+
+ @param force: If False, '-pv' is send to emerge. Default: False.
+ @type force: boolean
+ @param options: Additional options to send to the emerge command
+ @type options: string[]"""
+
+ def prepare(queue):
+ """Prepares the list of iterators and the list of packages."""
+ list = []
+ its = {}
+ for k in queue:
+ list += ["="+k]
+ if self.tree:
+ its.update({k : self.iters["install"][k]})
+
+ return list, its
+
+ if self.tree:
+ ownit = self.iters["install"]
+ else:
+ ownit = {}
+
+ # oneshot-queue
+ if self.oneshotmerge:
+ # prepare package-list for oneshot
+ list, its = prepare(self.oneshotmerge)
+ if not self.mergequeue :# the other one does not exist - remove completely
+ its = ownit
+
+ s = system.get_oneshot_option()
+ if not force: s += system.get_pretend_option()
+ if options is not None: s += options
+
+ self.doEmerge(s, list, its, caller = self.emerge)
+
+ # normal queue
+ if self.mergequeue:
+ # prepare package-list
+ list, its = prepare(self.mergequeue)
+ if not self.oneshotmerge: # the other one does not exist - remove completely
+ its = ownit
+
+ s = []
+ if not force: s = system.get_pretend_option()
+ if options is not None: s += options
+
+ self.doEmerge(s, list, its, caller = self.emerge)
+
+ def unmerge (self, force = False, options = None):
+ """Unmerges everything in the umerge-queue.
+
+ @param force: If False, '-pv' is send to emerge. Default: False.
+ @type force: boolean
+ @param options: Additional options to send to the emerge command
+ @type options: string[]"""
+
+ if len(self.unmergequeue) == 0: return # nothing in queue
+
+ list = self.unmergequeue[:] # copy the unmerge-queue
+
+ # set options
+ s = system.get_unmerge_option()
+ if not force: s += system.get_pretend_option()
+ if options is not None: s += options
+
+ if self.tree:
+ it = self.iters["uninstall"]
+ else:
+ it = {}
+
+ self.doEmerge(s,list, it, caller = self.unmerge)
+
+ def update_world(self, sets = ("world",), force = False, newuse = False, deep = False, options = None):
+ """Does an update world. newuse and deep are the arguments handed to emerge.
+
+ @param sets: The sets to update.
+ @type sets: string[]
+ @param force: If False, '-pv' is send to emerge. Default: False.
+ @type force: boolean
+ @param newuse: If True, append newuse options
+ @type newuse: boolean
+ @param deep: If True, append deep options
+ @type deep: boolean
+ @param options: Additional options to send to the emerge command
+ @type options: string[]"""
+
+ opts = system.get_update_option()
+
+ if newuse: opts += system.get_newuse_option()
+ if deep: opts += system.get_deep_option()
+ if not force: opts += system.get_pretend_option()
+ if options is not None: opts += options
+
+ if self.tree:
+ it = self.iters["update"]
+ else:
+ it = {}
+
+ self.doEmerge(opts, list(sets), it, caller = self.update_world)
+
+ def sync (self, command = None):
+ """Calls "emerge --sync".
+
+ @param command: command to execute to sync. If None "emerge --sync" is taken.
+ @type command: string[]"""
+
+ if command is None:
+ command = system.get_sync_command()
+
+ try:
+ while True:
+ idx = command.index("&&")
+ self.doEmerge([],[],{}, command[:idx], caller = self.sync)
+ command = command[idx+1:]
+ except ValueError: # no && in command
+ self.doEmerge([],[],{}, command, caller = self.sync)
+
+ def kill_emerge (self):
+ """Kills the emerge process."""
+ if self.process is not None:
+ self.threadQueue.clear() # remove all pending emerge threads
+ try:
+ pgid = os.getpgid(self.process.pid)
+ os.killpg(pgid, signal.SIGTERM)
+ debug("Process should be terminated")
+ if self.process.poll() is None:
+ os.killpg(pgid, signal.SIGKILL)
+ debug("Process should be killed")
+ except AttributeError:
+ debug("AttributeError occured ==> process not exisiting - ignore")
+ except OSError:
+ debug("OSError occured ==> process already stopped - ignore")
+
+ self.process = None
+
+ def stop_emerge (self):
+ if self.process is not None:
+ os.killpg(os.getpgid(self.process.pid), signal.SIGSTOP)
+ debug("Process should be stopped")
+
+ def continue_emerge (self):
+ if self.process is not None:
+ os.killpg(os.getpgid(self.process.pid), signal.SIGCONT)
+ debug("Process should continue")
+
+ def remove_with_children (self, it, removeNewFlags = True):
+ """Convenience function which removes all children of an iterator and than the iterator itself.
+
+ @param it: The iter which to remove.
+ @type it: Iterator
+ @param removeNewFlags: True if new flags should be removed; False otherwise. Default: True.
+ @type removeNewFlags: boolean"""
+
+ self.remove_children(it, removeNewFlags)
+ self.remove(it, removeNewFlags)
+
+ def remove_children (self, parentIt, removeNewFlags = True):
+ """Removes all children of a given parent TreeIter recursivly.
+
+ @param parentIt: The iter from which to remove all children.
+ @type parentIt: Iterator
+ @param removeNewFlags: True if new flags should be removed; False otherwise. Default: True.
+ @type removeNewFlags: boolean"""
+
+ childIt = self.tree.first_child_iter(parentIt)
+
+ while childIt:
+ if (self.tree.iter_has_children(childIt)): # recursive call
+ self.remove_children(childIt, removeNewFlags)
+ temp = childIt
+ childIt = self.tree.next_iter(childIt)
+ self.remove(temp, removeNewFlags)
+
+ def remove (self, it, removeNewFlags = True):
+ """Removes a specific item in the tree. This does not remove the top-entries.
+
+ @param it: Iterator which points to the entry we are going to remove.
+ @type it: Iterator
+ @param removeNewFlags: True if new flags should be removed; False otherwise. Default: True.
+ @type removeNewFlags: boolean"""
+
+ def __remove (type, cpv):
+ del self.iters[type][cpv]
+ try:
+ del self.deps[type][cpv]
+ except KeyError: # this seems to be removed due to a BlockedException - so no deps here atm ;)
+ debug("Catched KeyError => %s seems not to be in self.deps. Should be no harm in normal cases.", cpv)
+
+ for key in self.blocks[type].keys():
+ if cpv in self.blocks[type][key]:
+ self.blocks[type][key].remove(cpv)
+
+ if not self.blocks[type][key]: # list is empty -> remove the whole key
+ del self.blocks[type][key]
+
+ if removeNewFlags: # remove the changed flags
+ flags.remove_new_use_flags(cpv)
+ flags.remove_new_masked(cpv)
+ flags.remove_new_testing(cpv)
+
+ if self.tree.iter_has_parent(it):
+ cpv = self.tree.get_value(it, self.tree.get_cpv_column())
+ if self.tree.is_in_emerge(it): # Emerge
+
+ __remove("install", cpv)
+
+ try:
+ self.mergequeue.remove(cpv)
+ except ValueError: # this is a dependency - ignore
+ try:
+ self.oneshotmerge.remove(cpv)
+ except ValueError:
+ debug("Catched ValueError => %s seems not to be in merge-queue. Should be no harm.", cpv)
+
+ elif self.tree.is_in_unmerge(it): # in Unmerge
+ del self.iters["uninstall"][cpv]
+ self.unmergequeue.remove(cpv)
+
+ elif self.tree.is_in_update(it):
+ __remove("update", cpv)
+
+
+ self.tree.remove(it)
+
+ def is_empty (self):
+ """Checks whether the current queue is empty and not working. Therefore it looks, whether the queues are empty,
+ and the process is not running.
+
+ @returns: True if everything is empty and the process is not running.
+ @rtype: bool"""
+
+ return not (self.process or any(map(len, self.iters.itervalues())))
diff --git a/portato/gui/session.py b/portato/gui/session.py
index a37cd85..1eca248 100644
--- a/portato/gui/session.py
+++ b/portato/gui/session.py
@@ -18,16 +18,16 @@ SESSION_VERSION = 1
class SessionException (Exception):
- error = _("Version mismatch.")
- def __init__ (self, got, expected):
- self.got = got
- self.expected = expected
+ error = _("Version mismatch.")
+ def __init__ (self, got, expected):
+ self.got = got
+ self.expected = expected
- def __str__ (self):
- return "%s %s" % (self.error, (_("Got '%d' - expected '%d'.") % (self.got, self.expected)))
+ def __str__ (self):
+ return "%s %s" % (self.error, (_("Got '%d' - expected '%d'.") % (self.got, self.expected)))
class OldSessionException (SessionException):
- error = _("Current session format is too old.")
+ error = _("Current session format is too old.")
class NewSessionException (SessionException):
- error = _("Current session format is newer than this version supports.")
+ error = _("Current session format is newer than this version supports.")
diff --git a/portato/gui/updater.py b/portato/gui/updater.py
index c4c81e7..c4ad2a5 100644
--- a/portato/gui/updater.py
+++ b/portato/gui/updater.py
@@ -18,109 +18,109 @@ import threading, subprocess, time
from ..helper import debug, warning, error
class Updater (object):
- """
- This class is intended to check what package is currently being installed and remove this one from the queue.
+ """
+ This class is intended to check what package is currently being installed and remove this one from the queue.
- @cvar SED_EXP: The sed expression to strip the package name out of the qlop call.
- """
-
- SED_EXP = r"""
+ @cvar SED_EXP: The sed expression to strip the package name out of the qlop call.
+ """
+
+ SED_EXP = r"""
/\*/{
s/ \* //
n
}
d"""
-
- def __init__ (self, queue, iterators, threadClass = threading.Thread):
- """
- Constructor.
- Also directly initializes the thread.
-
- @param queue: an emerge queue instance
- @type queue: EmergeQueue
- @param iterators: a dictionary of iterators in the current queue
- @type iterators: dict(string->Iterator)
- """
-
- if not issubclass(threadClass, threading.Thread):
- raise ValueError, "Only subclasses of threading.Thread are allowed."
-
- self.queue = queue
- self.iterators = iterators
- self.threadClass = threadClass
- self.stopEvent = threading.Event()
- self.removed = set()
-
- t = threadClass(name = "Queue Updater Thread", target = self.run)
- t.setDaemon(True)
- t.start()
-
- def run (self):
- """
- Run and run and run ...
- Checks the packages until being stopped.
- """
-
- curr = set()
- while not self.stopEvent.isSet():
-
- # this = $(qlop -cCq | sed $SED_EXP)
- p1 = subprocess.Popen(["qlop", "--current", "--nocolor", "--quiet"], stdout = subprocess.PIPE)
- this = subprocess.Popen(["sed", self.SED_EXP], stdout = subprocess.PIPE, stdin = p1.stdout).communicate()[0]
-
- this = set(this.split()) if this else set()
- for removed in curr - this:
- self.remove(self.find(removed)) # remove the previous
- curr = this
-
- time.sleep(2.0)
-
- self.removed = set()
-
- def stop (self):
- """
- Stops the current updater.
- """
- self.stopEvent.set()
-
- def find (self, pv, masked = False):
- """
- As qlop only returns 'package-version' we need to assign it to a cpv.
- This is done here.
- """
-
- pkgs = system.find_packages("=%s" % pv, only_cpv = True, masked = masked)
-
- if len(pkgs) > 1: # ambigous - try to find the one which is also in the iterators
- for p in pkgs:
- if p in self.iterators:
- return p
- elif not pkgs: # nothing found =|
- if not masked:
- warning(_("No unmasked version of package '%s' found. Trying masked ones. This normally should not happen..."), pv)
- return self.find(pv, True)
-
- else:
- error(_("Trying to remove package '%s' from queue which does not exist in system."), pv)
- return None
- else: # only one choice =)
- return pkgs[0]
-
- def remove (self, cpv):
- """
- Remove a package from the queue.
- """
-
- if cpv is None:
- debug("Nothing to remove.")
- return
-
- if cpv in self.removed:
- return
-
- self.removed.add(cpv)
-
- try:
- self.queue.remove_with_children(self.iterators[cpv])
- except KeyError:
- debug("'%s' should be removed, but is not in queue.", cpv)
+
+ def __init__ (self, queue, iterators, threadClass = threading.Thread):
+ """
+ Constructor.
+ Also directly initializes the thread.
+
+ @param queue: an emerge queue instance
+ @type queue: EmergeQueue
+ @param iterators: a dictionary of iterators in the current queue
+ @type iterators: dict(string->Iterator)
+ """
+
+ if not issubclass(threadClass, threading.Thread):
+ raise ValueError, "Only subclasses of threading.Thread are allowed."
+
+ self.queue = queue
+ self.iterators = iterators
+ self.threadClass = threadClass
+ self.stopEvent = threading.Event()
+ self.removed = set()
+
+ t = threadClass(name = "Queue Updater Thread", target = self.run)
+ t.setDaemon(True)
+ t.start()
+
+ def run (self):
+ """
+ Run and run and run ...
+ Checks the packages until being stopped.
+ """
+
+ curr = set()
+ while not self.stopEvent.isSet():
+
+ # this = $(qlop -cCq | sed $SED_EXP)
+ p1 = subprocess.Popen(["qlop", "--current", "--nocolor", "--quiet"], stdout = subprocess.PIPE)
+ this = subprocess.Popen(["sed", self.SED_EXP], stdout = subprocess.PIPE, stdin = p1.stdout).communicate()[0]
+
+ this = set(this.split()) if this else set()
+ for removed in curr - this:
+ self.remove(self.find(removed)) # remove the previous
+ curr = this
+
+ time.sleep(2.0)
+
+ self.removed = set()
+
+ def stop (self):
+ """
+ Stops the current updater.
+ """
+ self.stopEvent.set()
+
+ def find (self, pv, masked = False):
+ """
+ As qlop only returns 'package-version' we need to assign it to a cpv.
+ This is done here.
+ """
+
+ pkgs = system.find_packages("=%s" % pv, only_cpv = True, masked = masked)
+
+ if len(pkgs) > 1: # ambigous - try to find the one which is also in the iterators
+ for p in pkgs:
+ if p in self.iterators:
+ return p
+ elif not pkgs: # nothing found =|
+ if not masked:
+ warning(_("No unmasked version of package '%s' found. Trying masked ones. This normally should not happen..."), pv)
+ return self.find(pv, True)
+
+ else:
+ error(_("Trying to remove package '%s' from queue which does not exist in system."), pv)
+ return None
+ else: # only one choice =)
+ return pkgs[0]
+
+ def remove (self, cpv):
+ """
+ Remove a package from the queue.
+ """
+
+ if cpv is None:
+ debug("Nothing to remove.")
+ return
+
+ if cpv in self.removed:
+ return
+
+ self.removed.add(cpv)
+
+ try:
+ self.queue.remove_with_children(self.iterators[cpv])
+ except KeyError:
+ debug("'%s' should be removed, but is not in queue.", cpv)
diff --git a/portato/gui/utils.py b/portato/gui/utils.py
index cd5c50c..661af6b 100644
--- a/portato/gui/utils.py
+++ b/portato/gui/utils.py
@@ -32,280 +32,280 @@ from ..constants import APP, LOCALE_DIR
from ..config_parser import ConfigParser
def get_color (cfg, name):
- return gtk.gdk.color_parse("#%s" % cfg.get(name, section = "COLORS"))
+ return gtk.gdk.color_parse("#%s" % cfg.get(name, section = "COLORS"))
class GtkThread (Thread):
- def run(self):
- # for some reason, I have to install this for each thread ...
- gettext.install(APP, LOCALE_DIR, unicode = True)
- try:
- Thread.run(self)
- except SystemExit:
- raise # let normal thread handle it
- except:
- type, val, tb = sys.exc_info()
- try:
- sys.excepthook(type, val, tb, thread = self.getName())
- except TypeError:
- raise type, val, tb # let normal thread handle it
- finally:
- del type, val, tb
+ def run(self):
+ # for some reason, I have to install this for each thread ...
+ gettext.install(APP, LOCALE_DIR, unicode = True)
+ try:
+ Thread.run(self)
+ except SystemExit:
+ raise # let normal thread handle it
+ except:
+ type, val, tb = sys.exc_info()
+ try:
+ sys.excepthook(type, val, tb, thread = self.getName())
+ except TypeError:
+ raise type, val, tb # let normal thread handle it
+ finally:
+ del type, val, tb
class Config (ConfigParser):
-
- def __init__ (self, cfgFile):
- """Constructor.
-
- @param cfgFile: path to config file
- @type cfgFile: string"""
-
- ConfigParser.__init__(self, cfgFile)
-
- # read config
- self.parse()
-
- # local configs
- self.local = {}
-
- def modify_flags_config (self):
- """Sets the internal config of the L{flags}-module.
- @see: L{flags.set_config()}"""
-
- flagCfg = {
- "usefile": self.get("useFile"),
- "usePerVersion" : self.get_boolean("usePerVersion"),
- "maskfile" : self.get("maskFile"),
- "maskPerVersion" : self.get_boolean("maskPerVersion"),
- "testingfile" : self.get("keywordFile"),
- "testingPerVersion" : self.get_boolean("keywordPerVersion")}
- flags.set_config(flagCfg)
-
- def modify_debug_config (self):
- if self.get_boolean("debug"):
- level = logging.DEBUG
- else:
- level = logging.INFO
-
- set_log_level(level)
-
- def modify_system_config (self):
- """Sets the system config.
- @see: L{backend.set_system()}"""
- set_system(self.get("system"))
-
- def modify_external_configs (self):
- """Convenience function setting all external configs."""
- self.modify_debug_config()
- self.modify_flags_config()
- self.modify_system_config()
-
- def set_local(self, cpv, name, val):
- """Sets some local config.
-
- @param cpv: the cpv describing the package for which to set this option
- @type cpv: string (cpv)
- @param name: the option's name
- @type name: string
- @param val: the value to set
- @type val: any"""
-
- if not cpv in self.local:
- self.local[cpv] = {}
-
- self.local[cpv].update({name:val})
-
- def get_local(self, cpv, name):
- """Returns something out of the local config.
-
- @param cpv: the cpv describing the package from which to get this option
- @type cpv: string (cpv)
- @param name: the option's name
- @type name: string
- @return: value stored for the cpv and name or None if not found
- @rtype: any"""
-
- if not cpv in self.local:
- return None
- if not name in self.local[cpv]:
- return None
-
- return self.local[cpv][name]
-
- def write(self):
- """Writes to the config file and modify any external configs."""
- ConfigParser.write(self)
- self.modify_external_configs()
+
+ def __init__ (self, cfgFile):
+ """Constructor.
+
+ @param cfgFile: path to config file
+ @type cfgFile: string"""
+
+ ConfigParser.__init__(self, cfgFile)
+
+ # read config
+ self.parse()
+
+ # local configs
+ self.local = {}
+
+ def modify_flags_config (self):
+ """Sets the internal config of the L{flags}-module.
+ @see: L{flags.set_config()}"""
+
+ flagCfg = {
+ "usefile": self.get("useFile"),
+ "usePerVersion" : self.get_boolean("usePerVersion"),
+ "maskfile" : self.get("maskFile"),
+ "maskPerVersion" : self.get_boolean("maskPerVersion"),
+ "testingfile" : self.get("keywordFile"),
+ "testingPerVersion" : self.get_boolean("keywordPerVersion")}
+ flags.set_config(flagCfg)
+
+ def modify_debug_config (self):
+ if self.get_boolean("debug"):
+ level = logging.DEBUG
+ else:
+ level = logging.INFO
+
+ set_log_level(level)
+
+ def modify_system_config (self):
+ """Sets the system config.
+ @see: L{backend.set_system()}"""
+ set_system(self.get("system"))
+
+ def modify_external_configs (self):
+ """Convenience function setting all external configs."""
+ self.modify_debug_config()
+ self.modify_flags_config()
+ self.modify_system_config()
+
+ def set_local(self, cpv, name, val):
+ """Sets some local config.
+
+ @param cpv: the cpv describing the package for which to set this option
+ @type cpv: string (cpv)
+ @param name: the option's name
+ @type name: string
+ @param val: the value to set
+ @type val: any"""
+
+ if not cpv in self.local:
+ self.local[cpv] = {}
+
+ self.local[cpv].update({name:val})
+
+ def get_local(self, cpv, name):
+ """Returns something out of the local config.
+
+ @param cpv: the cpv describing the package from which to get this option
+ @type cpv: string (cpv)
+ @param name: the option's name
+ @type name: string
+ @return: value stored for the cpv and name or None if not found
+ @rtype: any"""
+
+ if not cpv in self.local:
+ return None
+ if not name in self.local[cpv]:
+ return None
+
+ return self.local[cpv][name]
+
+ def write(self):
+ """Writes to the config file and modify any external configs."""
+ ConfigParser.write(self)
+ self.modify_external_configs()
class PkgData (object):
- __slots__ = ("cat", "pkg", "inst")
+ __slots__ = ("cat", "pkg", "inst")
- def __init__ (self, cat, pkg, inst):
- self.cat = cat
- self.pkg = pkg
- self.inst = 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 __iter__ (self):
+ return iter((self.cat, self.pkg, self.inst))
- def __cmp__ (self, other):
- return cmp(self.pkg.lower(), other.pkg.lower())
+ 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}
+ def __repr__ (self):
+ return "<Package (%(cat)s, %(pkg)s, %(inst)s)>" % {"cat" : self.cat, "pkg" : self.pkg, "inst" : self.inst}
class Database (object):
- """An internal database which holds a simple dictionary cat -> [package_list]."""
-
- ALL = _("ALL")
-
- def __init__ (self):
- """Constructor."""
- self.__initialize()
- self._lock = RLock()
-
- 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)
+ """An internal database which holds a simple dictionary cat -> [package_list]."""
+
+ ALL = _("ALL")
+
+ def __init__ (self):
+ """Constructor."""
+ self.__initialize()
+ self._lock = RLock()
+
+ 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)
diff --git a/portato/gui/views.py b/portato/gui/views.py
index bd98ad8..a2d0468 100644
--- a/portato/gui/views.py
+++ b/portato/gui/views.py
@@ -20,130 +20,130 @@ import logging
from ..helper import warning
class LazyView (object):
- def __init__ (self):
- self.connect("map", self.cb_mapped)
+ def __init__ (self):
+ self.connect("map", self.cb_mapped)
- self.pkg = None
- self.updated = False
+ self.pkg = None
+ self.updated = False
- def update (self, pkg, force = False):
- self.pkg = pkg
- self.updated = True
-
- if force:
- self.cb_mapped()
+ def update (self, pkg, force = False):
+ self.pkg = pkg
+ self.updated = True
+
+ if force:
+ self.cb_mapped()
- def cb_mapped (self, *args):
- if self.updated and self.pkg:
- self.set_text("".join(self._get_content()))
- self.updated = False
+ def cb_mapped (self, *args):
+ if self.updated and self.pkg:
+ self.set_text("".join(self._get_content()))
+ self.updated = False
- return False
+ return False
- def set_text (self, text):
- raise NotImplementedError
+ def set_text (self, text):
+ raise NotImplementedError
- def _get_content (self):
- raise NotImplementedError
+ def _get_content (self):
+ raise NotImplementedError
class ListView (gtk.TextView, LazyView):
- def __init__ (self, content_fn):
- self.content_fn = content_fn
+ def __init__ (self, content_fn):
+ self.content_fn = content_fn
- gtk.TextView.__init__(self)
- LazyView.__init__(self)
+ gtk.TextView.__init__(self)
+ LazyView.__init__(self)
- self.set_editable(False)
- self.set_cursor_visible(False)
+ self.set_editable(False)
+ self.set_cursor_visible(False)
- def set_text (self, text):
- self.get_buffer().set_text(text)
+ def set_text (self, text):
+ self.get_buffer().set_text(text)
- def _get_content (self):
- return self.content_fn(self.pkg)
+ def _get_content (self):
+ return self.content_fn(self.pkg)
class InstalledOnlyView (ListView):
- def _get_content (self):
- if self.pkg:
- if not self.pkg.is_installed():
- return _("Package is not installed")
- else:
- return ListView._get_content(self)
- else:
- return "Huh?"
+ def _get_content (self):
+ if self.pkg:
+ if not self.pkg.is_installed():
+ return _("Package is not installed")
+ else:
+ return ListView._get_content(self)
+ else:
+ return "Huh?"
class HighlightView (gtksourceview2.View, LazyView):
- def __init__ (self, get_file_fn, languages = []):
- self.get_fn = get_file_fn
-
- man = gtksourceview2.LanguageManager()
-
- language = None
- old_lang = None
- for lang in languages:
- if old_lang:
- warning(_("No %(old)s language file installed. Falling back to %(new)s."), {"old" : old_lang, "new" : lang})
-
- language = man.get_language(lang)
- if language:
- break
- else:
- old_lang = lang
-
- if not language and old_lang:
- warning(_("No %(old)s language file installed. Disable highlighting."), {"old" : old_lang})
-
- buf = gtksourceview2.Buffer()
- buf.set_language(language)
-
- gtksourceview2.View.__init__(self, buf)
- LazyView.__init__(self)
-
- self.set_editable(False)
- self.set_cursor_visible(False)
-
- def set_text (self, text):
- self.get_buffer().set_text(text)
-
- def _get_content (self):
- try:
- with open(self.get_fn(self.pkg)) as f:
- return f.readlines()
- except IOError, e:
- return _("Error: %s") % e.strerror
-
+ def __init__ (self, get_file_fn, languages = []):
+ self.get_fn = get_file_fn
+
+ man = gtksourceview2.LanguageManager()
+
+ language = None
+ old_lang = None
+ for lang in languages:
+ if old_lang:
+ warning(_("No %(old)s language file installed. Falling back to %(new)s."), {"old" : old_lang, "new" : lang})
+
+ language = man.get_language(lang)
+ if language:
+ break
+ else:
+ old_lang = lang
+
+ if not language and old_lang:
+ warning(_("No %(old)s language file installed. Disable highlighting."), {"old" : old_lang})
+
+ buf = gtksourceview2.Buffer()
+ buf.set_language(language)
+
+ gtksourceview2.View.__init__(self, buf)
+ LazyView.__init__(self)
+
+ self.set_editable(False)
+ self.set_cursor_visible(False)
+
+ def set_text (self, text):
+ self.get_buffer().set_text(text)
+
+ def _get_content (self):
+ try:
+ with open(self.get_fn(self.pkg)) as f:
+ return f.readlines()
+ except IOError, e:
+ return _("Error: %s") % e.strerror
+
class LogView (logging.Handler):
- colors = (
- (logging.DEBUG, "debug", "blue"),
- (logging.INFO, "info", "green"),
- (logging.WARNING, "warning", "yellow"),
- (-1, "error", "red")
- )
-
- def __init__ (self, view):
- logging.Handler.__init__(self, logging.DEBUG)
-
- self.view = view
- self.buf = view.get_buffer()
-
- # set tags
- for lvl, name, color in self.colors:
- self.buf.create_tag("log_%s" % name, foreground = color,weight = pango.WEIGHT_BOLD)
-
- logging.getLogger("portatoLogger").addHandler(self)
-
- def emit (self, record):
-
- for lvl, name, color in self.colors:
- if lvl == -1 or record.levelno <= lvl:
- tag = "log_%s" % name
- break
-
- def _add():
- self.buf.insert_with_tags_by_name(self.buf.get_end_iter(), "* ", tag)
- self.buf.insert(self.buf.get_end_iter(), record.getMessage()+"\n")
-
- gobject.idle_add(_add) # logger might be called from another thread
+ colors = (
+ (logging.DEBUG, "debug", "blue"),
+ (logging.INFO, "info", "green"),
+ (logging.WARNING, "warning", "yellow"),
+ (-1, "error", "red")
+ )
+
+ def __init__ (self, view):
+ logging.Handler.__init__(self, logging.DEBUG)
+
+ self.view = view
+ self.buf = view.get_buffer()
+
+ # set tags
+ for lvl, name, color in self.colors:
+ self.buf.create_tag("log_%s" % name, foreground = color,weight = pango.WEIGHT_BOLD)
+
+ logging.getLogger("portatoLogger").addHandler(self)
+
+ def emit (self, record):
+
+ for lvl, name, color in self.colors:
+ if lvl == -1 or record.levelno <= lvl:
+ tag = "log_%s" % name
+ break
+
+ def _add():
+ self.buf.insert_with_tags_by_name(self.buf.get_end_iter(), "* ", tag)
+ self.buf.insert(self.buf.get_end_iter(), record.getMessage()+"\n")
+
+ gobject.idle_add(_add) # logger might be called from another thread
diff --git a/portato/gui/windows/about.py b/portato/gui/windows/about.py
index df724f3..21608c0 100644
--- a/portato/gui/windows/about.py
+++ b/portato/gui/windows/about.py
@@ -18,17 +18,17 @@ from .basic import AbstractDialog
from ...constants import VERSION, APP_ICON
class AboutWindow (AbstractDialog):
- """A window showing the "about"-informations."""
+ """A window showing the "about"-informations."""
- def __init__ (self, parent):
+ def __init__ (self, parent):
- AbstractDialog.__init__(self, parent)
+ AbstractDialog.__init__(self, parent)
- img = gtk.Image()
- img.set_from_file(APP_ICON)
+ img = gtk.Image()
+ img.set_from_file(APP_ICON)
- self.window.set_version(VERSION)
- self.window.set_logo(img.get_pixbuf())
+ self.window.set_version(VERSION)
+ self.window.set_logo(img.get_pixbuf())
- self.window.show_all()
+ self.window.show_all()
diff --git a/portato/gui/windows/basic.py b/portato/gui/windows/basic.py
index 09d0d7e..6d74858 100644
--- a/portato/gui/windows/basic.py
+++ b/portato/gui/windows/basic.py
@@ -27,101 +27,101 @@ gtk.glade.bindtextdomain (APP, LOCALE_DIR)
gtk.glade.textdomain (APP)
class WrappedTree (object):
- __slots__ = ("klass", "tree", "get_widget")
- def __init__ (self, klass, tree):
- self.tree = tree
- self.klass = klass
-
- def __getattribute__ (self, name):
- if name in WrappedTree.__slots__:
- return object.__getattribute__(self, name)
- else:
- return getattr(self.tree, name)
-
- def get_widget(self, name):
- w = self.tree.get_widget(name)
- if w is None:
- error("Widget '%s' could not be found in class '%s'.", name, self.klass)
- return w
+ __slots__ = ("klass", "tree", "get_widget")
+ def __init__ (self, klass, tree):
+ self.tree = tree
+ self.klass = klass
+
+ def __getattribute__ (self, name):
+ if name in WrappedTree.__slots__:
+ return object.__getattribute__(self, name)
+ else:
+ return getattr(self.tree, name)
+
+ def get_widget(self, name):
+ w = self.tree.get_widget(name)
+ if w is None:
+ error("Widget '%s' could not be found in class '%s'.", name, self.klass)
+ return w
class Window (object):
- def __init__ (self):
-
- if not hasattr(self, "__tree__"):
- self.__tree__ = self.__class__.__name__
-
- if not hasattr(self, "__window__"):
- self.__window__ = self.__class__.__name__
-
- if not hasattr(self, "__file__"):
- self.__file__ = self.__class__.__name__
-
- self.tree = self.get_tree(self.__tree__)
- self.tree.signal_autoconnect(self)
- self.window = self.tree.get_widget(self.__window__)
- self.window.set_icon_from_file(APP_ICON)
-
- @staticmethod
- def watch_cursor (func):
- """This is a decorator for functions being so time consuming, that it is appropriate to show the watch-cursor.
- @attention: this function relies on the gtk.Window-Object being stored as self.window"""
-
- @wraps(func)
- def wrapper (self, *args, **kwargs):
- ret = None
- def cb_idle():
- try:
- ret = func(self, *args, **kwargs)
- finally:
- self.window.window.set_cursor(None)
- return False
-
- watch = gtk.gdk.Cursor(gtk.gdk.WATCH)
- self.window.window.set_cursor(watch)
- gobject.idle_add(cb_idle)
- return ret
-
- return wrapper
-
- def get_tree (self, name):
- return WrappedTree(self.__class__.__name__, gtk.glade.XML(os.path.join(TEMPLATE_DIR, self.__file__+".glade"), name))
+ def __init__ (self):
+
+ if not hasattr(self, "__tree__"):
+ self.__tree__ = self.__class__.__name__
+
+ if not hasattr(self, "__window__"):
+ self.__window__ = self.__class__.__name__
+
+ if not hasattr(self, "__file__"):
+ self.__file__ = self.__class__.__name__
+
+ self.tree = self.get_tree(self.__tree__)
+ self.tree.signal_autoconnect(self)
+ self.window = self.tree.get_widget(self.__window__)
+ self.window.set_icon_from_file(APP_ICON)
+
+ @staticmethod
+ def watch_cursor (func):
+ """This is a decorator for functions being so time consuming, that it is appropriate to show the watch-cursor.
+ @attention: this function relies on the gtk.Window-Object being stored as self.window"""
+
+ @wraps(func)
+ def wrapper (self, *args, **kwargs):
+ ret = None
+ def cb_idle():
+ try:
+ ret = func(self, *args, **kwargs)
+ finally:
+ self.window.window.set_cursor(None)
+ return False
+
+ watch = gtk.gdk.Cursor(gtk.gdk.WATCH)
+ self.window.window.set_cursor(watch)
+ gobject.idle_add(cb_idle)
+ return ret
+
+ return wrapper
+
+ def get_tree (self, name):
+ return WrappedTree(self.__class__.__name__, gtk.glade.XML(os.path.join(TEMPLATE_DIR, self.__file__+".glade"), name))
class AbstractDialog (Window):
- """A class all our dialogs get derived from. It sets useful default vars and automatically handles the ESC-Button."""
-
- def __init__ (self, parent):
- """Constructor.
-
- @param parent: the parent window
- @type parent: gtk.Window"""
-
- Window.__init__(self)
-
- # set parent
- self.window.set_transient_for(parent)
- self.parent = parent
-
- # catch the ESC-key
- self.window.connect("key-press-event", self.cb_key_pressed)
-
- def cb_key_pressed (self, widget, event):
- """Closes the window if ESC is pressed."""
- keyname = gtk.gdk.keyval_name(event.keyval)
- if keyname == "Escape":
- self.close()
- return True
- else:
- return False
-
- def close (self, *args):
- self.window.destroy()
+ """A class all our dialogs get derived from. It sets useful default vars and automatically handles the ESC-Button."""
+
+ def __init__ (self, parent):
+ """Constructor.
+
+ @param parent: the parent window
+ @type parent: gtk.Window"""
+
+ Window.__init__(self)
+
+ # set parent
+ self.window.set_transient_for(parent)
+ self.parent = parent
+
+ # catch the ESC-key
+ self.window.connect("key-press-event", self.cb_key_pressed)
+
+ def cb_key_pressed (self, widget, event):
+ """Closes the window if ESC is pressed."""
+ keyname = gtk.gdk.keyval_name(event.keyval)
+ if keyname == "Escape":
+ self.close()
+ return True
+ else:
+ return False
+
+ def close (self, *args):
+ self.window.destroy()
class Popup (object):
- def __init__ (self, name, parent, file = "popups"):
- self.tree = gtk.glade.XML(os.path.join(TEMPLATE_DIR, file+".glade"), root = name)
- self.tree.signal_autoconnect(parent)
- self._popup = self.tree.get_widget(name)
+ def __init__ (self, name, parent, file = "popups"):
+ self.tree = gtk.glade.XML(os.path.join(TEMPLATE_DIR, file+".glade"), root = name)
+ self.tree.signal_autoconnect(parent)
+ self._popup = self.tree.get_widget(name)
- def popup (self, *args):
- self._popup.popup(*args)
+ def popup (self, *args):
+ self._popup.popup(*args)
diff --git a/portato/gui/windows/mailinfo.py b/portato/gui/windows/mailinfo.py
index 5d0a24c..22e750a 100644
--- a/portato/gui/windows/mailinfo.py
+++ b/portato/gui/windows/mailinfo.py
@@ -22,69 +22,69 @@ from ...helper import debug, info
from ...constants import VERSION
class MailInfoWindow (AbstractDialog):
- TO = "bugs@portato.necoro.net"
+ TO = "bugs@portato.necoro.net"
- def __init__ (self, parent, tb):
+ def __init__ (self, parent, tb):
- AbstractDialog.__init__(self, parent)
-
- self.tb = tb
- self.window.show_all()
+ AbstractDialog.__init__(self, parent)
+
+ self.tb = tb
+ self.window.show_all()
- def set_data (self):
- name = self.tree.get_widget("nameEntry").get_text()
- addr = self.tree.get_widget("mailEntry").get_text()
+ def set_data (self):
+ name = self.tree.get_widget("nameEntry").get_text()
+ addr = self.tree.get_widget("mailEntry").get_text()
- if not addr:
- addr = self.TO
+ if not addr:
+ addr = self.TO
- if name:
- fro = "%s <%s>" % (name, addr)
- else:
- fro = addr
+ if name:
+ fro = "%s <%s>" % (name, addr)
+ else:
+ fro = addr
- commentBuffer = self.tree.get_widget("commentEntry").get_buffer()
- text = commentBuffer.get_text(*commentBuffer.get_bounds())
+ commentBuffer = self.tree.get_widget("commentEntry").get_buffer()
+ text = commentBuffer.get_text(*commentBuffer.get_bounds())
- if text:
- text += "\n\n===========\n"
+ if text:
+ text += "\n\n===========\n"
- text += self.tb
+ text += self.tb
- message = """From: %s
+ message = """From: %s
To: %s
Subject: %s
%s""" % ( fro, self.TO, ("[Bug Report] Bug in Portato %s" % VERSION), text)
- self.addr = addr
- self.message = message
-
- def send (self):
- try:
- debug("Connecting to server")
- server = smtplib.SMTP("mail.necoro.eu")
- debug("Sending mail")
- try:
- try:
- server.sendmail(self.addr, self.TO, self.message)
- except smtplib.SMTPRecipientsRefused, e:
- info(_("An error occurred while sending. I think we were greylisted. The error: %s") % e)
- info(_("Retrying after waiting 60 seconds."))
- time.sleep(60)
- server.sendmail(self.addr, self.TO, self.message)
- debug("Sent")
- finally:
- server.quit()
- except socket.error, e:
- mail_failure_dialog("%s (Code: %s)" % (e.args[1], e.args[0]))
-
- def cb_cancel_clicked (self, *args):
-
- self.close()
- return True
-
- def cb_send_clicked (self, *args):
- self.set_data()
- GtkThread(target = self.send, name = "Mail Send Thread").start()
- self.close()
- return True
+ self.addr = addr
+ self.message = message
+
+ def send (self):
+ try:
+ debug("Connecting to server")
+ server = smtplib.SMTP("mail.necoro.eu")
+ debug("Sending mail")
+ try:
+ try:
+ server.sendmail(self.addr, self.TO, self.message)
+ except smtplib.SMTPRecipientsRefused, e:
+ info(_("An error occurred while sending. I think we were greylisted. The error: %s") % e)
+ info(_("Retrying after waiting 60 seconds."))
+ time.sleep(60)
+ server.sendmail(self.addr, self.TO, self.message)
+ debug("Sent")
+ finally:
+ server.quit()
+ except socket.error, e:
+ mail_failure_dialog("%s (Code: %s)" % (e.args[1], e.args[0]))
+
+ def cb_cancel_clicked (self, *args):
+
+ self.close()
+ return True
+
+ def cb_send_clicked (self, *args):
+ self.set_data()
+ GtkThread(target = self.send, name = "Mail Send Thread").start()
+ self.close()
+ return True
diff --git a/portato/gui/windows/main.py b/portato/gui/windows/main.py
index 1b7c0f0..fca4535 100644
--- a/portato/gui/windows/main.py
+++ b/portato/gui/windows/main.py
@@ -36,8 +36,8 @@ from ..session import SESSION_VERSION, SessionException, OldSessionException, Ne
from ..wrapper import GtkTree, GtkConsole
from ..views import LogView, HighlightView, InstalledOnlyView
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)
+ nothing_found_dialog, queue_not_empty_dialog, remove_deps_dialog,
+ remove_queue_dialog, remove_updates_dialog, unmask_dialog)
# even more GUI stuff
from .basic import Window, Popup
@@ -48,1744 +48,1744 @@ from .search import SearchWindow
from .update import UpdateWindow
class PackageTable:
- """A window with data about a specfic package."""
-
- def __init__ (self, main):
- """Build up window contents.
-
- @param main: the main window
- @type main: MainWindow"""
-
- self.main = main
- self.tree = main.tree
- self.window = main.window
- self.tree.signal_autoconnect(self)
-
- # all the package data is in this one VB
- self.vb = self.tree.get_widget("packageVB")
-
- # the notebook
- self.notebook = self.tree.get_widget("packageNotebook")
-
- # chechboxes
- self.installedCheck = self.tree.get_widget("installedCheck")
- self.maskedCheck = self.tree.get_widget("maskedCheck")
- self.testingCheck = self.tree.get_widget("testingCheck")
- self.maskedLabel = self.tree.get_widget("maskedLabel")
-
- # labels
- generalVB = self.tree.get_widget("generalVB")
- generalVB.modify_bg(gtk.STATE_NORMAL, get_color(self.main.cfg, "packagetable"))
-
- self.nameLabel = self.tree.get_widget("nameLabel")
- self.descLabel = self.tree.get_widget("descLabel")
- self.overlayLabel = self.tree.get_widget("overlayLabel")
- self.overlayLL = self.tree.get_widget("overlayLabelLabel")
- self.licenseLabel = self.tree.get_widget("licenseLabel")
- self.linkBox = self.tree.get_widget("linkBox")
- self.notInSysLabel = self.tree.get_widget("notInSysLabel")
- self.missingLabel = self.tree.get_widget("missingLabel")
- self.useFlagsLabel = self.tree.get_widget("useFlagsLabel")
- self.useFlagsLL = self.tree.get_widget("useFlagsLabelLabel")
-
- # buttons
- self.emergeBtn = self.tree.get_widget("pkgEmergeBtn")
- self.unmergeBtn = self.tree.get_widget("pkgUnmergeBtn")
- self.revertBtn = self.tree.get_widget("pkgRevertBtn")
-
- # useList
- self.useList = self.tree.get_widget("useList")
- self.build_use_list()
-
- # depList
- self.depList = self.tree.get_widget("dependencyList")
- self.build_dep_list()
-
- # views
- self.ebuildView = self.tree.get_widget("ebuildScroll").get_child()
- self.changelogView = self.tree.get_widget("changelogScroll").get_child()
- self.filesView = self.tree.get_widget("filesScroll").get_child()
-
- # icons
- self.icons = {}
- self.icons["use"] = self.window.render_icon(gtk.STOCK_REMOVE, gtk.ICON_SIZE_MENU)
- self.icons["installed"] = self.window.render_icon(gtk.STOCK_YES, gtk.ICON_SIZE_MENU)
- self.icons["or"] = self.window.render_icon(gtk.STOCK_MEDIA_PAUSE, gtk.ICON_SIZE_MENU)
- self.icons["block"] = self.window.render_icon(gtk.STOCK_NO, gtk.ICON_SIZE_MENU)
-
- def update (self, pkg, queue = None, doEmerge = True, instantChange = False, type = None):
- """Updates the table to show the contents for the package.
-
- @param pkg: the selected package
- @type pkg: Package
- @param queue: emerge-queue (if None the emerge-buttons are disabled)
- @type queue: EmergeQueue
- @param doEmerge: if False, the emerge buttons are disabled
- @type doEmerge: boolean
- @param instantChange: if True the changed keywords are updated instantly
- @type instantChange: boolean
- @param type: the type of the queue this package is in; if None there is no queue :)
- @type type: string"""
-
- self.pkg = pkg
- self.queue = queue
- self.doEmerge = doEmerge
- self.instantChange = instantChange
- self.type = type
-
- if not self.queue or not self.doEmerge:
- self.emergeBtn.set_sensitive(False)
- self.unmergeBtn.set_sensitive(False)
-
- # current status
- self._update_table()
- self.vb.show_all()
-
- def hide (self):
- self.vb.hide_all()
-
- def set_labels (self):
- pkg = self.pkg
-
- # name
- self.nameLabel.set_markup("<b>%s</b>" % pkg.get_cpv())
-
- # description
- desc = pkg.get_package_settings("DESCRIPTION") or _("<no description>")
- self.descLabel.set_label(desc)
-
- # overlay
- if pkg.is_in_overlay():
- self.overlayLabel.set_label(pkg.get_overlay_path())
- self.overlayLabel.show()
- self.overlayLL.show()
- else:
- self.overlayLabel.hide()
- self.overlayLL.hide()
-
- # license
- self.licenseLabel.set_label(pkg.get_package_settings("LICENSE"))
-
- # link
- for c in self.linkBox.get_children():
- self.linkBox.remove(c)
-
- text = pkg.get_package_settings("HOMEPAGE")
- texts = text.split(" ")
- ftexts = []
-
- for count, t in enumerate(texts):
- if not t.startswith(("http", "ftp")):
- if count == 0:
- error(_("The first homepage part does not start with 'http' or 'ftp'."))
- ftexts.append(t)
- continue
- else:
- info(_("Blank inside homepage."))
- ftexts[-1] += " %s" % t
- else:
- ftexts.append(t)
-
- for t in ftexts:
- link = gtk.LinkButton(t)
- link.set_alignment(0.0, 0.5)
- link.set_border_width(0)
- self.linkBox.add(link)
-
- # useflags
- flaglist = list(itt.ifilterfalse(pkg.use_expanded, pkg.get_iuse_flags()))
- flaglist.sort()
- flags = ", ".join(flaglist)
-
- if flags:
- self.useFlagsLL.show()
- self.useFlagsLabel.show()
- self.useFlagsLabel.set_label(flags)
- else:
- self.useFlagsLL.hide()
- self.useFlagsLabel.hide()
-
- def fill_dep_list(self):
-
- store = self.depList.get_model()
-
- def add (tree, it):
-
- def get_icon (dep):
- if dep.satisfied:
- return self.icons["installed"]
- elif dep.dep[0] == "!":
- return self.icons["block"]
- else:
- return None
-
- # useflags
- for use, usetree in tree.flags.iteritems():
- if use[0] == "!":
- usestring = _("If '%s' is disabled") % use[1:]
- else:
- usestring = _("If '%s' is enabled") % use
- useit = store.append(it, [self.icons["use"], usestring])
- add(usetree, useit)
-
- # ORs
- ordeps = (dep for dep in tree.deps if isinstance(dep, dependency.OrDependency))
-
- for ordep in ordeps:
- orit = store.append(it, [self.icons["or"], _("One of the following")])
-
- for dep in ordep.dep:
- if isinstance(dep, dependency.AllOfDependency): # a list inside or
- allit = store.append(orit, [None, _("All of the following")])
- for adep in dep.dep:
- store.append(allit, [get_icon(adep), adep.dep])
- else:
- store.append(orit, [get_icon(dep), dep.dep])
-
- # normal
- def sort_key (x):
- split = system.split_cpv(x.dep)
-
- if split is None: # split_cpv returns None if this is only a CP; we assume there are only valid deps
- return x.dep
- else:
- return "/".join(split[0:2])
-
- ndeps = [dep for dep in tree.deps if not isinstance(dep, dependency.OrDependency)]
- ndeps.sort(key = sort_key)
- for dep in ndeps:
- store.append(it, [get_icon(dep), dep.dep])
-
- try:
- deptree = self.pkg.get_dependencies()
- except AssertionError:
- w = _("Can't display dependencies: This package has an unsupported dependency string.")
- error(w)
- store.append(None, [None, w])
- else:
- add(deptree, None)
-
- def fill_use_list(self):
-
- pkg = self.pkg
- pkg_flags = pkg.get_iuse_flags()
- pkg_flags.sort()
-
- actual_exp = None
- actual_exp_it = None
-
- euse = pkg.get_actual_use_flags()
- instuse = pkg.get_installed_use_flags()
-
- store = self.useList.get_model()
-
- for use in pkg_flags:
- exp = pkg.use_expanded(use, suggest = actual_exp)
- if exp is not None:
- if exp != actual_exp:
- actual_exp_it = store.append(None, [None, None, exp, "<i>%s</i>" % _("This is an expanded use flag and cannot be selected")])
- actual_exp = exp
- else:
- actual_exp_it = None
- actual_exp = None
-
- enabled = use in euse
- installed = use in instuse
- store.append(actual_exp_it, [enabled, installed, use, system.get_use_desc(use, self.pkg.get_cp())])
-
- def build_dep_list (self):
- store = gtk.TreeStore(gtk.gdk.Pixbuf, str)
-
- self.depList.set_model(store)
-
- col = gtk.TreeViewColumn()
-
- cell = gtk.CellRendererPixbuf()
- col.pack_start(cell, False)
- col.add_attribute(cell, "pixbuf", 0)
-
- cell = gtk.CellRendererText()
- col.pack_start(cell, True)
- col.add_attribute(cell, "text", 1)
-
- self.depList.append_column(col)
-
- def build_use_list (self):
- """Builds the useList."""
- store = gtk.TreeStore(bool, bool, str, str)
- self.useList.set_model(store)
-
- # build view
- cell = gtk.CellRendererText()
- iCell = gtk.CellRendererToggle()
- iCell.set_property("activatable", False)
- tCell = gtk.CellRendererToggle()
- tCell.set_property("activatable", True)
- tCell.connect("toggled", self.cb_use_flag_toggled, store)
- self.useList.append_column(gtk.TreeViewColumn(_("Enabled"), tCell, active = 0))
- self.useList.append_column(gtk.TreeViewColumn(_("Installed"), iCell, active = 1))
- self.useList.append_column(gtk.TreeViewColumn(_("Flag"), cell, text = 2))
- self.useList.append_column(gtk.TreeViewColumn(_("Description"), cell, markup = 3))
-
- self.useList.set_search_column(2)
- self.useList.set_enable_tree_lines(True)
-
- def _update_keywords (self, emerge, update = False):
- if emerge:
- type = "install" if not self.type else self.type
- try:
- try:
- self.queue.append(self.pkg.get_cpv(), type = type, update = update)
- except PackageNotFoundException, e:
- if unmask_dialog(e[0]) == gtk.RESPONSE_YES:
- self.queue.append(self.pkg.get_cpv(), type = type, unmask = True, update = update)
- except BlockedException, e:
- blocked_dialog(e[0], e[1])
- else:
- try:
- self.queue.append(self.pkg.get_cpv(), type = "uninstall")
- except PackageNotFoundException, e:
- error(_("Package could not be found: %s"), e[0])
- #masked_dialog(e[0])
-
- def _update_table (self, *args):
-
- pkg = self.pkg
-
- # set the views
- for v in (self.ebuildView, self.changelogView, self.filesView):
- v.update(pkg, force = self.notebook.get_nth_page(self.notebook.get_current_page()) == v.get_parent())
-
- # set the labels
- self.set_labels()
-
- # set use list
- self.useList.get_model().clear()
- self.useList.columns_autosize()
- self.fill_use_list()
-
- # set dep list
- self.depList.get_model().clear()
- self.useList.columns_autosize()
- self.fill_dep_list()
-
- #
- # rebuild the buttons and checkboxes in all the different manners which are possible
- #
- if (not pkg.is_in_system()) or pkg.is_missing_keyword():
- if not pkg.is_in_system():
- self.missingLabel.hide()
- self.notInSysLabel.show()
- else: # missing keyword
- self.missingLabel.show()
- self.notInSysLabel.hide()
-#
- self.installedCheck.hide()
- self.maskedCheck.hide()
- self.maskedLabel.hide()
- self.testingCheck.hide()
- self.emergeBtn.set_sensitive(False)
- else: # normal package
- self.missingLabel.hide()
- self.notInSysLabel.hide()
- self.installedCheck.show()
- self.maskedCheck.show()
- self.maskedLabel.show()
- self.testingCheck.show()
- if self.doEmerge:
- self.emergeBtn.set_sensitive(True)
- self.installedCheck.set_active(pkg.is_installed())
-
- reason = pkg.get_masking_reason() or " "
- if pkg.is_masked(use_changed = False) and not pkg.is_masked(use_changed = True):
- self.maskedCheck.set_label("<i>(%s)</i>" % _("Masked"))
- self.maskedCheck.get_child().set_use_markup(True)
- else:
- self.maskedCheck.set_label(_("Masked"))
-
- if pkg.is_locally_masked():
- self.maskedCheck.set_label("<b>%s</b>" % _("Masked"))
- self.maskedCheck.get_child().set_use_markup(True)
- self.maskedCheck.set_active(True)
- reason = _("Masked by user")
- else:
- self.maskedCheck.set_active(pkg.is_masked(use_changed = False))
-
- if reason:
- self.maskedLabel.set_label(reason)
-
- if pkg.is_testing(use_keywords = False) and not pkg.is_testing(use_keywords = True):
- self.testingCheck.set_label("<i>(%s)</i>" % _("Testing"))
- self.testingCheck.get_child().set_use_markup(True)
- else:
- self.testingCheck.set_label(_("Testing"))
-
- self.testingCheck.set_active(pkg.is_testing(use_keywords = False))
-
- if self.doEmerge:
- # set emerge-button-label
- if not pkg.is_installed():
- self.unmergeBtn.set_sensitive(False)
- else:
- self.unmergeBtn.set_sensitive(True)
-
- self.vb.show_all()
- return True
-
- def cb_button_pressed (self, b, event):
- """Callback for pressed checkboxes. Just quits the event-loop - no redrawing."""
- if not isinstance(b, gtk.CellRendererToggle):
- b.emit_stop_by_name("button-press-event")
- return True
-
- def cb_package_revert_clicked (self, button):
- """Callback for pressed revert-button."""
- self.pkg.remove_new_use_flags()
- self.pkg.remove_new_masked()
- self.pkg.remove_new_testing()
- self._update_table()
- if self.instantChange:
- self._update_keywords(True, update = True)
- return True
-
- def cb_package_emerge_clicked (self, button):
- """Callback for pressed emerge-button. Adds the package to the EmergeQueue."""
- self._update_keywords(True)
- self.main.sysNotebook.set_current_page(self.main.QUEUE_PAGE)
- return True
-
- def cb_package_unmerge_clicked (self, button):
- """Callback for pressed unmerge-button clicked. Adds the package to the UnmergeQueue."""
- self._update_keywords(False)
- self.main.sysNotebook.set_current_page(self.main.QUEUE_PAGE)
- return True
-
- def cb_testing_toggled (self, button):
- """Callback for toggled testing-checkbox."""
- status = button.get_active()
-
- # end of recursion :)
- if self.pkg.is_testing(use_keywords = False) == status:
- return False
-
- # if the package is not testing - don't allow to set it as such
- if not self.pkg.is_testing(use_keywords = False):
- button.set_active(False)
- return True
-
- # re-set to testing status
- if not self.pkg.is_testing(use_keywords = True):
- self.pkg.set_testing(False)
- button.set_label(_("Testing"))
- button.set_active(True)
- else: # disable testing
- self.pkg.set_testing(True)
- button.set_label("<i>(%s)</i>" % _("Testing"))
- button.get_child().set_use_markup(True)
- button.set_active(True)
-
- if self.instantChange:
- self._update_keywords(True, update = True)
-
- return True
-
- def cb_masked_toggled (self, button):
- """Callback for toggled masking-checkbox."""
- status = button.get_active()
- pkg = self.pkg
-
- if pkg.is_masked(use_changed = False) == status and not pkg.is_locally_masked():
- return False
-
- if pkg.is_locally_masked() and status:
- return False
-
- if not pkg.is_masked(use_changed = True):
- pkg.set_masked(True)
- if pkg.is_locally_masked():
- button.set_label("<b>%s</b>" % _("Masked"))
- button.get_child().set_use_markup(True)
- self.maskedLabel.set_label(_("Masked by user"))
- else:
- button.set_label(_("Masked"))
-
- button.set_active(True)
- else:
- locally = pkg.is_locally_masked()
- pkg.set_masked(False)
- if pkg.is_masked(use_changed=False) and not locally:
- button.set_label("<i>(%s)</i>" % _("Masked"))
- button.get_child().set_use_markup(True)
- button.set_active(True)
- else:
- button.set_label(_("Masked"))
- self.maskedLabel.set_label("")
-
- if self.instantChange:
- self._update_keywords(True, update = True)
-
- return True
-
- def cb_use_flag_toggled (self, cell, path, store):
- """Callback for a toggled use-flag button."""
- flag = store[path][2]
- pkg = self.pkg
-
- if pkg.use_expanded(flag): # ignore expanded flags
- return False
-
- store[path][0] = not store[path][0]
- prefix = ""
- if not store[path][0]:
- prefix = "-"
-
- pkg.set_use_flag(prefix+flag)
- if self.instantChange:
- self._update_keywords(True, update = True)
-
- return True
+ """A window with data about a specfic package."""
+
+ def __init__ (self, main):
+ """Build up window contents.
+
+ @param main: the main window
+ @type main: MainWindow"""
+
+ self.main = main
+ self.tree = main.tree
+ self.window = main.window
+ self.tree.signal_autoconnect(self)
+
+ # all the package data is in this one VB
+ self.vb = self.tree.get_widget("packageVB")
+
+ # the notebook
+ self.notebook = self.tree.get_widget("packageNotebook")
+
+ # chechboxes
+ self.installedCheck = self.tree.get_widget("installedCheck")
+ self.maskedCheck = self.tree.get_widget("maskedCheck")
+ self.testingCheck = self.tree.get_widget("testingCheck")
+ self.maskedLabel = self.tree.get_widget("maskedLabel")
+
+ # labels
+ generalVB = self.tree.get_widget("generalVB")
+ generalVB.modify_bg(gtk.STATE_NORMAL, get_color(self.main.cfg, "packagetable"))
+
+ self.nameLabel = self.tree.get_widget("nameLabel")
+ self.descLabel = self.tree.get_widget("descLabel")
+ self.overlayLabel = self.tree.get_widget("overlayLabel")
+ self.overlayLL = self.tree.get_widget("overlayLabelLabel")
+ self.licenseLabel = self.tree.get_widget("licenseLabel")
+ self.linkBox = self.tree.get_widget("linkBox")
+ self.notInSysLabel = self.tree.get_widget("notInSysLabel")
+ self.missingLabel = self.tree.get_widget("missingLabel")
+ self.useFlagsLabel = self.tree.get_widget("useFlagsLabel")
+ self.useFlagsLL = self.tree.get_widget("useFlagsLabelLabel")
+
+ # buttons
+ self.emergeBtn = self.tree.get_widget("pkgEmergeBtn")
+ self.unmergeBtn = self.tree.get_widget("pkgUnmergeBtn")
+ self.revertBtn = self.tree.get_widget("pkgRevertBtn")
+
+ # useList
+ self.useList = self.tree.get_widget("useList")
+ self.build_use_list()
+
+ # depList
+ self.depList = self.tree.get_widget("dependencyList")
+ self.build_dep_list()
+
+ # views
+ self.ebuildView = self.tree.get_widget("ebuildScroll").get_child()
+ self.changelogView = self.tree.get_widget("changelogScroll").get_child()
+ self.filesView = self.tree.get_widget("filesScroll").get_child()
+
+ # icons
+ self.icons = {}
+ self.icons["use"] = self.window.render_icon(gtk.STOCK_REMOVE, gtk.ICON_SIZE_MENU)
+ self.icons["installed"] = self.window.render_icon(gtk.STOCK_YES, gtk.ICON_SIZE_MENU)
+ self.icons["or"] = self.window.render_icon(gtk.STOCK_MEDIA_PAUSE, gtk.ICON_SIZE_MENU)
+ self.icons["block"] = self.window.render_icon(gtk.STOCK_NO, gtk.ICON_SIZE_MENU)
+
+ def update (self, pkg, queue = None, doEmerge = True, instantChange = False, type = None):
+ """Updates the table to show the contents for the package.
+
+ @param pkg: the selected package
+ @type pkg: Package
+ @param queue: emerge-queue (if None the emerge-buttons are disabled)
+ @type queue: EmergeQueue
+ @param doEmerge: if False, the emerge buttons are disabled
+ @type doEmerge: boolean
+ @param instantChange: if True the changed keywords are updated instantly
+ @type instantChange: boolean
+ @param type: the type of the queue this package is in; if None there is no queue :)
+ @type type: string"""
+
+ self.pkg = pkg
+ self.queue = queue
+ self.doEmerge = doEmerge
+ self.instantChange = instantChange
+ self.type = type
+
+ if not self.queue or not self.doEmerge:
+ self.emergeBtn.set_sensitive(False)
+ self.unmergeBtn.set_sensitive(False)
+
+ # current status
+ self._update_table()
+ self.vb.show_all()
+
+ def hide (self):
+ self.vb.hide_all()
+
+ def set_labels (self):
+ pkg = self.pkg
+
+ # name
+ self.nameLabel.set_markup("<b>%s</b>" % pkg.get_cpv())
+
+ # description
+ desc = pkg.get_package_settings("DESCRIPTION") or _("<no description>")
+ self.descLabel.set_label(desc)
+
+ # overlay
+ if pkg.is_in_overlay():
+ self.overlayLabel.set_label(pkg.get_overlay_path())
+ self.overlayLabel.show()
+ self.overlayLL.show()
+ else:
+ self.overlayLabel.hide()
+ self.overlayLL.hide()
+
+ # license
+ self.licenseLabel.set_label(pkg.get_package_settings("LICENSE"))
+
+ # link
+ for c in self.linkBox.get_children():
+ self.linkBox.remove(c)
+
+ text = pkg.get_package_settings("HOMEPAGE")
+ texts = text.split(" ")
+ ftexts = []
+
+ for count, t in enumerate(texts):
+ if not t.startswith(("http", "ftp")):
+ if count == 0:
+ error(_("The first homepage part does not start with 'http' or 'ftp'."))
+ ftexts.append(t)
+ continue
+ else:
+ info(_("Blank inside homepage."))
+ ftexts[-1] += " %s" % t
+ else:
+ ftexts.append(t)
+
+ for t in ftexts:
+ link = gtk.LinkButton(t)
+ link.set_alignment(0.0, 0.5)
+ link.set_border_width(0)
+ self.linkBox.add(link)
+
+ # useflags
+ flaglist = list(itt.ifilterfalse(pkg.use_expanded, pkg.get_iuse_flags()))
+ flaglist.sort()
+ flags = ", ".join(flaglist)
+
+ if flags:
+ self.useFlagsLL.show()
+ self.useFlagsLabel.show()
+ self.useFlagsLabel.set_label(flags)
+ else:
+ self.useFlagsLL.hide()
+ self.useFlagsLabel.hide()
+
+ def fill_dep_list(self):
+
+ store = self.depList.get_model()
+
+ def add (tree, it):
+
+ def get_icon (dep):
+ if dep.satisfied:
+ return self.icons["installed"]
+ elif dep.dep[0] == "!":
+ return self.icons["block"]
+ else:
+ return None
+
+ # useflags
+ for use, usetree in tree.flags.iteritems():
+ if use[0] == "!":
+ usestring = _("If '%s' is disabled") % use[1:]
+ else:
+ usestring = _("If '%s' is enabled") % use
+ useit = store.append(it, [self.icons["use"], usestring])
+ add(usetree, useit)
+
+ # ORs
+ ordeps = (dep for dep in tree.deps if isinstance(dep, dependency.OrDependency))
+
+ for ordep in ordeps:
+ orit = store.append(it, [self.icons["or"], _("One of the following")])
+
+ for dep in ordep.dep:
+ if isinstance(dep, dependency.AllOfDependency): # a list inside or
+ allit = store.append(orit, [None, _("All of the following")])
+ for adep in dep.dep:
+ store.append(allit, [get_icon(adep), adep.dep])
+ else:
+ store.append(orit, [get_icon(dep), dep.dep])
+
+ # normal
+ def sort_key (x):
+ split = system.split_cpv(x.dep)
+
+ if split is None: # split_cpv returns None if this is only a CP; we assume there are only valid deps
+ return x.dep
+ else:
+ return "/".join(split[0:2])
+
+ ndeps = [dep for dep in tree.deps if not isinstance(dep, dependency.OrDependency)]
+ ndeps.sort(key = sort_key)
+ for dep in ndeps:
+ store.append(it, [get_icon(dep), dep.dep])
+
+ try:
+ deptree = self.pkg.get_dependencies()
+ except AssertionError:
+ w = _("Can't display dependencies: This package has an unsupported dependency string.")
+ error(w)
+ store.append(None, [None, w])
+ else:
+ add(deptree, None)
+
+ def fill_use_list(self):
+
+ pkg = self.pkg
+ pkg_flags = pkg.get_iuse_flags()
+ pkg_flags.sort()
+
+ actual_exp = None
+ actual_exp_it = None
+
+ euse = pkg.get_actual_use_flags()
+ instuse = pkg.get_installed_use_flags()
+
+ store = self.useList.get_model()
+
+ for use in pkg_flags:
+ exp = pkg.use_expanded(use, suggest = actual_exp)
+ if exp is not None:
+ if exp != actual_exp:
+ actual_exp_it = store.append(None, [None, None, exp, "<i>%s</i>" % _("This is an expanded use flag and cannot be selected")])
+ actual_exp = exp
+ else:
+ actual_exp_it = None
+ actual_exp = None
+
+ enabled = use in euse
+ installed = use in instuse
+ store.append(actual_exp_it, [enabled, installed, use, system.get_use_desc(use, self.pkg.get_cp())])
+
+ def build_dep_list (self):
+ store = gtk.TreeStore(gtk.gdk.Pixbuf, str)
+
+ self.depList.set_model(store)
+
+ col = gtk.TreeViewColumn()
+
+ cell = gtk.CellRendererPixbuf()
+ col.pack_start(cell, False)
+ col.add_attribute(cell, "pixbuf", 0)
+
+ cell = gtk.CellRendererText()
+ col.pack_start(cell, True)
+ col.add_attribute(cell, "text", 1)
+
+ self.depList.append_column(col)
+
+ def build_use_list (self):
+ """Builds the useList."""
+ store = gtk.TreeStore(bool, bool, str, str)
+ self.useList.set_model(store)
+
+ # build view
+ cell = gtk.CellRendererText()
+ iCell = gtk.CellRendererToggle()
+ iCell.set_property("activatable", False)
+ tCell = gtk.CellRendererToggle()
+ tCell.set_property("activatable", True)
+ tCell.connect("toggled", self.cb_use_flag_toggled, store)
+ self.useList.append_column(gtk.TreeViewColumn(_("Enabled"), tCell, active = 0))
+ self.useList.append_column(gtk.TreeViewColumn(_("Installed"), iCell, active = 1))
+ self.useList.append_column(gtk.TreeViewColumn(_("Flag"), cell, text = 2))
+ self.useList.append_column(gtk.TreeViewColumn(_("Description"), cell, markup = 3))
+
+ self.useList.set_search_column(2)
+ self.useList.set_enable_tree_lines(True)
+
+ def _update_keywords (self, emerge, update = False):
+ if emerge:
+ type = "install" if not self.type else self.type
+ try:
+ try:
+ self.queue.append(self.pkg.get_cpv(), type = type, update = update)
+ except PackageNotFoundException, e:
+ if unmask_dialog(e[0]) == gtk.RESPONSE_YES:
+ self.queue.append(self.pkg.get_cpv(), type = type, unmask = True, update = update)
+ except BlockedException, e:
+ blocked_dialog(e[0], e[1])
+ else:
+ try:
+ self.queue.append(self.pkg.get_cpv(), type = "uninstall")
+ except PackageNotFoundException, e:
+ error(_("Package could not be found: %s"), e[0])
+ #masked_dialog(e[0])
+
+ def _update_table (self, *args):
+
+ pkg = self.pkg
+
+ # set the views
+ for v in (self.ebuildView, self.changelogView, self.filesView):
+ v.update(pkg, force = self.notebook.get_nth_page(self.notebook.get_current_page()) == v.get_parent())
+
+ # set the labels
+ self.set_labels()
+
+ # set use list
+ self.useList.get_model().clear()
+ self.useList.columns_autosize()
+ self.fill_use_list()
+
+ # set dep list
+ self.depList.get_model().clear()
+ self.useList.columns_autosize()
+ self.fill_dep_list()
+
+ #
+ # rebuild the buttons and checkboxes in all the different manners which are possible
+ #
+ if (not pkg.is_in_system()) or pkg.is_missing_keyword():
+ if not pkg.is_in_system():
+ self.missingLabel.hide()
+ self.notInSysLabel.show()
+ else: # missing keyword
+ self.missingLabel.show()
+ self.notInSysLabel.hide()
+#
+ self.installedCheck.hide()
+ self.maskedCheck.hide()
+ self.maskedLabel.hide()
+ self.testingCheck.hide()
+ self.emergeBtn.set_sensitive(False)
+ else: # normal package
+ self.missingLabel.hide()
+ self.notInSysLabel.hide()
+ self.installedCheck.show()
+ self.maskedCheck.show()
+ self.maskedLabel.show()
+ self.testingCheck.show()
+ if self.doEmerge:
+ self.emergeBtn.set_sensitive(True)
+ self.installedCheck.set_active(pkg.is_installed())
+
+ reason = pkg.get_masking_reason() or " "
+ if pkg.is_masked(use_changed = False) and not pkg.is_masked(use_changed = True):
+ self.maskedCheck.set_label("<i>(%s)</i>" % _("Masked"))
+ self.maskedCheck.get_child().set_use_markup(True)
+ else:
+ self.maskedCheck.set_label(_("Masked"))
+
+ if pkg.is_locally_masked():
+ self.maskedCheck.set_label("<b>%s</b>" % _("Masked"))
+ self.maskedCheck.get_child().set_use_markup(True)
+ self.maskedCheck.set_active(True)
+ reason = _("Masked by user")
+ else:
+ self.maskedCheck.set_active(pkg.is_masked(use_changed = False))
+
+ if reason:
+ self.maskedLabel.set_label(reason)
+
+ if pkg.is_testing(use_keywords = False) and not pkg.is_testing(use_keywords = True):
+ self.testingCheck.set_label("<i>(%s)</i>" % _("Testing"))
+ self.testingCheck.get_child().set_use_markup(True)
+ else:
+ self.testingCheck.set_label(_("Testing"))
+
+ self.testingCheck.set_active(pkg.is_testing(use_keywords = False))
+
+ if self.doEmerge:
+ # set emerge-button-label
+ if not pkg.is_installed():
+ self.unmergeBtn.set_sensitive(False)
+ else:
+ self.unmergeBtn.set_sensitive(True)
+
+ self.vb.show_all()
+ return True
+
+ def cb_button_pressed (self, b, event):
+ """Callback for pressed checkboxes. Just quits the event-loop - no redrawing."""
+ if not isinstance(b, gtk.CellRendererToggle):
+ b.emit_stop_by_name("button-press-event")
+ return True
+
+ def cb_package_revert_clicked (self, button):
+ """Callback for pressed revert-button."""
+ self.pkg.remove_new_use_flags()
+ self.pkg.remove_new_masked()
+ self.pkg.remove_new_testing()
+ self._update_table()
+ if self.instantChange:
+ self._update_keywords(True, update = True)
+ return True
+
+ def cb_package_emerge_clicked (self, button):
+ """Callback for pressed emerge-button. Adds the package to the EmergeQueue."""
+ self._update_keywords(True)
+ self.main.sysNotebook.set_current_page(self.main.QUEUE_PAGE)
+ return True
+
+ def cb_package_unmerge_clicked (self, button):
+ """Callback for pressed unmerge-button clicked. Adds the package to the UnmergeQueue."""
+ self._update_keywords(False)
+ self.main.sysNotebook.set_current_page(self.main.QUEUE_PAGE)
+ return True
+
+ def cb_testing_toggled (self, button):
+ """Callback for toggled testing-checkbox."""
+ status = button.get_active()
+
+ # end of recursion :)
+ if self.pkg.is_testing(use_keywords = False) == status:
+ return False
+
+ # if the package is not testing - don't allow to set it as such
+ if not self.pkg.is_testing(use_keywords = False):
+ button.set_active(False)
+ return True
+
+ # re-set to testing status
+ if not self.pkg.is_testing(use_keywords = True):
+ self.pkg.set_testing(False)
+ button.set_label(_("Testing"))
+ button.set_active(True)
+ else: # disable testing
+ self.pkg.set_testing(True)
+ button.set_label("<i>(%s)</i>" % _("Testing"))
+ button.get_child().set_use_markup(True)
+ button.set_active(True)
+
+ if self.instantChange:
+ self._update_keywords(True, update = True)
+
+ return True
+
+ def cb_masked_toggled (self, button):
+ """Callback for toggled masking-checkbox."""
+ status = button.get_active()
+ pkg = self.pkg
+
+ if pkg.is_masked(use_changed = False) == status and not pkg.is_locally_masked():
+ return False
+
+ if pkg.is_locally_masked() and status:
+ return False
+
+ if not pkg.is_masked(use_changed = True):
+ pkg.set_masked(True)
+ if pkg.is_locally_masked():
+ button.set_label("<b>%s</b>" % _("Masked"))
+ button.get_child().set_use_markup(True)
+ self.maskedLabel.set_label(_("Masked by user"))
+ else:
+ button.set_label(_("Masked"))
+
+ button.set_active(True)
+ else:
+ locally = pkg.is_locally_masked()
+ pkg.set_masked(False)
+ if pkg.is_masked(use_changed=False) and not locally:
+ button.set_label("<i>(%s)</i>" % _("Masked"))
+ button.get_child().set_use_markup(True)
+ button.set_active(True)
+ else:
+ button.set_label(_("Masked"))
+ self.maskedLabel.set_label("")
+
+ if self.instantChange:
+ self._update_keywords(True, update = True)
+
+ return True
+
+ def cb_use_flag_toggled (self, cell, path, store):
+ """Callback for a toggled use-flag button."""
+ flag = store[path][2]
+ pkg = self.pkg
+
+ if pkg.use_expanded(flag): # ignore expanded flags
+ return False
+
+ store[path][0] = not store[path][0]
+ prefix = ""
+ if not store[path][0]:
+ prefix = "-"
+
+ pkg.set_use_flag(prefix+flag)
+ if self.instantChange:
+ self._update_keywords(True, update = True)
+
+ return True
class MainWindow (Window):
- """
- Application main window.
- """
-
- # NOTEBOOK PAGE CONSTANTS
- (
- QUEUE_PAGE,
- CONSOLE_PAGE,
- LOG_PAGE
- ) = range(3)
-
- def __init__ (self, splash = None):
- """
- Build up window.
-
- @param splash: the splash screen =)
- @type splash: SplashScreen
- """
-
- if splash is None:
- splash = lambda x: True
-
- # the title
- self.main_title = "Portato (%s)" % VERSION
-
- # main window stuff
- Window.__init__(self)
- self.window.set_title(self.main_title)
- self.window.set_geometry_hints (self.window, max_height = gtk.gdk.screen_height(), max_width = gtk.gdk.screen_width())
-
- # booleans
- self.doUpdate = False
- self.showAll = True # show only installed or all packages?
- self.__searchChanged = False
-
- # installed pixbuf
- self.instPixbuf = self.window.render_icon(gtk.STOCK_YES, gtk.ICON_SIZE_MENU)
-
- # get the logging window as soon as possible
- self.logView = LogView(self.tree.get_widget("logView"))
-
- # config
- splash(_("Loading Config"))
- try:
- self.cfg = Config(CONFIG_LOCATION)
- except IOError, e:
- io_ex_dialog(e)
- raise
-
- self.cfg.modify_external_configs()
- self.set_uri_hook(self.cfg.get("browserCmd", section = "GUI"))
- gtk.about_dialog_set_url_hook(lambda *args: True) # dummy - if not set link is not set as link; if link is clicked the normal uuri_hook is called too - thus do not call browser here
-
- # package db
- splash(_("Creating Database"))
- self.db = Database()
- self.db.populate()
-
- # set plugins and plugin-menu
- splash(_("Loading Plugins"))
-
- plugin.load_plugins()
- menus = [p.menus for p in plugin.get_plugin_queue().get_plugins()]
- if menus:
- self.tree.get_widget("pluginMenuItem").set_no_show_all(False)
- pluginMenu = self.tree.get_widget("pluginMenu")
-
- for m in itt.chain(*menus):
- item = gtk.MenuItem(m.label)
- item.connect("activate", m.call)
- pluginMenu.append(item)
-
- splash(_("Building frontend"))
- # set paned position
- self.vpaned = self.tree.get_widget("vpaned")
- self.vpaned.set_position(int(self.window.get_size()[1]/2))
- self.hpaned = self.tree.get_widget("hpaned")
- self.hpaned.set_position(int(self.window.get_size()[0]/1.5))
-
- # lists
- self.selCatName = ""
- self.selCP = ""
- self.selCPV = ""
- self.sortPkgListByName = True
- self.catList = self.tree.get_widget("catList")
- self.pkgList = self.tree.get_widget("pkgList")
- self.versionList = self.tree.get_widget("versionList")
- self.build_cat_list()
- self.build_pkg_list()
- self.build_version_list()
-
- # search entry
- self.searchEntry = self.tree.get_widget("searchEntry")
-
- # queue list
- self.queueOneshot = self.tree.get_widget("oneshotCB")
- self.queueOneshotHandler = self.queueOneshot.connect("toggled", self.cb_oneshot_clicked)
- self.queueList = self.tree.get_widget("queueList")
- self.build_queue_list()
-
- # the terminal
- self.console = GtkConsole()
- self.termHB = self.tree.get_widget("termHB")
- self.build_terminal()
-
- # notebooks
- self.sysNotebook = self.tree.get_widget("systemNotebook")
- self.pkgNotebook = self.tree.get_widget("packageNotebook")
- self.set_notebook_tabpos(map(PreferenceWindow.tabpos.get, map(int, (self.cfg.get("packageTabPos", "GUI"), self.cfg.get("systemTabPos", "GUI")))))
-
- # the different scrolls
- ebuildScroll = self.tree.get_widget("ebuildScroll")
- ebuildScroll.add(HighlightView(lambda p: p.get_ebuild_path(), ["gentoo", "sh"]))
-
- changelogScroll = self.tree.get_widget("changelogScroll")
- changelogScroll.add(HighlightView(lambda p: os.path.join(p.get_package_path(), "ChangeLog"), ["changelog"]))
-
- def show_files (p):
- try:
- for f in p.get_files():
- yield " %s\n" % f
- except IOError, e:
- yield _("Error: %s") % e.strerror
-
- filesScroll = self.tree.get_widget("filesScroll")
- filesScroll.add(InstalledOnlyView(show_files))
-
- # table
- self.packageTable = PackageTable(self)
-
- # popups
- self.consolePopup = Popup("consolePopup", self, self.__file__)
- self.trayPopup = Popup("systrayPopup", self)
-
- # pause menu items
- self.emergePaused = False
- self.pauseItems = {}
- self.pauseItems["tray"] = self.trayPopup.tree.get_widget("pauseItemTray")
- self.pauseItems["popup"] = self.consolePopup.tree.get_widget("pauseItemPopup")
- self.pauseItems["menu"] = self.tree.get_widget("pauseItemMenu")
-
- for k,v in self.pauseItems.iteritems():
- self.pauseItems[k] = (v, v.connect_after("activate", self.cb_pause_emerge(k)))
-
- # systray
- if self.cfg.get_boolean("showSystray", "GUI"):
- self.tray = gtk.status_icon_new_from_file(APP_ICON)
- self.tray.connect("activate", self.cb_systray_activated)
- self.tray.connect("popup-menu", lambda icon, btn, time: self.trayPopup.popup(None, None, None, btn, time))
- else:
- self.tray = None
-
- # set emerge queue
- self.queueTree = GtkTree(self.queueList.get_model())
- self.queue = EmergeQueue(console = self.console, tree = self.queueTree, db = self.db, title_update = self.title_update, threadClass = GtkThread)
-
- # session
- splash(_("Restoring Session"))
- try:
- try:
- self.load_session()
- except OldSessionException, e:
- self.load_session(e)
- except SessionException, e:
- warning(str(e))
-
- splash(_("Finishing startup"))
-
- self.window.show_all()
-
- def show_package (self, pkg = None, cpv = None, cp = None, version = None, **kwargs):
- p = None
-
- if pkg:
- p = pkg
- elif cpv:
- p = system.find_packages("="+cpv, masked = True)[0]
- elif cp:
- if version:
- p = system.find_packages("=%s-%s" % (cp, version), masked = True)[0]
-
- else:
- best = system.find_best_match(cp)
- if best:
- p = best
- else:
- p = system.find_packages(cp)[0]
-
- self.packageTable.update(p, **kwargs)
-
- def build_terminal (self):
- """
- Builds the terminal.
- """
-
- self.console.set_scrollback_lines(int(self.cfg.get("scrollbacklines", "GUI")))
- self.console.set_scroll_on_output(True)
- self.console.set_font_from_string(self.cfg.get("consolefont", "GUI"))
- self.console.connect("button-press-event", self.cb_right_click)
- self.termHB.pack_start(self.console, True, True)
-
- # add scrollbar
- termScroll = gtk.VScrollbar(self.console.get_adjustment())
- self.termHB.pack_start(termScroll, False)
-
- def build_queue_list (self):
- """
- Builds the queue list.
- """
-
- store = gtk.TreeStore(str,str,bool)
-
- self.queueList.set_model(store)
-
- cell = gtk.CellRendererText()
- col = gtk.TreeViewColumn(_("Queue"), cell, markup = 0)
- self.queueList.append_column(col)
-
- col = gtk.TreeViewColumn(_("Options"), cell, markup = 1)
- self.queueList.append_column(col)
-
- self.queueList.get_selection().connect("changed", self.cb_queue_list_selection)
-
- def build_cat_list (self):
- """
- Builds the category list.
- """
-
- store = gtk.TreeStore(str)
-
- self.fill_cat_store(store)
-
- self.catList.set_model(store)
- cell = gtk.CellRendererText()
- col = gtk.TreeViewColumn(_("Categories"), cell, text = 0)
- self.catList.append_column(col)
-
- self.catList.get_selection().connect("changed", self.cb_cat_list_selection)
-
- def fill_cat_store (self, store = None):
- """
- Fills the category store with data.
-
- @param store: the store to fill
- @type store: gtk.ListStore
- """
-
- if store is None:
- store = self.catList.get_model()
-
- store.clear()
-
- cats = self.db.get_categories(installed = not self.showAll)
-
- if not self.cfg.get_boolean("collapseCats", "GUI"):
- for p in cats:
- store.append(None, [p])
- else:
- splitCats = defaultdict(list)
- for c in cats:
- try:
- pre, post = c.split("-", 1)
- except ValueError: # no "-" in cat name -- do not split
- debug("Category '%s' can't be split up. Should be no harm.", c)
- splitCats["not-split"].append(c)
- else:
- splitCats[pre].append(post)
-
- for sc in splitCats:
- if sc == "not-split":
- it = None # append not splitted stuff to root
- else:
- it = store.append(None, [sc])
- for cat in splitCats[sc]:
- store.append(it, [cat])
-
- # sort them alphabetically
- store.set_sort_column_id(0, gtk.SORT_ASCENDING)
-
- def build_pkg_list (self, name = None):
- """
- Builds the package list.
-
- @param name: name of the selected catetegory
- @type name: string
- """
-
- store = gtk.ListStore(gtk.gdk.Pixbuf, str, str)
- self.fill_pkg_store(store, name)
-
- # build view
- self.pkgList.set_model(store)
-
- col = gtk.TreeViewColumn(_("Packages"))
- col.set_clickable(True)
- col.connect("clicked", self.cb_pkg_list_header_clicked)
-
- # adding the pixbuf
- cell = gtk.CellRendererPixbuf()
- col.pack_start(cell, False)
- col.add_attribute(cell, "pixbuf", 0)
-
- # adding the package name
- cell = gtk.CellRendererText()
- col.pack_start(cell, True)
- col.add_attribute(cell, "text", 1)
-
- self.pkgList.append_column(col)
-
- self.pkgList.get_selection().connect("changed", self.cb_pkg_list_selection)
-
- def fill_pkg_store (self, store = None, name = None):
- """
- Fills a given ListStore with the packages in a category.
-
- @param store: the store to fill
- @type store: gtk.ListStore
- @param name: the name of the category
- @type name: string
- """
-
- if store is None:
- store = self.pkgList.get_model()
- store.clear()
-
- if name:
- for cat, pkg, is_inst in self.db.get_cat(name, self.sortPkgListByName):
- if is_inst:
- icon = self.instPixbuf
- elif not self.showAll:
- continue # ignore not installed packages
- else:
- icon = None
- store.append([icon, pkg, cat])
-
- def build_version_list (self):
- store = gtk.ListStore(gtk.gdk.Pixbuf, str, str)
-
- # build view
- self.versionList.set_model(store)
-
- col = gtk.TreeViewColumn(_("Versions"))
- col.set_property("expand", True)
-
- self.slotcol = gtk.TreeViewColumn(_("Slot"))
- self.slotcol.set_property("expand", True)
-
- # adding the pixbuf
- cell = gtk.CellRendererPixbuf()
- col.pack_start(cell, False)
- col.add_attribute(cell, "pixbuf", 0)
-
- # adding the package name
- cell = gtk.CellRendererText()
- col.pack_start(cell, True)
- col.add_attribute(cell, "text", 1)
-
- # adding the slot
- cell = gtk.CellRendererText()
- self.slotcol.pack_start(cell, True)
- self.slotcol.add_attribute(cell, "text", 2)
-
- self.versionList.append_column(col)
- self.versionList.append_column(self.slotcol)
-
- self.versionList.get_selection().connect("changed", self.cb_vers_list_selection)
-
- def fill_version_list (self, cp, version = None):
-
- store = self.versionList.get_model()
- store.clear()
-
- # this is here for performance reasons
- # to not query the package with info, we do not need
- if self.cfg.get_boolean("showSlots", "GUI"):
- def get_slot(pkg):
- return pkg.get_package_settings("SLOT")
-
- self.slotcol.set_visible(True)
-
- else:
- def get_slot(*args):
- return ""
-
- self.slotcol.set_visible(False)
-
- packages = system.sort_package_list(system.find_packages(cp, masked=True))
-
- # append versions
- for vers, inst, slot in ((x.get_version(), x.is_installed(), get_slot(x)) for x in packages):
- if inst:
- icon = self.instPixbuf
- else:
- icon = None
-
- store.append([icon, vers, slot])
-
- pos = ((0,)) # default
-
- # activate the first one
- try:
- best_version = ""
- if version:
- best_version = version
- else:
- best_version = system.find_best_match(packages[0].get_cp()).get_version()
- for i, p in enumerate(packages):
- if p.get_version() == best_version:
- pos = (i,)
- break
- except AttributeError: # no package found
- pass
-
- self.versionList.get_selection().select_path(pos)
- self.versionList.scroll_to_cell(pos)
-
- def refresh_stores (self):
- """
- Refreshes the category and package stores.
- """
- self.fill_cat_store()
-
- if self.selCatName:
- self.fill_pkg_store(name = self.selCatName)
- else: # no selCatName -> so no category selected --> ignore
- debug("No category selected --> should be no harm.")
-
- def load_session(self, sessionEx = None):
- """
- Loads the session data.
- """
- try:
- self.session = Session("gtk_session.cfg")
- except (OSError, IOError), e:
- io_ex_dialog(e)
- return
-
- oldVersion = SESSION_VERSION
- allowedVersions = (0,1)
-
- if sessionEx and isinstance(sessionEx, SessionException):
- if sessionEx.got in allowedVersions:
- info(_("Translating session from version %d to %d.") % (sessionEx.got, sessionEx.expected))
- oldVersion = sessionEx.got
- else:
- warning(_("Cannot translate session from version %d to %d.") % (sessionEx.got, sessionEx.expected))
- raise sessionEx
-
- #
- # the callbacks for the different session variables
- #
-
- # QUEUE
- def load_queue (merge, unmerge, oneshot):
- def _load(q, **kwargs):
- if q:
- for i in q.split(","):
- self.queue.append(i, **kwargs)
-
- _load(merge)
- _load(unmerge, unmerge = True)
- _load(oneshot, oneshot = True)
-
- def save_queue ():
- if self.__save_queue:
- return (",".join(self.queue.mergequeue), ",".join(self.queue.unmergequeue), ",".join(self.queue.oneshotmerge))
- else:
- return ("", "", "")
-
- # PANED
- def load_paned (*pos):
- pos = map(int, pos)
- [x.set_position(p) for x,p in zip((self.vpaned, self.hpaned), pos)]
-
- def save_paned ():
- return [x.get_position() for x in (self.vpaned, self.hpaned)]
-
- # SELECTION
- def load_selection (list, col):
- def _load (name):
- pos = "0" # default
-
- if name:
- for cname, path in ((x[col], x.path) for x in list.get_model()):
- if cname == name:
- pos = path
- break
-
- if self.cfg.get_boolean("collapseCats", "GUI") and \
- pos == "0" and isinstance(list.get_model(), gtk.TreeStore): # try the new split up
-
- try:
- pre, post = name.split("-", 1)
- except ValueError: # nothing to split
- pass
- else:
- for row in list.get_model():
- if row[col] == pre: # found first part
- pos = row.path
- list.expand_row(pos, False)
- for cname, path in ((x[col], x.path) for x in row.iterchildren()):
- if cname == post: # found second
- pos = ":".join(map(str,path))
- break
- break
-
- debug("Selecting path '%s'.", pos)
- list.get_selection().select_path(pos)
- list.scroll_to_cell(pos)
-
- return _load
-
- def save_pkg_selection ():
- store, iter = self.pkgList.get_selection().get_selected()
- if iter:
- return store.get_value(iter, 1)
- else:
- return ""
-
- def save_cat_selection ():
- # try to find the correct category using the pkgList selection
- # so we do not select ALL =)
- # if no package has been selected - return selCatName
- store, iter = self.pkgList.get_selection().get_selected()
- if iter:
- return store.get_value(iter, 2)
- else:
- return self.selCatName
-
- # PLUGIN
- def load_plugin (p):
- def _load(val):
- if val:
- p.status = int(val)*2
-
- return _load
-
- def save_plugin (p):
- def _save ():
- if p.status == p.STAT_HARD_DISABLED:
- return ""
-
- return int(p.status >= p.STAT_ENABLED)
-
- return _save
-
- # SESSION VERSION
- def load_session_version (version):
- if oldVersion != SESSION_VERSION: # we are trying to convert
- return
-
- version = int(version)
-
- if version < SESSION_VERSION:
- raise OldSessionException(version, SESSION_VERSION)
- elif version > SESSION_VERSION:
- raise NewSessionException(version, SESSION_VERSION)
-
- def _add (value):
- if len(value) == 4:
- self.session.add_handler(value[:3], default = value[3])
- else:
- self.session.add_handler(value)
-
- # set the simple ones :)
- map(_add,[
- ([("gtksessionversion", "session")], load_session_version, lambda: SESSION_VERSION),
- ([("width", "window"), ("height", "window")], lambda w,h: self.window.resize(int(w), int(h)), self.window.get_size),
- ([("vpanedpos", "window"), ("hpanedpos", "window")], load_paned, save_paned),
- ([("catsel", "window")], load_selection(self.catList, 0), save_cat_selection, ["app-portage"]),
- ([("pkgsel", "window")], load_selection(self.pkgList, 1), save_pkg_selection, ["portato"])
- #([("merge", "queue"), ("unmerge", "queue"), ("oneshot", "queue")], load_queue, save_queue),
- ])
-
- # set the plugins
- queue = plugin.get_plugin_queue()
- if queue:
- for p in queue.get_plugins():
- self.session.add_handler(([(p.name.replace(" ","_"), "plugins")], load_plugin(p), save_plugin(p)))
-
- # now we have the handlers -> load
- self.session.load()
-
- def jump_to (self, cp, version = None):
- """
- Is called when we want to jump to a specific package.
-
- @param cp: the CP to jump to
- @type cp: string
- @param version: if not None jump to a specific version
- @type version: string
- """
-
- cat, pkg = cp.split("/")
-
- for list, idx, what, expr in ((self.catList, 0, "categories", cat), (self.pkgList, 1, "packages", pkg)):
- pathes = [row.path for row in list.get_model() if row[idx] == expr]
-
- if len(pathes) == 1:
- list.get_selection().select_path(pathes[0])
- list.scroll_to_cell(pathes[0])
- else:
- debug("Unexpected number of %s returned after search: %d", what, len(pathes))
- break
-
- self.show_package(cp = cp, version = version, queue = self.queue)
-
- def set_uri_hook (self, browser):
- """
- Sets the browser command which is called when a URL is going to be opened.
-
- @param browser: the browser command
- @type browser: string
- """
-
- browser = browser.split()
- gtk.link_button_set_uri_hook(lambda btn, x: get_listener().send_cmd(browser+[btn.get_uri()]))
-
- def set_notebook_tabpos (self, tabposlist):
- """
- Sets the positions of the tabs of the notebooks.
-
- @param tabposlist: the list of positions: first comes the one for package tabs; sndly for sys tabs
- @type tabposlist: int[]
- """
- self.pkgNotebook.set_tab_pos(tabposlist[0])
- self.sysNotebook.set_tab_pos(tabposlist[1])
-
- def title_update (self, title):
- """
- Updates the titles of the window and the systray.
- Mainly used with emerge term titles.
-
- @param title: the title
- @type title: string
- """
-
- def window_title_update (title):
- """
- Updates the title of the main window.
- """
- if title is None or not self.cfg.get_boolean("updateTitle", "GUI"):
- self.window.set_title(self.main_title)
- else:
- title = title.strip()
- if title[0] == '*':
- self.window.set_title(self.main_title)
- else:
- space_idx = title.rfind(" ")
- if space_idx != -1:
- title = title[:space_idx]
-
- self.window.set_title(("Portato >>> %s" % title))
-
- def __update(title):
- if self.tray:
- self.tray.set_tooltip(title)
-
- window_title_update(title)
- if title is None or not self.cfg.get_boolean("updateConsole", "GUI"):
- title = _("Console")
- else:
- title = ("%s (%s)") % (_("Console"), title)
-
- tlength = int(self.cfg.get("titlelength", "GUI"))
- if (len(title) > tlength): title = "%s..." % title[:tlength-3]
- self.sysNotebook.set_tab_label_text(self.termHB, title)
-
- return False
-
- # as this might get called from other threads use gobject.idle_add
- gobject.idle_add(__update, title)
-
- def cb_cat_list_selection (self, selection):
- """
- Callback for a category-list selection.
- Updates the package list with the packages in the category.
- """
- # get the selected category
- store, it = selection.get_selected()
- if it:
- if not self.cfg.get_boolean("collapseCats", "GUI"):
- self.selCatName = store.get_value(it, 0)
- else:
- parent = store.iter_parent(it)
- if parent is None:
- if store.iter_has_child(it): # this is a split up selector -> do nothing
- return True
- else:
- self.selCatName = store.get_value(it, 0) # this is a non-split up top
- else:
- self.selCatName = ("%s-%s" % (store.get_value(parent, 0), store.get_value(it, 0)))
-
- self.fill_pkg_store(name = self.selCatName)
- return True
-
- def cb_pkg_list_selection (self, selection):
- """
- Callback for a package-list selection.
- Updates the version list.
- """
- store, it = selection.get_selected()
- if it:
- self.selCP = "%s/%s" % (store.get_value(it, 2), store.get_value(it, 1))
- self.fill_version_list(self.selCP)
- return True
-
- def cb_pkg_list_header_clicked(self, col):
- self.sortPkgListByName = not self.sortPkgListByName
- self.fill_pkg_store(name = self.selCatName)
- return True
-
- def cb_vers_list_selection (self, selection):
- """
- Callback for a package-list selection.
- Updates the version list.
- """
- store, it = selection.get_selected()
- if it:
- self.selCPV = "%s-%s" % (self.selCP, store.get_value(it, 1))
- self.show_package(cpv = self.selCPV, queue = self.queue)
-
- return True
-
- def cb_queue_list_selection (self, selection):
-
- def set_val (val):
- self.queueOneshot.handler_block(self.queueOneshotHandler)
- self.queueOneshot.set_active(val)
- self.queueOneshot.handler_unblock(self.queueOneshotHandler)
-
- store, it = selection.get_selected()
- if it:
- parent = self.queueTree.parent_iter(it)
- if self.queueTree.is_in_emerge(it) and parent and not self.queueTree.iter_has_parent(parent):
- package = store.get_value(it, 0)
- self.queueOneshot.set_sensitive(True)
- set_val(package in self.queue.oneshotmerge)
- return True
-
- self.queueOneshot.set_sensitive(False)
- set_val(False)
- return True
-
- def cb_queue_row_activated (self, view, path, *args):
- """Callback for an activated row in the emergeQueue. Opens a package window."""
- store = self.queueTree
- if len(path) > 1:
- iterator = store.get_original().get_iter(path)
- if store.iter_has_parent(iterator):
- package = store.get_value(iterator, store.get_cpv_column())
-
- if store.is_in_emerge(iterator):
- type = "install"
- elif store.is_in_unmerge(iterator):
- type = "uninstall"
- elif store.is_in_update(iterator):
- type = "update"
-
- self.show_package(cpv = package, queue = self.queue, instantChange = True, doEmerge = False, type = type)
- return True
-
- def cb_queue_tooltip_queried (self, view, x, y, is_keyboard, tooltip):
- store = self.queueList.get_model()
- path = self.queueList.get_path_at_pos(x,y)
-
- if path is None:
- return False
-
- it = store.get_iter(path[0])
-
- if store.iter_parent(it) is None:
- return False # do not show tooltips for the root entries
-
- pkg = system.new_package(store.get_value(it, 0))
-
- enabled = []
- disabled = []
- expanded = set()
-
- pkg_flags = pkg.get_iuse_flags()
- pkg_flags.sort()
- if not pkg_flags: # no flags - stop here
- return None
-
- actual = set(pkg.get_actual_use_flags())
-
- if pkg.is_installed():
- installed = set(pkg.get_iuse_flags()).intersection(pkg.get_installed_use_flags())
- else:
- inst = system.find_packages(pkg.get_slot_cp(), system.SET_INSTALLED)
- if inst:
- installed = set(inst[0].get_iuse_flags()).intersection(inst[0].get_installed_use_flags())
- else:
- installed = set()
-
- diff = actual.symmetric_difference(installed)
-
- for use in pkg_flags:
- exp = pkg.use_expanded(use)
- if exp:
- expanded.add(exp)
-
- else:
- useStr = use
- if installed and use in diff:
- useStr += " %"
- if use in actual:
- enabled.append(useStr)
- else:
- disabled.append(useStr)
-
- string = ""
-
- if enabled:
- string = "<b>+%s</b>" % ("\n+".join(enabled),)
- if len(disabled) > 0:
- string = string + "\n"
-
- if disabled:
- string = string+"<i>- %s</i>" % ("\n- ".join(disabled),)
-
- if expanded:
- string = string+"\n\n"+"\n".join(expanded)
-
- tooltip.set_markup(string)
- return string != ""
-
- def cb_execute_clicked (self, action):
- """Execute the current queue."""
-
- if len(flags.newUseFlags) > 0:
- if not self.session.get_boolean("useflags", "dialogs"):
- self.session.set("useflags", changed_flags_dialog(_("use flags"))[1], "dialogs")
- try:
- flags.write_use_flags()
- except IOError, e:
- io_ex_dialog(e)
- return True
-
- if len(flags.new_masked)>0 or len(flags.new_unmasked)>0 or len(flags.newTesting)>0:
- debug("new masked: %s",flags.new_masked)
- debug("new unmasked: %s", flags.new_unmasked)
- debug("new testing: %s", flags.newTesting)
- if not self.session.get_boolean("keywords", "dialogs"):
- self.session.set("keywords", changed_flags_dialog(_("masking keywords"))[1], "dialogs")
- try:
- flags.write_masked()
- flags.write_testing()
- except IOError, e:
- io_ex_dialog(e)
- return True
- else:
- system.reload_settings()
-
- model, iter = self.queueList.get_selection().get_selected()
-
- if iter is None:
- if model.iter_n_children(None) == 1: # only one queue there - take this as being selected
- iter = model.get_iter_root()
- else:
- return False
-
- self.sysNotebook.set_current_page(self.CONSOLE_PAGE)
-
- # test which type of queue we have here
- if self.queueTree.is_in_emerge(iter):
- self.queue.emerge(force = True)
- elif self.queueTree.is_in_unmerge(iter):
- self.queue.unmerge(force = True)
- else:
- self.queue.update_world(sets = self.updateSets, force=True, newuse = self.cfg.get_boolean("newuse"), deep = self.cfg.get_boolean("deep"))
-
- return True
-
- def cb_update_clicked (self, action):
- def __update():
-
- def cb_idle_append (updating):
- try:
- try:
- for pkg, old_pkg in updating:
- self.queue.append(pkg.get_cpv(), type = "update", unmask = False)
- except PackageNotFoundException, e:
- if unmask_dialog(e[0]) == gtk.RESPONSE_YES:
- for pkg, old_pkg in updating:
- self.queue.append(pkg.get_cpv(), type = "update", unmask = True)
-
- except BlockedException, e:
- blocked_dialog(e[0], e[1])
- self.queue.remove_children(self.queueTree.get_update_it())
-
- return False
-
- watch = gtk.gdk.Cursor(gtk.gdk.WATCH)
- self.window.window.set_cursor(watch)
- try:
- if system.has_set_support():
- confsets = [x.strip() for x in self.cfg.get("updatesets").split(",")]
- self.updateSets = [s for s in confsets if s in system.get_sets()]
- updating = system.update_world(sets = self.updateSets, newuse = self.cfg.get_boolean("newuse"), deep = self.cfg.get_boolean("deep"))
- else:
- updating = system.update_world(newuse = self.cfg.get_boolean("newuse"), deep = self.cfg.get_boolean("deep"))
- self.updateSets = ("world",)
-
- debug("updating list: %s --> length: %s", [(x.get_cpv(), y.get_cpv()) for x,y in updating], len(updating))
- gobject.idle_add(cb_idle_append, updating)
- finally:
- self.window.window.set_cursor(None)
-
- GtkThread(name="Update-Thread", target=__update).start()
-
- return True
-
- def cb_remove_clicked (self, button):
- """Removes a selected item in the (un)emerge-queue if possible."""
- model, iter = self.queueList.get_selection().get_selected()
-
- if iter:
- parent = model.iter_parent(iter)
-
- if self.queueTree.is_in_update(iter) and parent:
- if remove_updates_dialog() == gtk.RESPONSE_YES:
- self.queue.remove_with_children(self.queueTree.get_update_it())
-
- elif not parent: # top-level
- if model.iter_n_children(iter) > 0: # and has children which can be removed :)
- if remove_queue_dialog() == gtk.RESPONSE_YES :
- self.queue.remove_with_children(iter)
- else:
- self.queue.remove(iter)
-
- elif model.iter_parent(parent): # this is in the 3rd level => dependency
- remove_deps_dialog()
- else:
- self.queue.remove_with_children(iter)
-
- if model.iter_n_children(parent) == 0: # no more children left - remove queue too
- self.queue.remove(parent)
-
- return True
- return False
-
- def cb_sync_clicked (self, action):
- self.sysNotebook.set_current_page(self.CONSOLE_PAGE)
- cmd = self.cfg.get("syncCommand")
-
- if cmd != "emerge --sync":
- cmd = cmd.split()
- self.queue.sync(cmd)
- else:
- self.queue.sync()
-
- def cb_save_flags_clicked (self, action):
- try:
- flags.write_use_flags()
- flags.write_testing()
- flags.write_masked()
- except IOError, e:
- io_ex_dialog(e)
-
- @Window.watch_cursor
- def cb_reload_clicked (self, action):
- """Reloads the portage settings and the database."""
- system.reload_settings()
- self.db.reload()
-
- @Window.watch_cursor
- def cb_search_clicked (self, entry):
- """Do a search."""
- text = entry.get_text()
- if text != "":
- if "/" not in text:
- text = "/.*"+text # only look for package names
-
- packages = system.find_packages(text, with_version = False)
-
- if packages == []:
- nothing_found_dialog()
- else:
- if len(packages) == 1:
- self.jump_to(packages[0])
- else:
- SearchWindow(self.window, packages, self.jump_to)
-
- return True
-
- def cb_search_changed (self, *args):
- """
- Called when the user enters something in the search field.
- Updates the packages according to the search expression.
- """
- if not self.__searchChanged and self.cfg.get_boolean("searchOnType", section="GUI"):
- self.__searchChanged = True
-
- def __update():
- self.__searchChanged = False
- txt = self.searchEntry.get_text()
-
- if txt or self.db.restrict:
- self.db.restrict = txt
-
- self.refresh_stores()
- self.catList.get_selection().select_path("0") # XXX make this smarter
-
- return False # not again ;)
-
- gobject.timeout_add(100, __update)
-
- def cb_delete_search_clicked (self, *args):
- self.searchEntry.set_text("")
- return True
-
- def cb_preferences_clicked (self, *args):
- """
- User wants to open preferences.
- """
- PreferenceWindow(self.window, self.cfg, self.console.set_font_from_string, self.set_uri_hook, self.set_notebook_tabpos, self.fill_cat_store)
- return True
-
- def cb_about_clicked (self, *args):
- """
- User wants to open about dialog.
- """
- AboutWindow(self.window)
- return True
-
- def cb_plugins_clicked (self, *args):
- """
- User wants to open plugin dialog.
- """
- queue = plugin.get_plugin_queue()
- if queue is None:
- plugins = []
- else:
- plugins = list(queue.get_plugins())
-
- PluginWindow(self.window, plugins, self.queue)
- return True
-
- def cb_show_updates_clicked (self, *args):
- """
- Show the list of updateble packages.
- """
-
- def __update():
- def cb_idle_show(packages):
- """
- Callback opening the menu when the calculation is finished.
-
- @returns: False to signal that it is finished
- """
- UpdateWindow(self.window, packages, self.queue, self.jump_to)
- return False
-
- watch = gtk.gdk.Cursor(gtk.gdk.WATCH)
- self.window.window.set_cursor(watch)
-
- packages = []
- try:
- packages.extend(system.get_updated_packages())
- finally:
- self.window.window.set_cursor(None)
-
- gobject.idle_add(cb_idle_show, packages)
-
- GtkThread(name="Show Updates Thread", target = __update).start()
- return True
-
- def cb_show_installed_toggled (self, *args):
- """
- Toggle the "show only installed" option.
- """
- self.showAll = not self.showAll
- self.refresh_stores()
-
- def cb_right_click (self, object, event):
- """
- Called when the user right clicks somewhere.
- Used to display a menu.
-
- This method should handle ALL such menus.
-
- @param object: the object/widget where the click is done
- @type object: gtk.Widget
- @param event: the event triggered
- @type event: gtk.gdk.Event
- """
-
- if event.type == gtk.gdk.BUTTON_PRESS and event.button == 3: # 3 == right click
- x = int(event.x)
- y = int(event.y)
- time = event.time
-
- if object == self.console:
- self.consolePopup.popup(None, None, None, event.button, time)
- else:
- return False
- else:
- return False
-
- def cb_oneshot_clicked (self, *args):
- """
- Mark a package as oneshot.
- """
- sel = self.queueList.get_selection()
- store, it = sel.get_selected()
- if it:
- if self.queueTree.is_in_emerge(it) and self.queueTree.iter_has_parent(it):
- package = store.get_value(it, 0)
- set = (package not in self.queue.oneshotmerge)
-
- self.queue.append(package, update = True, oneshot = set, forceUpdate = True)
-
- def cb_pause_emerge (self, curr):
- """
- This method returns a callback for a "pause emerge" toggle button.
- It is needed as there are different toggle buttons of this type and if one is clicked,
- the others should be marked too.
-
- @param curr: The button to return the callback for.
- @type curr: gtk.ToggleButton
- """
- def pause (cb):
- """
- The actual callback.
-
- Mark all other buttons too.
-
- @param cb: The button which got toggled.
- @type cb: gtk.ToggleButton
- """
-
- # pause or continue
- self.emergePaused = cb.get_active()
- if not self.emergePaused:
- self.queue.continue_emerge()
- #self.tray.set_from_file(APP_ICON)
- else:
- self.queue.stop_emerge()
- #self.tray.set_from_file(os.path.join(ICON_DIR, "pausing.png"))
-
- # block the handlers of the other buttons
- # so that calling "set_active" does not call this callback recursivly
- for v in self.pauseItems.itervalues():
- v[0].handler_block(v[1])
-
- # mark the others
- for k, v in self.pauseItems.iteritems():
- if k != curr:
- v[0].set_active(self.emergePaused)
-
- # unblock
- for v in self.pauseItems.itervalues():
- v[0].handler_unblock(v[1])
-
- return False
- return pause
-
- def cb_kill_clicked (self, *args):
- """
- Kill emerge.
- """
- self.queue.kill_emerge()
- if self.emergePaused: # unmark the "pause emerge" buttons
- self.pauseItems["menu"][0].set_active(False) # calling one button is enough (see: cb_pause_emerge)
-
- def cb_copy_clicked (self, *args):
- """
- Copy marked text in the terminal to clipboard.
- """
- self.console.copy_clipboard()
-
- def cb_delete (self, *args):
- """
- Called when the user wants to quit the application.
-
- Asks the user for confirmation if there is something in the queue.
- Also saves session data.
- """
-
- self.__save_queue = False
-
- if not self.queue.is_empty():
- ret = queue_not_empty_dialog()
- if ret == gtk.RESPONSE_CANCEL:
- return True
- else: # there is sth in queue AND the user still wants to close -> kill emerge
- self.__save_queue = (ret == gtk.RESPONSE_YES)
- self.queue.kill_emerge()
-
- # write session
- self.session.save()
-
- return False
-
- def cb_minimized (self, window, event):
- """
- User wants to minimize the window.
- If it is possible to minimize to tray, it is done.
- """
-
- if self.tray and self.cfg.get_boolean("hideOnMinimize", "GUI"):
- if event.changed_mask & gtk.gdk.WINDOW_STATE_ICONIFIED:
- if event.new_window_state & gtk.gdk.WINDOW_STATE_ICONIFIED:
- self.window.hide()
- return True
-
- return False
-
- def cb_systray_activated (self, *args):
- """
- Systray was activated. Show or hide the window.
- """
- if self.window.iconify_initially:
- self.window.deiconify()
- self.window.show()
- self.window.window.show()
- else:
- self.window.iconify()
-
- def cb_close (self, *args):
- """
- "Close" menu entry called.
- Emulate normal quitting.
- """
- if not self.cb_delete(): # do the checks
- self.window.destroy()
-
- def cb_destroy (self, *args):
- """
- Calls main_quit().
- """
- gtk.main_quit()
-
- def main (self):
- """
- Main.
- """
- gobject.threads_init()
- # now subthreads can run normally, but are not allowed to touch the GUI. If threads should change sth there - use gobject.idle_add().
- # for more informations on threading and gtk: http://www.async.com.br/faq/pygtk/index.py?req=show&file=faq20.006.htp
- plugin.hook("main")(gtk.main)()
+ """
+ Application main window.
+ """
+
+ # NOTEBOOK PAGE CONSTANTS
+ (
+ QUEUE_PAGE,
+ CONSOLE_PAGE,
+ LOG_PAGE
+ ) = range(3)
+
+ def __init__ (self, splash = None):
+ """
+ Build up window.
+
+ @param splash: the splash screen =)
+ @type splash: SplashScreen
+ """
+
+ if splash is None:
+ splash = lambda x: True
+
+ # the title
+ self.main_title = "Portato (%s)" % VERSION
+
+ # main window stuff
+ Window.__init__(self)
+ self.window.set_title(self.main_title)
+ self.window.set_geometry_hints (self.window, max_height = gtk.gdk.screen_height(), max_width = gtk.gdk.screen_width())
+
+ # booleans
+ self.doUpdate = False
+ self.showAll = True # show only installed or all packages?
+ self.__searchChanged = False
+
+ # installed pixbuf
+ self.instPixbuf = self.window.render_icon(gtk.STOCK_YES, gtk.ICON_SIZE_MENU)
+
+ # get the logging window as soon as possible
+ self.logView = LogView(self.tree.get_widget("logView"))
+
+ # config
+ splash(_("Loading Config"))
+ try:
+ self.cfg = Config(CONFIG_LOCATION)
+ except IOError, e:
+ io_ex_dialog(e)
+ raise
+
+ self.cfg.modify_external_configs()
+ self.set_uri_hook(self.cfg.get("browserCmd", section = "GUI"))
+ gtk.about_dialog_set_url_hook(lambda *args: True) # dummy - if not set link is not set as link; if link is clicked the normal uuri_hook is called too - thus do not call browser here
+
+ # package db
+ splash(_("Creating Database"))
+ self.db = Database()
+ self.db.populate()
+
+ # set plugins and plugin-menu
+ splash(_("Loading Plugins"))
+
+ plugin.load_plugins()
+ menus = [p.menus for p in plugin.get_plugin_queue().get_plugins()]
+ if menus:
+ self.tree.get_widget("pluginMenuItem").set_no_show_all(False)
+ pluginMenu = self.tree.get_widget("pluginMenu")
+
+ for m in itt.chain(*menus):
+ item = gtk.MenuItem(m.label)
+ item.connect("activate", m.call)
+ pluginMenu.append(item)
+
+ splash(_("Building frontend"))
+ # set paned position
+ self.vpaned = self.tree.get_widget("vpaned")
+ self.vpaned.set_position(int(self.window.get_size()[1]/2))
+ self.hpaned = self.tree.get_widget("hpaned")
+ self.hpaned.set_position(int(self.window.get_size()[0]/1.5))
+
+ # lists
+ self.selCatName = ""
+ self.selCP = ""
+ self.selCPV = ""
+ self.sortPkgListByName = True
+ self.catList = self.tree.get_widget("catList")
+ self.pkgList = self.tree.get_widget("pkgList")
+ self.versionList = self.tree.get_widget("versionList")
+ self.build_cat_list()
+ self.build_pkg_list()
+ self.build_version_list()
+
+ # search entry
+ self.searchEntry = self.tree.get_widget("searchEntry")
+
+ # queue list
+ self.queueOneshot = self.tree.get_widget("oneshotCB")
+ self.queueOneshotHandler = self.queueOneshot.connect("toggled", self.cb_oneshot_clicked)
+ self.queueList = self.tree.get_widget("queueList")
+ self.build_queue_list()
+
+ # the terminal
+ self.console = GtkConsole()
+ self.termHB = self.tree.get_widget("termHB")
+ self.build_terminal()
+
+ # notebooks
+ self.sysNotebook = self.tree.get_widget("systemNotebook")
+ self.pkgNotebook = self.tree.get_widget("packageNotebook")
+ self.set_notebook_tabpos(map(PreferenceWindow.tabpos.get, map(int, (self.cfg.get("packageTabPos", "GUI"), self.cfg.get("systemTabPos", "GUI")))))
+
+ # the different scrolls
+ ebuildScroll = self.tree.get_widget("ebuildScroll")
+ ebuildScroll.add(HighlightView(lambda p: p.get_ebuild_path(), ["gentoo", "sh"]))
+
+ changelogScroll = self.tree.get_widget("changelogScroll")
+ changelogScroll.add(HighlightView(lambda p: os.path.join(p.get_package_path(), "ChangeLog"), ["changelog"]))
+
+ def show_files (p):
+ try:
+ for f in p.get_files():
+ yield " %s\n" % f
+ except IOError, e:
+ yield _("Error: %s") % e.strerror
+
+ filesScroll = self.tree.get_widget("filesScroll")
+ filesScroll.add(InstalledOnlyView(show_files))
+
+ # table
+ self.packageTable = PackageTable(self)
+
+ # popups
+ self.consolePopup = Popup("consolePopup", self, self.__file__)
+ self.trayPopup = Popup("systrayPopup", self)
+
+ # pause menu items
+ self.emergePaused = False
+ self.pauseItems = {}
+ self.pauseItems["tray"] = self.trayPopup.tree.get_widget("pauseItemTray")
+ self.pauseItems["popup"] = self.consolePopup.tree.get_widget("pauseItemPopup")
+ self.pauseItems["menu"] = self.tree.get_widget("pauseItemMenu")
+
+ for k,v in self.pauseItems.iteritems():
+ self.pauseItems[k] = (v, v.connect_after("activate", self.cb_pause_emerge(k)))
+
+ # systray
+ if self.cfg.get_boolean("showSystray", "GUI"):
+ self.tray = gtk.status_icon_new_from_file(APP_ICON)
+ self.tray.connect("activate", self.cb_systray_activated)
+ self.tray.connect("popup-menu", lambda icon, btn, time: self.trayPopup.popup(None, None, None, btn, time))
+ else:
+ self.tray = None
+
+ # set emerge queue
+ self.queueTree = GtkTree(self.queueList.get_model())
+ self.queue = EmergeQueue(console = self.console, tree = self.queueTree, db = self.db, title_update = self.title_update, threadClass = GtkThread)
+
+ # session
+ splash(_("Restoring Session"))
+ try:
+ try:
+ self.load_session()
+ except OldSessionException, e:
+ self.load_session(e)
+ except SessionException, e:
+ warning(str(e))
+
+ splash(_("Finishing startup"))
+
+ self.window.show_all()
+
+ def show_package (self, pkg = None, cpv = None, cp = None, version = None, **kwargs):
+ p = None
+
+ if pkg:
+ p = pkg
+ elif cpv:
+ p = system.find_packages("="+cpv, masked = True)[0]
+ elif cp:
+ if version:
+ p = system.find_packages("=%s-%s" % (cp, version), masked = True)[0]
+
+ else:
+ best = system.find_best_match(cp)
+ if best:
+ p = best
+ else:
+ p = system.find_packages(cp)[0]
+
+ self.packageTable.update(p, **kwargs)
+
+ def build_terminal (self):
+ """
+ Builds the terminal.
+ """
+
+ self.console.set_scrollback_lines(int(self.cfg.get("scrollbacklines", "GUI")))
+ self.console.set_scroll_on_output(True)
+ self.console.set_font_from_string(self.cfg.get("consolefont", "GUI"))
+ self.console.connect("button-press-event", self.cb_right_click)
+ self.termHB.pack_start(self.console, True, True)
+
+ # add scrollbar
+ termScroll = gtk.VScrollbar(self.console.get_adjustment())
+ self.termHB.pack_start(termScroll, False)
+
+ def build_queue_list (self):
+ """
+ Builds the queue list.
+ """
+
+ store = gtk.TreeStore(str,str,bool)
+
+ self.queueList.set_model(store)
+
+ cell = gtk.CellRendererText()
+ col = gtk.TreeViewColumn(_("Queue"), cell, markup = 0)
+ self.queueList.append_column(col)
+
+ col = gtk.TreeViewColumn(_("Options"), cell, markup = 1)
+ self.queueList.append_column(col)
+
+ self.queueList.get_selection().connect("changed", self.cb_queue_list_selection)
+
+ def build_cat_list (self):
+ """
+ Builds the category list.
+ """
+
+ store = gtk.TreeStore(str)
+
+ self.fill_cat_store(store)
+
+ self.catList.set_model(store)
+ cell = gtk.CellRendererText()
+ col = gtk.TreeViewColumn(_("Categories"), cell, text = 0)
+ self.catList.append_column(col)
+
+ self.catList.get_selection().connect("changed", self.cb_cat_list_selection)
+
+ def fill_cat_store (self, store = None):
+ """
+ Fills the category store with data.
+
+ @param store: the store to fill
+ @type store: gtk.ListStore
+ """
+
+ if store is None:
+ store = self.catList.get_model()
+
+ store.clear()
+
+ cats = self.db.get_categories(installed = not self.showAll)
+
+ if not self.cfg.get_boolean("collapseCats", "GUI"):
+ for p in cats:
+ store.append(None, [p])
+ else:
+ splitCats = defaultdict(list)
+ for c in cats:
+ try:
+ pre, post = c.split("-", 1)
+ except ValueError: # no "-" in cat name -- do not split
+ debug("Category '%s' can't be split up. Should be no harm.", c)
+ splitCats["not-split"].append(c)
+ else:
+ splitCats[pre].append(post)
+
+ for sc in splitCats:
+ if sc == "not-split":
+ it = None # append not splitted stuff to root
+ else:
+ it = store.append(None, [sc])
+ for cat in splitCats[sc]:
+ store.append(it, [cat])
+
+ # sort them alphabetically
+ store.set_sort_column_id(0, gtk.SORT_ASCENDING)
+
+ def build_pkg_list (self, name = None):
+ """
+ Builds the package list.
+
+ @param name: name of the selected catetegory
+ @type name: string
+ """
+
+ store = gtk.ListStore(gtk.gdk.Pixbuf, str, str)
+ self.fill_pkg_store(store, name)
+
+ # build view
+ self.pkgList.set_model(store)
+
+ col = gtk.TreeViewColumn(_("Packages"))
+ col.set_clickable(True)
+ col.connect("clicked", self.cb_pkg_list_header_clicked)
+
+ # adding the pixbuf
+ cell = gtk.CellRendererPixbuf()
+ col.pack_start(cell, False)
+ col.add_attribute(cell, "pixbuf", 0)
+
+ # adding the package name
+ cell = gtk.CellRendererText()
+ col.pack_start(cell, True)
+ col.add_attribute(cell, "text", 1)
+
+ self.pkgList.append_column(col)
+
+ self.pkgList.get_selection().connect("changed", self.cb_pkg_list_selection)
+
+ def fill_pkg_store (self, store = None, name = None):
+ """
+ Fills a given ListStore with the packages in a category.
+
+ @param store: the store to fill
+ @type store: gtk.ListStore
+ @param name: the name of the category
+ @type name: string
+ """
+
+ if store is None:
+ store = self.pkgList.get_model()
+ store.clear()
+
+ if name:
+ for cat, pkg, is_inst in self.db.get_cat(name, self.sortPkgListByName):
+ if is_inst:
+ icon = self.instPixbuf
+ elif not self.showAll:
+ continue # ignore not installed packages
+ else:
+ icon = None
+ store.append([icon, pkg, cat])
+
+ def build_version_list (self):
+ store = gtk.ListStore(gtk.gdk.Pixbuf, str, str)
+
+ # build view
+ self.versionList.set_model(store)
+
+ col = gtk.TreeViewColumn(_("Versions"))
+ col.set_property("expand", True)
+
+ self.slotcol = gtk.TreeViewColumn(_("Slot"))
+ self.slotcol.set_property("expand", True)
+
+ # adding the pixbuf
+ cell = gtk.CellRendererPixbuf()
+ col.pack_start(cell, False)
+ col.add_attribute(cell, "pixbuf", 0)
+
+ # adding the package name
+ cell = gtk.CellRendererText()
+ col.pack_start(cell, True)
+ col.add_attribute(cell, "text", 1)
+
+ # adding the slot
+ cell = gtk.CellRendererText()
+ self.slotcol.pack_start(cell, True)
+ self.slotcol.add_attribute(cell, "text", 2)
+
+ self.versionList.append_column(col)
+ self.versionList.append_column(self.slotcol)
+
+ self.versionList.get_selection().connect("changed", self.cb_vers_list_selection)
+
+ def fill_version_list (self, cp, version = None):
+
+ store = self.versionList.get_model()
+ store.clear()
+
+ # this is here for performance reasons
+ # to not query the package with info, we do not need
+ if self.cfg.get_boolean("showSlots", "GUI"):
+ def get_slot(pkg):
+ return pkg.get_package_settings("SLOT")
+
+ self.slotcol.set_visible(True)
+
+ else:
+ def get_slot(*args):
+ return ""
+
+ self.slotcol.set_visible(False)
+
+ packages = system.sort_package_list(system.find_packages(cp, masked=True))
+
+ # append versions
+ for vers, inst, slot in ((x.get_version(), x.is_installed(), get_slot(x)) for x in packages):
+ if inst:
+ icon = self.instPixbuf
+ else:
+ icon = None
+
+ store.append([icon, vers, slot])
+
+ pos = ((0,)) # default
+
+ # activate the first one
+ try:
+ best_version = ""
+ if version:
+ best_version = version
+ else:
+ best_version = system.find_best_match(packages[0].get_cp()).get_version()
+ for i, p in enumerate(packages):
+ if p.get_version() == best_version:
+ pos = (i,)
+ break
+ except AttributeError: # no package found
+ pass
+
+ self.versionList.get_selection().select_path(pos)
+ self.versionList.scroll_to_cell(pos)
+
+ def refresh_stores (self):
+ """
+ Refreshes the category and package stores.
+ """
+ self.fill_cat_store()
+
+ if self.selCatName:
+ self.fill_pkg_store(name = self.selCatName)
+ else: # no selCatName -> so no category selected --> ignore
+ debug("No category selected --> should be no harm.")
+
+ def load_session(self, sessionEx = None):
+ """
+ Loads the session data.
+ """
+ try:
+ self.session = Session("gtk_session.cfg")
+ except (OSError, IOError), e:
+ io_ex_dialog(e)
+ return
+
+ oldVersion = SESSION_VERSION
+ allowedVersions = (0,1)
+
+ if sessionEx and isinstance(sessionEx, SessionException):
+ if sessionEx.got in allowedVersions:
+ info(_("Translating session from version %d to %d.") % (sessionEx.got, sessionEx.expected))
+ oldVersion = sessionEx.got
+ else:
+ warning(_("Cannot translate session from version %d to %d.") % (sessionEx.got, sessionEx.expected))
+ raise sessionEx
+
+ #
+ # the callbacks for the different session variables
+ #
+
+ # QUEUE
+ def load_queue (merge, unmerge, oneshot):
+ def _load(q, **kwargs):
+ if q:
+ for i in q.split(","):
+ self.queue.append(i, **kwargs)
+
+ _load(merge)
+ _load(unmerge, unmerge = True)
+ _load(oneshot, oneshot = True)
+
+ def save_queue ():
+ if self.__save_queue:
+ return (",".join(self.queue.mergequeue), ",".join(self.queue.unmergequeue), ",".join(self.queue.oneshotmerge))
+ else:
+ return ("", "", "")
+
+ # PANED
+ def load_paned (*pos):
+ pos = map(int, pos)
+ [x.set_position(p) for x,p in zip((self.vpaned, self.hpaned), pos)]
+
+ def save_paned ():
+ return [x.get_position() for x in (self.vpaned, self.hpaned)]
+
+ # SELECTION
+ def load_selection (list, col):
+ def _load (name):
+ pos = "0" # default
+
+ if name:
+ for cname, path in ((x[col], x.path) for x in list.get_model()):
+ if cname == name:
+ pos = path
+ break
+
+ if self.cfg.get_boolean("collapseCats", "GUI") and \
+ pos == "0" and isinstance(list.get_model(), gtk.TreeStore): # try the new split up
+
+ try:
+ pre, post = name.split("-", 1)
+ except ValueError: # nothing to split
+ pass
+ else:
+ for row in list.get_model():
+ if row[col] == pre: # found first part
+ pos = row.path
+ list.expand_row(pos, False)
+ for cname, path in ((x[col], x.path) for x in row.iterchildren()):
+ if cname == post: # found second
+ pos = ":".join(map(str,path))
+ break
+ break
+
+ debug("Selecting path '%s'.", pos)
+ list.get_selection().select_path(pos)
+ list.scroll_to_cell(pos)
+
+ return _load
+
+ def save_pkg_selection ():
+ store, iter = self.pkgList.get_selection().get_selected()
+ if iter:
+ return store.get_value(iter, 1)
+ else:
+ return ""
+
+ def save_cat_selection ():
+ # try to find the correct category using the pkgList selection
+ # so we do not select ALL =)
+ # if no package has been selected - return selCatName
+ store, iter = self.pkgList.get_selection().get_selected()
+ if iter:
+ return store.get_value(iter, 2)
+ else:
+ return self.selCatName
+
+ # PLUGIN
+ def load_plugin (p):
+ def _load(val):
+ if val:
+ p.status = int(val)*2
+
+ return _load
+
+ def save_plugin (p):
+ def _save ():
+ if p.status == p.STAT_HARD_DISABLED:
+ return ""
+
+ return int(p.status >= p.STAT_ENABLED)
+
+ return _save
+
+ # SESSION VERSION
+ def load_session_version (version):
+ if oldVersion != SESSION_VERSION: # we are trying to convert
+ return
+
+ version = int(version)
+
+ if version < SESSION_VERSION:
+ raise OldSessionException(version, SESSION_VERSION)
+ elif version > SESSION_VERSION:
+ raise NewSessionException(version, SESSION_VERSION)
+
+ def _add (value):
+ if len(value) == 4:
+ self.session.add_handler(value[:3], default = value[3])
+ else:
+ self.session.add_handler(value)
+
+ # set the simple ones :)
+ map(_add,[
+ ([("gtksessionversion", "session")], load_session_version, lambda: SESSION_VERSION),
+ ([("width", "window"), ("height", "window")], lambda w,h: self.window.resize(int(w), int(h)), self.window.get_size),
+ ([("vpanedpos", "window"), ("hpanedpos", "window")], load_paned, save_paned),
+ ([("catsel", "window")], load_selection(self.catList, 0), save_cat_selection, ["app-portage"]),
+ ([("pkgsel", "window")], load_selection(self.pkgList, 1), save_pkg_selection, ["portato"])
+ #([("merge", "queue"), ("unmerge", "queue"), ("oneshot", "queue")], load_queue, save_queue),
+ ])
+
+ # set the plugins
+ queue = plugin.get_plugin_queue()
+ if queue:
+ for p in queue.get_plugins():
+ self.session.add_handler(([(p.name.replace(" ","_"), "plugins")], load_plugin(p), save_plugin(p)))
+
+ # now we have the handlers -> load
+ self.session.load()
+
+ def jump_to (self, cp, version = None):
+ """
+ Is called when we want to jump to a specific package.
+
+ @param cp: the CP to jump to
+ @type cp: string
+ @param version: if not None jump to a specific version
+ @type version: string
+ """
+
+ cat, pkg = cp.split("/")
+
+ for list, idx, what, expr in ((self.catList, 0, "categories", cat), (self.pkgList, 1, "packages", pkg)):
+ pathes = [row.path for row in list.get_model() if row[idx] == expr]
+
+ if len(pathes) == 1:
+ list.get_selection().select_path(pathes[0])
+ list.scroll_to_cell(pathes[0])
+ else:
+ debug("Unexpected number of %s returned after search: %d", what, len(pathes))
+ break
+
+ self.show_package(cp = cp, version = version, queue = self.queue)
+
+ def set_uri_hook (self, browser):
+ """
+ Sets the browser command which is called when a URL is going to be opened.
+
+ @param browser: the browser command
+ @type browser: string
+ """
+
+ browser = browser.split()
+ gtk.link_button_set_uri_hook(lambda btn, x: get_listener().send_cmd(browser+[btn.get_uri()]))
+
+ def set_notebook_tabpos (self, tabposlist):
+ """
+ Sets the positions of the tabs of the notebooks.
+
+ @param tabposlist: the list of positions: first comes the one for package tabs; sndly for sys tabs
+ @type tabposlist: int[]
+ """
+ self.pkgNotebook.set_tab_pos(tabposlist[0])
+ self.sysNotebook.set_tab_pos(tabposlist[1])
+
+ def title_update (self, title):
+ """
+ Updates the titles of the window and the systray.
+ Mainly used with emerge term titles.
+
+ @param title: the title
+ @type title: string
+ """
+
+ def window_title_update (title):
+ """
+ Updates the title of the main window.
+ """
+ if title is None or not self.cfg.get_boolean("updateTitle", "GUI"):
+ self.window.set_title(self.main_title)
+ else:
+ title = title.strip()
+ if title[0] == '*':
+ self.window.set_title(self.main_title)
+ else:
+ space_idx = title.rfind(" ")
+ if space_idx != -1:
+ title = title[:space_idx]
+
+ self.window.set_title(("Portato >>> %s" % title))
+
+ def __update(title):
+ if self.tray:
+ self.tray.set_tooltip(title)
+
+ window_title_update(title)
+ if title is None or not self.cfg.get_boolean("updateConsole", "GUI"):
+ title = _("Console")
+ else:
+ title = ("%s (%s)") % (_("Console"), title)
+
+ tlength = int(self.cfg.get("titlelength", "GUI"))
+ if (len(title) > tlength): title = "%s..." % title[:tlength-3]
+ self.sysNotebook.set_tab_label_text(self.termHB, title)
+
+ return False
+
+ # as this might get called from other threads use gobject.idle_add
+ gobject.idle_add(__update, title)
+
+ def cb_cat_list_selection (self, selection):
+ """
+ Callback for a category-list selection.
+ Updates the package list with the packages in the category.
+ """
+ # get the selected category
+ store, it = selection.get_selected()
+ if it:
+ if not self.cfg.get_boolean("collapseCats", "GUI"):
+ self.selCatName = store.get_value(it, 0)
+ else:
+ parent = store.iter_parent(it)
+ if parent is None:
+ if store.iter_has_child(it): # this is a split up selector -> do nothing
+ return True
+ else:
+ self.selCatName = store.get_value(it, 0) # this is a non-split up top
+ else:
+ self.selCatName = ("%s-%s" % (store.get_value(parent, 0), store.get_value(it, 0)))
+
+ self.fill_pkg_store(name = self.selCatName)
+ return True
+
+ def cb_pkg_list_selection (self, selection):
+ """
+ Callback for a package-list selection.
+ Updates the version list.
+ """
+ store, it = selection.get_selected()
+ if it:
+ self.selCP = "%s/%s" % (store.get_value(it, 2), store.get_value(it, 1))
+ self.fill_version_list(self.selCP)
+ return True
+
+ def cb_pkg_list_header_clicked(self, col):
+ self.sortPkgListByName = not self.sortPkgListByName
+ self.fill_pkg_store(name = self.selCatName)
+ return True
+
+ def cb_vers_list_selection (self, selection):
+ """
+ Callback for a package-list selection.
+ Updates the version list.
+ """
+ store, it = selection.get_selected()
+ if it:
+ self.selCPV = "%s-%s" % (self.selCP, store.get_value(it, 1))
+ self.show_package(cpv = self.selCPV, queue = self.queue)
+
+ return True
+
+ def cb_queue_list_selection (self, selection):
+
+ def set_val (val):
+ self.queueOneshot.handler_block(self.queueOneshotHandler)
+ self.queueOneshot.set_active(val)
+ self.queueOneshot.handler_unblock(self.queueOneshotHandler)
+
+ store, it = selection.get_selected()
+ if it:
+ parent = self.queueTree.parent_iter(it)
+ if self.queueTree.is_in_emerge(it) and parent and not self.queueTree.iter_has_parent(parent):
+ package = store.get_value(it, 0)
+ self.queueOneshot.set_sensitive(True)
+ set_val(package in self.queue.oneshotmerge)
+ return True
+
+ self.queueOneshot.set_sensitive(False)
+ set_val(False)
+ return True
+
+ def cb_queue_row_activated (self, view, path, *args):
+ """Callback for an activated row in the emergeQueue. Opens a package window."""
+ store = self.queueTree
+ if len(path) > 1:
+ iterator = store.get_original().get_iter(path)
+ if store.iter_has_parent(iterator):
+ package = store.get_value(iterator, store.get_cpv_column())
+
+ if store.is_in_emerge(iterator):
+ type = "install"
+ elif store.is_in_unmerge(iterator):
+ type = "uninstall"
+ elif store.is_in_update(iterator):
+ type = "update"
+
+ self.show_package(cpv = package, queue = self.queue, instantChange = True, doEmerge = False, type = type)
+ return True
+
+ def cb_queue_tooltip_queried (self, view, x, y, is_keyboard, tooltip):
+ store = self.queueList.get_model()
+ path = self.queueList.get_path_at_pos(x,y)
+
+ if path is None:
+ return False
+
+ it = store.get_iter(path[0])
+
+ if store.iter_parent(it) is None:
+ return False # do not show tooltips for the root entries
+
+ pkg = system.new_package(store.get_value(it, 0))
+
+ enabled = []
+ disabled = []
+ expanded = set()
+
+ pkg_flags = pkg.get_iuse_flags()
+ pkg_flags.sort()
+ if not pkg_flags: # no flags - stop here
+ return None
+
+ actual = set(pkg.get_actual_use_flags())
+
+ if pkg.is_installed():
+ installed = set(pkg.get_iuse_flags()).intersection(pkg.get_installed_use_flags())
+ else:
+ inst = system.find_packages(pkg.get_slot_cp(), system.SET_INSTALLED)
+ if inst:
+ installed = set(inst[0].get_iuse_flags()).intersection(inst[0].get_installed_use_flags())
+ else:
+ installed = set()
+
+ diff = actual.symmetric_difference(installed)
+
+ for use in pkg_flags:
+ exp = pkg.use_expanded(use)
+ if exp:
+ expanded.add(exp)
+
+ else:
+ useStr = use
+ if installed and use in diff:
+ useStr += " %"
+ if use in actual:
+ enabled.append(useStr)
+ else:
+ disabled.append(useStr)
+
+ string = ""
+
+ if enabled:
+ string = "<b>+%s</b>" % ("\n+".join(enabled),)
+ if len(disabled) > 0:
+ string = string + "\n"
+
+ if disabled:
+ string = string+"<i>- %s</i>" % ("\n- ".join(disabled),)
+
+ if expanded:
+ string = string+"\n\n"+"\n".join(expanded)
+
+ tooltip.set_markup(string)
+ return string != ""
+
+ def cb_execute_clicked (self, action):
+ """Execute the current queue."""
+
+ if len(flags.newUseFlags) > 0:
+ if not self.session.get_boolean("useflags", "dialogs"):
+ self.session.set("useflags", changed_flags_dialog(_("use flags"))[1], "dialogs")
+ try:
+ flags.write_use_flags()
+ except IOError, e:
+ io_ex_dialog(e)
+ return True
+
+ if len(flags.new_masked)>0 or len(flags.new_unmasked)>0 or len(flags.newTesting)>0:
+ debug("new masked: %s",flags.new_masked)
+ debug("new unmasked: %s", flags.new_unmasked)
+ debug("new testing: %s", flags.newTesting)
+ if not self.session.get_boolean("keywords", "dialogs"):
+ self.session.set("keywords", changed_flags_dialog(_("masking keywords"))[1], "dialogs")
+ try:
+ flags.write_masked()
+ flags.write_testing()
+ except IOError, e:
+ io_ex_dialog(e)
+ return True
+ else:
+ system.reload_settings()
+
+ model, iter = self.queueList.get_selection().get_selected()
+
+ if iter is None:
+ if model.iter_n_children(None) == 1: # only one queue there - take this as being selected
+ iter = model.get_iter_root()
+ else:
+ return False
+
+ self.sysNotebook.set_current_page(self.CONSOLE_PAGE)
+
+ # test which type of queue we have here
+ if self.queueTree.is_in_emerge(iter):
+ self.queue.emerge(force = True)
+ elif self.queueTree.is_in_unmerge(iter):
+ self.queue.unmerge(force = True)
+ else:
+ self.queue.update_world(sets = self.updateSets, force=True, newuse = self.cfg.get_boolean("newuse"), deep = self.cfg.get_boolean("deep"))
+
+ return True
+
+ def cb_update_clicked (self, action):
+ def __update():
+
+ def cb_idle_append (updating):
+ try:
+ try:
+ for pkg, old_pkg in updating:
+ self.queue.append(pkg.get_cpv(), type = "update", unmask = False)
+ except PackageNotFoundException, e:
+ if unmask_dialog(e[0]) == gtk.RESPONSE_YES:
+ for pkg, old_pkg in updating:
+ self.queue.append(pkg.get_cpv(), type = "update", unmask = True)
+
+ except BlockedException, e:
+ blocked_dialog(e[0], e[1])
+ self.queue.remove_children(self.queueTree.get_update_it())
+
+ return False
+
+ watch = gtk.gdk.Cursor(gtk.gdk.WATCH)
+ self.window.window.set_cursor(watch)
+ try:
+ if system.has_set_support():
+ confsets = [x.strip() for x in self.cfg.get("updatesets").split(",")]
+ self.updateSets = [s for s in confsets if s in system.get_sets()]
+ updating = system.update_world(sets = self.updateSets, newuse = self.cfg.get_boolean("newuse"), deep = self.cfg.get_boolean("deep"))
+ else:
+ updating = system.update_world(newuse = self.cfg.get_boolean("newuse"), deep = self.cfg.get_boolean("deep"))
+ self.updateSets = ("world",)
+
+ debug("updating list: %s --> length: %s", [(x.get_cpv(), y.get_cpv()) for x,y in updating], len(updating))
+ gobject.idle_add(cb_idle_append, updating)
+ finally:
+ self.window.window.set_cursor(None)
+
+ GtkThread(name="Update-Thread", target=__update).start()
+
+ return True
+
+ def cb_remove_clicked (self, button):
+ """Removes a selected item in the (un)emerge-queue if possible."""
+ model, iter = self.queueList.get_selection().get_selected()
+
+ if iter:
+ parent = model.iter_parent(iter)
+
+ if self.queueTree.is_in_update(iter) and parent:
+ if remove_updates_dialog() == gtk.RESPONSE_YES:
+ self.queue.remove_with_children(self.queueTree.get_update_it())
+
+ elif not parent: # top-level
+ if model.iter_n_children(iter) > 0: # and has children which can be removed :)
+ if remove_queue_dialog() == gtk.RESPONSE_YES :
+ self.queue.remove_with_children(iter)
+ else:
+ self.queue.remove(iter)
+
+ elif model.iter_parent(parent): # this is in the 3rd level => dependency
+ remove_deps_dialog()
+ else:
+ self.queue.remove_with_children(iter)
+
+ if model.iter_n_children(parent) == 0: # no more children left - remove queue too
+ self.queue.remove(parent)
+
+ return True
+ return False
+
+ def cb_sync_clicked (self, action):
+ self.sysNotebook.set_current_page(self.CONSOLE_PAGE)
+ cmd = self.cfg.get("syncCommand")
+
+ if cmd != "emerge --sync":
+ cmd = cmd.split()
+ self.queue.sync(cmd)
+ else:
+ self.queue.sync()
+
+ def cb_save_flags_clicked (self, action):
+ try:
+ flags.write_use_flags()
+ flags.write_testing()
+ flags.write_masked()
+ except IOError, e:
+ io_ex_dialog(e)
+
+ @Window.watch_cursor
+ def cb_reload_clicked (self, action):
+ """Reloads the portage settings and the database."""
+ system.reload_settings()
+ self.db.reload()
+
+ @Window.watch_cursor
+ def cb_search_clicked (self, entry):
+ """Do a search."""
+ text = entry.get_text()
+ if text != "":
+ if "/" not in text:
+ text = "/.*"+text # only look for package names
+
+ packages = system.find_packages(text, with_version = False)
+
+ if packages == []:
+ nothing_found_dialog()
+ else:
+ if len(packages) == 1:
+ self.jump_to(packages[0])
+ else:
+ SearchWindow(self.window, packages, self.jump_to)
+
+ return True
+
+ def cb_search_changed (self, *args):
+ """
+ Called when the user enters something in the search field.
+ Updates the packages according to the search expression.
+ """
+ if not self.__searchChanged and self.cfg.get_boolean("searchOnType", section="GUI"):
+ self.__searchChanged = True
+
+ def __update():
+ self.__searchChanged = False
+ txt = self.searchEntry.get_text()
+
+ if txt or self.db.restrict:
+ self.db.restrict = txt
+
+ self.refresh_stores()
+ self.catList.get_selection().select_path("0") # XXX make this smarter
+
+ return False # not again ;)
+
+ gobject.timeout_add(100, __update)
+
+ def cb_delete_search_clicked (self, *args):
+ self.searchEntry.set_text("")
+ return True
+
+ def cb_preferences_clicked (self, *args):
+ """
+ User wants to open preferences.
+ """
+ PreferenceWindow(self.window, self.cfg, self.console.set_font_from_string, self.set_uri_hook, self.set_notebook_tabpos, self.fill_cat_store)
+ return True
+
+ def cb_about_clicked (self, *args):
+ """
+ User wants to open about dialog.
+ """
+ AboutWindow(self.window)
+ return True
+
+ def cb_plugins_clicked (self, *args):
+ """
+ User wants to open plugin dialog.
+ """
+ queue = plugin.get_plugin_queue()
+ if queue is None:
+ plugins = []
+ else:
+ plugins = list(queue.get_plugins())
+
+ PluginWindow(self.window, plugins, self.queue)
+ return True
+
+ def cb_show_updates_clicked (self, *args):
+ """
+ Show the list of updateble packages.
+ """
+
+ def __update():
+ def cb_idle_show(packages):
+ """
+ Callback opening the menu when the calculation is finished.
+
+ @returns: False to signal that it is finished
+ """
+ UpdateWindow(self.window, packages, self.queue, self.jump_to)
+ return False
+
+ watch = gtk.gdk.Cursor(gtk.gdk.WATCH)
+ self.window.window.set_cursor(watch)
+
+ packages = []
+ try:
+ packages.extend(system.get_updated_packages())
+ finally:
+ self.window.window.set_cursor(None)
+
+ gobject.idle_add(cb_idle_show, packages)
+
+ GtkThread(name="Show Updates Thread", target = __update).start()
+ return True
+
+ def cb_show_installed_toggled (self, *args):
+ """
+ Toggle the "show only installed" option.
+ """
+ self.showAll = not self.showAll
+ self.refresh_stores()
+
+ def cb_right_click (self, object, event):
+ """
+ Called when the user right clicks somewhere.
+ Used to display a menu.
+
+ This method should handle ALL such menus.
+
+ @param object: the object/widget where the click is done
+ @type object: gtk.Widget
+ @param event: the event triggered
+ @type event: gtk.gdk.Event
+ """
+
+ if event.type == gtk.gdk.BUTTON_PRESS and event.button == 3: # 3 == right click
+ x = int(event.x)
+ y = int(event.y)
+ time = event.time
+
+ if object == self.console:
+ self.consolePopup.popup(None, None, None, event.button, time)
+ else:
+ return False
+ else:
+ return False
+
+ def cb_oneshot_clicked (self, *args):
+ """
+ Mark a package as oneshot.
+ """
+ sel = self.queueList.get_selection()
+ store, it = sel.get_selected()
+ if it:
+ if self.queueTree.is_in_emerge(it) and self.queueTree.iter_has_parent(it):
+ package = store.get_value(it, 0)
+ set = (package not in self.queue.oneshotmerge)
+
+ self.queue.append(package, update = True, oneshot = set, forceUpdate = True)
+
+ def cb_pause_emerge (self, curr):
+ """
+ This method returns a callback for a "pause emerge" toggle button.
+ It is needed as there are different toggle buttons of this type and if one is clicked,
+ the others should be marked too.
+
+ @param curr: The button to return the callback for.
+ @type curr: gtk.ToggleButton
+ """
+ def pause (cb):
+ """
+ The actual callback.
+
+ Mark all other buttons too.
+
+ @param cb: The button which got toggled.
+ @type cb: gtk.ToggleButton
+ """
+
+ # pause or continue
+ self.emergePaused = cb.get_active()
+ if not self.emergePaused:
+ self.queue.continue_emerge()
+ #self.tray.set_from_file(APP_ICON)
+ else:
+ self.queue.stop_emerge()
+ #self.tray.set_from_file(os.path.join(ICON_DIR, "pausing.png"))
+
+ # block the handlers of the other buttons
+ # so that calling "set_active" does not call this callback recursivly
+ for v in self.pauseItems.itervalues():
+ v[0].handler_block(v[1])
+
+ # mark the others
+ for k, v in self.pauseItems.iteritems():
+ if k != curr:
+ v[0].set_active(self.emergePaused)
+
+ # unblock
+ for v in self.pauseItems.itervalues():
+ v[0].handler_unblock(v[1])
+
+ return False
+ return pause
+
+ def cb_kill_clicked (self, *args):
+ """
+ Kill emerge.
+ """
+ self.queue.kill_emerge()
+ if self.emergePaused: # unmark the "pause emerge" buttons
+ self.pauseItems["menu"][0].set_active(False) # calling one button is enough (see: cb_pause_emerge)
+
+ def cb_copy_clicked (self, *args):
+ """
+ Copy marked text in the terminal to clipboard.
+ """
+ self.console.copy_clipboard()
+
+ def cb_delete (self, *args):
+ """
+ Called when the user wants to quit the application.
+
+ Asks the user for confirmation if there is something in the queue.
+ Also saves session data.
+ """
+
+ self.__save_queue = False
+
+ if not self.queue.is_empty():
+ ret = queue_not_empty_dialog()
+ if ret == gtk.RESPONSE_CANCEL:
+ return True
+ else: # there is sth in queue AND the user still wants to close -> kill emerge
+ self.__save_queue = (ret == gtk.RESPONSE_YES)
+ self.queue.kill_emerge()
+
+ # write session
+ self.session.save()
+
+ return False
+
+ def cb_minimized (self, window, event):
+ """
+ User wants to minimize the window.
+ If it is possible to minimize to tray, it is done.
+ """
+
+ if self.tray and self.cfg.get_boolean("hideOnMinimize", "GUI"):
+ if event.changed_mask & gtk.gdk.WINDOW_STATE_ICONIFIED:
+ if event.new_window_state & gtk.gdk.WINDOW_STATE_ICONIFIED:
+ self.window.hide()
+ return True
+
+ return False
+
+ def cb_systray_activated (self, *args):
+ """
+ Systray was activated. Show or hide the window.
+ """
+ if self.window.iconify_initially:
+ self.window.deiconify()
+ self.window.show()
+ self.window.window.show()
+ else:
+ self.window.iconify()
+
+ def cb_close (self, *args):
+ """
+ "Close" menu entry called.
+ Emulate normal quitting.
+ """
+ if not self.cb_delete(): # do the checks
+ self.window.destroy()
+
+ def cb_destroy (self, *args):
+ """
+ Calls main_quit().
+ """
+ gtk.main_quit()
+
+ def main (self):
+ """
+ Main.
+ """
+ gobject.threads_init()
+ # now subthreads can run normally, but are not allowed to touch the GUI. If threads should change sth there - use gobject.idle_add().
+ # for more informations on threading and gtk: http://www.async.com.br/faq/pygtk/index.py?req=show&file=faq20.006.htp
+ plugin.hook("main")(gtk.main)()
diff --git a/portato/gui/windows/plugin.py b/portato/gui/windows/plugin.py
index a0694be..eccf302 100644
--- a/portato/gui/windows/plugin.py
+++ b/portato/gui/windows/plugin.py
@@ -21,161 +21,161 @@ from ...backend.exceptions import PackageNotFoundException, BlockedException
from ...helper import debug
class PluginWindow (AbstractDialog):
-
- statsStore = gtk.ListStore(str)
-
- for s in (_("Disabled"), _("Temporarily enabled"), _("Enabled"), _("Temporarily disabled")):
- statsStore.append([s])
-
- def __init__ (self, parent, plugins, queue = None):
- """Constructor.
-
- @param parent: the parent window
- @type parent: gtk.Window"""
-
- AbstractDialog.__init__(self, parent)
- self.plugins = plugins
- self.queue = queue
- self.changedPlugins = {}
- self.inst = []
- self.ninst = []
-
- self.buttons = map(self.tree.get_widget, ("disabledRB", "tempEnabledRB", "enabledRB", "tempDisabledRB"))
- map(lambda b: b.set_mode(False), self.buttons)
-
- self.descrLabel = self.tree.get_widget("descrLabel")
- self.authorLabel = self.tree.get_widget("authorLabel")
-
- self.depExpander = self.tree.get_widget("depExpander")
- self.installBtn = self.tree.get_widget("installBtn")
- self.depList = self.tree.get_widget("depList")
- self.build_dep_list()
-
- self.buttonBox = self.tree.get_widget("buttonBox")
-
- self.instIcon = self.window.render_icon(gtk.STOCK_YES, gtk.ICON_SIZE_MENU)
-
- self.view = self.tree.get_widget("pluginList")
- self.store = gtk.ListStore(str)
-
- self.view.set_model(self.store)
-
- cell = gtk.CellRendererText()
- col = gtk.TreeViewColumn("Plugin", cell, markup = 0)
- self.view.append_column(col)
-
- for p in plugins:
- self.store.append(["<b>%s</b>" % p.name])
-
- self.view.get_selection().connect("changed", self.cb_list_selection)
-
- self.window.show_all()
-
- def build_dep_list (self):
- store = gtk.ListStore(gtk.gdk.Pixbuf, str)
-
- self.depList.set_model(store)
-
- col = gtk.TreeViewColumn()
-
- cell = gtk.CellRendererPixbuf()
- col.pack_start(cell, False)
- col.add_attribute(cell, "pixbuf", 0)
-
- cell = gtk.CellRendererText()
- col.pack_start(cell, True)
- col.add_attribute(cell, "text", 1)
-
- self.depList.append_column(col)
-
- def fill_dep_list (self, inst = [], ninst = []):
- store = self.depList.get_model()
- store.clear()
-
- for dep in inst:
- store.append([self.instIcon, dep])
- for dep in ninst:
- store.append([None, dep])
-
- def cb_state_toggled (self, rb):
-
- plugin = self.get_actual()
-
- if plugin:
- state = self.buttons.index(rb)
-
- self.changedPlugins[plugin] = state
- debug("new changed plugins: %s => %d", plugin.name, state)
-
- def cb_ok_clicked (self, btn):
- for plugin, val in self.changedPlugins.iteritems():
- plugin.status = val
-
- self.close()
- return True
-
- def cb_list_selection (self, selection):
- plugin = self.get_actual()
- self.inst = []
- self.ninst = []
-
- if plugin:
- if not plugin.description:
- self.descrLabel.hide()
- else:
- self.descrLabel.set_markup(plugin.description)
- self.descrLabel.show()
-
- self.authorLabel.set_label(plugin.author)
-
- status = self.changedPlugins.get(plugin, plugin.status)
- self.buttons[status].set_active(True)
-
- if plugin.deps:
-
- for dep in plugin.deps:
- if system.find_packages(dep, pkgSet = system.SET_INSTALLED, with_version = False):
- self.inst.append(dep)
- else:
- self.ninst.append(dep)
-
- self.fill_dep_list(self.inst, self.ninst)
- self.depExpander.show()
-
- self.installBtn.show()
- self.installBtn.set_sensitive(bool(self.ninst))
-
- else:
- self.installBtn.hide()
- self.depExpander.hide()
-
- self.buttonBox.set_sensitive(not plugin._unresolved_deps and plugin.status != plugin.STAT_HARD_DISABLED)
-
- def cb_install_clicked (self, *args):
- if not self.queue:
- return False
-
- for cpv in self.ninst:
-
- pkg = system.find_best_match(cpv, masked = False, only_cpv = True)
- if not pkg:
- pkg = system.find_best_match(cpv, masked = True, only_cpv = True)
-
- try:
- try:
- self.queue.append(pkg, type = "install")
- except PackageNotFoundException, e:
- if unmask_dialog(e[0]) == gtk.RESPONSE_YES:
- self.queue.append(pkg, type = "install", unmask = True)
- except BlockedException, e:
- blocked_dialog(e[0], e[1])
-
- return True
-
- def get_actual (self):
- store, it = self.view.get_selection().get_selected()
-
- if it:
- return self.plugins[int(store.get_path(it)[0])]
- else:
- return None
+
+ statsStore = gtk.ListStore(str)
+
+ for s in (_("Disabled"), _("Temporarily enabled"), _("Enabled"), _("Temporarily disabled")):
+ statsStore.append([s])
+
+ def __init__ (self, parent, plugins, queue = None):
+ """Constructor.
+
+ @param parent: the parent window
+ @type parent: gtk.Window"""
+
+ AbstractDialog.__init__(self, parent)
+ self.plugins = plugins
+ self.queue = queue
+ self.changedPlugins = {}
+ self.inst = []
+ self.ninst = []
+
+ self.buttons = map(self.tree.get_widget, ("disabledRB", "tempEnabledRB", "enabledRB", "tempDisabledRB"))
+ map(lambda b: b.set_mode(False), self.buttons)
+
+ self.descrLabel = self.tree.get_widget("descrLabel")
+ self.authorLabel = self.tree.get_widget("authorLabel")
+
+ self.depExpander = self.tree.get_widget("depExpander")
+ self.installBtn = self.tree.get_widget("installBtn")
+ self.depList = self.tree.get_widget("depList")
+ self.build_dep_list()
+
+ self.buttonBox = self.tree.get_widget("buttonBox")
+
+ self.instIcon = self.window.render_icon(gtk.STOCK_YES, gtk.ICON_SIZE_MENU)
+
+ self.view = self.tree.get_widget("pluginList")
+ self.store = gtk.ListStore(str)
+
+ self.view.set_model(self.store)
+
+ cell = gtk.CellRendererText()
+ col = gtk.TreeViewColumn("Plugin", cell, markup = 0)
+ self.view.append_column(col)
+
+ for p in plugins:
+ self.store.append(["<b>%s</b>" % p.name])
+
+ self.view.get_selection().connect("changed", self.cb_list_selection)
+
+ self.window.show_all()
+
+ def build_dep_list (self):
+ store = gtk.ListStore(gtk.gdk.Pixbuf, str)
+
+ self.depList.set_model(store)
+
+ col = gtk.TreeViewColumn()
+
+ cell = gtk.CellRendererPixbuf()
+ col.pack_start(cell, False)
+ col.add_attribute(cell, "pixbuf", 0)
+
+ cell = gtk.CellRendererText()
+ col.pack_start(cell, True)
+ col.add_attribute(cell, "text", 1)
+
+ self.depList.append_column(col)
+
+ def fill_dep_list (self, inst = [], ninst = []):
+ store = self.depList.get_model()
+ store.clear()
+
+ for dep in inst:
+ store.append([self.instIcon, dep])
+ for dep in ninst:
+ store.append([None, dep])
+
+ def cb_state_toggled (self, rb):
+
+ plugin = self.get_actual()
+
+ if plugin:
+ state = self.buttons.index(rb)
+
+ self.changedPlugins[plugin] = state
+ debug("new changed plugins: %s => %d", plugin.name, state)
+
+ def cb_ok_clicked (self, btn):
+ for plugin, val in self.changedPlugins.iteritems():
+ plugin.status = val
+
+ self.close()
+ return True
+
+ def cb_list_selection (self, selection):
+ plugin = self.get_actual()
+ self.inst = []
+ self.ninst = []
+
+ if plugin:
+ if not plugin.description:
+ self.descrLabel.hide()
+ else:
+ self.descrLabel.set_markup(plugin.description)
+ self.descrLabel.show()
+
+ self.authorLabel.set_label(plugin.author)
+
+ status = self.changedPlugins.get(plugin, plugin.status)
+ self.buttons[status].set_active(True)
+
+ if plugin.deps:
+
+ for dep in plugin.deps:
+ if system.find_packages(dep, pkgSet = system.SET_INSTALLED, with_version = False):
+ self.inst.append(dep)
+ else:
+ self.ninst.append(dep)
+
+ self.fill_dep_list(self.inst, self.ninst)
+ self.depExpander.show()
+
+ self.installBtn.show()
+ self.installBtn.set_sensitive(bool(self.ninst))
+
+ else:
+ self.installBtn.hide()
+ self.depExpander.hide()
+
+ self.buttonBox.set_sensitive(not plugin._unresolved_deps and plugin.status != plugin.STAT_HARD_DISABLED)
+
+ def cb_install_clicked (self, *args):
+ if not self.queue:
+ return False
+
+ for cpv in self.ninst:
+
+ pkg = system.find_best_match(cpv, masked = False, only_cpv = True)
+ if not pkg:
+ pkg = system.find_best_match(cpv, masked = True, only_cpv = True)
+
+ try:
+ try:
+ self.queue.append(pkg, type = "install")
+ except PackageNotFoundException, e:
+ if unmask_dialog(e[0]) == gtk.RESPONSE_YES:
+ self.queue.append(pkg, type = "install", unmask = True)
+ except BlockedException, e:
+ blocked_dialog(e[0], e[1])
+
+ return True
+
+ def get_actual (self):
+ store, it = self.view.get_selection().get_selected()
+
+ if it:
+ return self.plugins[int(store.get_path(it)[0])]
+ else:
+ return None
diff --git a/portato/gui/windows/preference.py b/portato/gui/windows/preference.py
index dba28b1..94d7ade 100644
--- a/portato/gui/windows/preference.py
+++ b/portato/gui/windows/preference.py
@@ -22,197 +22,197 @@ from ..utils import get_color
from ...helper import debug
class PreferenceWindow (AbstractDialog):
- """Window displaying some preferences."""
-
- # all checkboxes in the window
- # widget name -> option name
- checkboxes = {
- "collapseCatCheck" : ("collapseCats", "GUI"),
- "consoleUpdateCheck" : ("updateConsole", "GUI"),
- "debugCheck" : "debug",
- "deepCheck" : "deep",
- "newUseCheck" : "newuse",
- "maskPerVersionCheck" : "maskPerVersion",
- "minimizeCheck" : ("hideOnMinimize", "GUI"),
- "searchOnTypeCheck" : ("searchOnType", "GUI"),
- "showSlotsCheck" : ("showSlots", "GUI"),
- "systrayCheck" : ("showSystray", "GUI"),
- "testPerVersionCheck" : "keywordPerVersion",
- "titleUpdateCheck" : ("updateTitle", "GUI"),
- "usePerVersionCheck" : "usePerVersion"
- }
-
- # all edits in the window
- # widget name -> option name
- edits = {
- "maskFileEdit" : "maskFile",
- "testFileEdit" : "keywordFile",
- "useFileEdit" : "useFile",
- "syncCommandEdit" : "syncCommand",
- "browserEdit" : ("browserCmd", "GUI")
- }
-
- # the mappings for the tabpos combos
- tabpos = {
- 1 : gtk.POS_TOP,
- 2 : gtk.POS_BOTTOM,
- 3 : gtk.POS_LEFT,
- 4 : gtk.POS_RIGHT
- }
-
- def __init__ (self, parent, cfg, console_fn, linkbtn_fn, tabpos_fn, catmodel_fn):
- """Constructor.
-
- @param parent: parent window
- @type parent: gtk.Window
- @param cfg: configuration object
- @type cfg: gui_helper.Config
- @param console_fn: function to call to set the console font
- @type console_fn: function(string)
- @param linkbtn_fn: function to call to set the linkbutton behavior
- @type linkbtn_fn: function(string)
- @param tabpos_fn: function to call to set the tabposition of the notebooks
- @type tabpos_fn: function(gtk.ComboBox,int)
- @param catmodel_fn: function to call to set the model of the cat list (collapsed/not collapsed)
- @type catmodel_fn: function()"""
-
- AbstractDialog.__init__(self, parent)
-
- # our config
- self.cfg = cfg
-
- # the setter functions
- self.console_fn = console_fn
- self.linkbtn_fn = linkbtn_fn
- self.tabpos_fn = tabpos_fn
- self.catmodel_fn = catmodel_fn
-
- # set the bg-color of the hint
- hintEB = self.tree.get_widget("hintEB")
- hintEB.modify_bg(gtk.STATE_NORMAL, get_color(self.cfg, "prefhint"))
-
- # the checkboxes
- for box, val in self.checkboxes.iteritems():
- if isinstance(val, tuple):
- self.tree.get_widget(box).\
- set_active(self.cfg.get_boolean(val[0], section = val[1]))
- else:
- self.tree.get_widget(box).\
- set_active(self.cfg.get_boolean(val))
-
- # the edits
- for edit, val in self.edits.iteritems():
- if isinstance(val,tuple):
- self.tree.get_widget(edit).\
- set_text(self.cfg.get(val[0], section = val[1]))
- else:
- self.tree.get_widget(edit).\
- set_text(self.cfg.get(val))
-
- # the set list
- self.setList = self.tree.get_widget("setList")
- if system.has_set_support():
- self.fill_setlist()
- self.tree.get_widget("setFrame").show()
-
- # the console font button
- self.consoleFontBtn = self.tree.get_widget("consoleFontBtn")
- self.consoleFontBtn.set_font_name(self.cfg.get("consolefont", section = "GUI"))
-
- # the console title length spin button
- self.titleLengthSpinBtn = self.tree.get_widget("titleLengthSpinBtn")
- self.titleLengthSpinBtn.set_value(int(self.cfg.get("titlelength", section = "GUI")))
-
- # the comboboxes
- self.systemTabCombo = self.tree.get_widget("systemTabCombo")
- self.pkgTabCombo = self.tree.get_widget("packageTabCombo")
-
- for c in (self.systemTabCombo, self.pkgTabCombo):
- m = c.get_model()
- m.clear()
- for i in (_("Top"), _("Bottom"), _("Left"), _("Right")):
- m.append((i,))
-
- self.systemTabCombo.set_active(int(self.cfg.get("systemTabPos", section = "GUI"))-1)
- self.pkgTabCombo.set_active(int(self.cfg.get("packageTabPos", section = "GUI"))-1)
-
- self.window.show_all()
-
- def _save(self):
- """Sets all options in the Config-instance."""
-
- for box, val in self.checkboxes.iteritems():
- if isinstance(val, tuple):
- self.cfg.set_boolean(val[0], self.tree.get_widget(box).get_active(), section = val[1])
- else:
- self.cfg.set_boolean(val, self.tree.get_widget(box).get_active())
-
- for edit, val in self.edits.iteritems():
- if isinstance(val,tuple):
- self.cfg.set(val[0], self.tree.get_widget(edit).get_text(), section = val[1])
- else:
- self.cfg.set(val,self.tree.get_widget(edit).get_text())
-
- if system.has_set_support():
- self.cfg.set("updatesets", ", ".join(sorted(name for enabled, markup, descr, name in self.setList.get_model() if enabled)))
-
- font = self.consoleFontBtn.get_font_name()
- self.cfg.set("consolefont", font, section = "GUI")
- self.console_fn(font)
-
- self.cfg.set("titlelength", str(self.titleLengthSpinBtn.get_value_as_int()), section = "GUI")
-
- pkgPos = self.pkgTabCombo.get_active()+1
- sysPos = self.systemTabCombo.get_active()+1
-
- self.cfg.set("packageTabPos", str(pkgPos), section = "GUI")
- self.cfg.set("systemTabPos", str(sysPos), section = "GUI")
-
- self.tabpos_fn(map(self.tabpos.get, (pkgPos, sysPos)))
-
- self.linkbtn_fn(self.cfg.get("browserCmd", section="GUI"))
-
- self.catmodel_fn()
-
- def fill_setlist (self):
- store = gtk.ListStore(bool, str, str, str)
-
- enabled = [x.strip() for x in self.cfg.get("updatesets").split(",")]
-
- for set, descr in system.get_sets(description = True):
- store.append([set in enabled, "<i>%s</i>" % set, descr, set])
-
- tCell = gtk.CellRendererToggle()
- tCell.set_property("activatable", True)
- tCell.connect("toggled", self.cb_check_toggled) # emulate the normal toggle behavior ...
-
- sCell = gtk.CellRendererText()
-
- col = gtk.TreeViewColumn(_("Package Set"), tCell, active = 0)
- col.pack_start(sCell)
- col.add_attribute(sCell, "markup", 1)
- self.setList.append_column(col)
-
- self.setList.append_column(gtk.TreeViewColumn(_("Description"), sCell, text = 2))
-
- self.setList.set_model(store)
-
- def cb_ok_clicked(self, button):
- """Saves, writes to config-file and closes the window."""
- self._save()
- try:
- self.cfg.write()
- except IOError, e:
- io_ex_dialog(e)
-
- self.window.destroy()
+ """Window displaying some preferences."""
+
+ # all checkboxes in the window
+ # widget name -> option name
+ checkboxes = {
+ "collapseCatCheck" : ("collapseCats", "GUI"),
+ "consoleUpdateCheck" : ("updateConsole", "GUI"),
+ "debugCheck" : "debug",
+ "deepCheck" : "deep",
+ "newUseCheck" : "newuse",
+ "maskPerVersionCheck" : "maskPerVersion",
+ "minimizeCheck" : ("hideOnMinimize", "GUI"),
+ "searchOnTypeCheck" : ("searchOnType", "GUI"),
+ "showSlotsCheck" : ("showSlots", "GUI"),
+ "systrayCheck" : ("showSystray", "GUI"),
+ "testPerVersionCheck" : "keywordPerVersion",
+ "titleUpdateCheck" : ("updateTitle", "GUI"),
+ "usePerVersionCheck" : "usePerVersion"
+ }
+
+ # all edits in the window
+ # widget name -> option name
+ edits = {
+ "maskFileEdit" : "maskFile",
+ "testFileEdit" : "keywordFile",
+ "useFileEdit" : "useFile",
+ "syncCommandEdit" : "syncCommand",
+ "browserEdit" : ("browserCmd", "GUI")
+ }
+
+ # the mappings for the tabpos combos
+ tabpos = {
+ 1 : gtk.POS_TOP,
+ 2 : gtk.POS_BOTTOM,
+ 3 : gtk.POS_LEFT,
+ 4 : gtk.POS_RIGHT
+ }
+
+ def __init__ (self, parent, cfg, console_fn, linkbtn_fn, tabpos_fn, catmodel_fn):
+ """Constructor.
+
+ @param parent: parent window
+ @type parent: gtk.Window
+ @param cfg: configuration object
+ @type cfg: gui_helper.Config
+ @param console_fn: function to call to set the console font
+ @type console_fn: function(string)
+ @param linkbtn_fn: function to call to set the linkbutton behavior
+ @type linkbtn_fn: function(string)
+ @param tabpos_fn: function to call to set the tabposition of the notebooks
+ @type tabpos_fn: function(gtk.ComboBox,int)
+ @param catmodel_fn: function to call to set the model of the cat list (collapsed/not collapsed)
+ @type catmodel_fn: function()"""
+
+ AbstractDialog.__init__(self, parent)
+
+ # our config
+ self.cfg = cfg
+
+ # the setter functions
+ self.console_fn = console_fn
+ self.linkbtn_fn = linkbtn_fn
+ self.tabpos_fn = tabpos_fn
+ self.catmodel_fn = catmodel_fn
+
+ # set the bg-color of the hint
+ hintEB = self.tree.get_widget("hintEB")
+ hintEB.modify_bg(gtk.STATE_NORMAL, get_color(self.cfg, "prefhint"))
+
+ # the checkboxes
+ for box, val in self.checkboxes.iteritems():
+ if isinstance(val, tuple):
+ self.tree.get_widget(box).\
+ set_active(self.cfg.get_boolean(val[0], section = val[1]))
+ else:
+ self.tree.get_widget(box).\
+ set_active(self.cfg.get_boolean(val))
+
+ # the edits
+ for edit, val in self.edits.iteritems():
+ if isinstance(val,tuple):
+ self.tree.get_widget(edit).\
+ set_text(self.cfg.get(val[0], section = val[1]))
+ else:
+ self.tree.get_widget(edit).\
+ set_text(self.cfg.get(val))
+
+ # the set list
+ self.setList = self.tree.get_widget("setList")
+ if system.has_set_support():
+ self.fill_setlist()
+ self.tree.get_widget("setFrame").show()
+
+ # the console font button
+ self.consoleFontBtn = self.tree.get_widget("consoleFontBtn")
+ self.consoleFontBtn.set_font_name(self.cfg.get("consolefont", section = "GUI"))
+
+ # the console title length spin button
+ self.titleLengthSpinBtn = self.tree.get_widget("titleLengthSpinBtn")
+ self.titleLengthSpinBtn.set_value(int(self.cfg.get("titlelength", section = "GUI")))
+
+ # the comboboxes
+ self.systemTabCombo = self.tree.get_widget("systemTabCombo")
+ self.pkgTabCombo = self.tree.get_widget("packageTabCombo")
+
+ for c in (self.systemTabCombo, self.pkgTabCombo):
+ m = c.get_model()
+ m.clear()
+ for i in (_("Top"), _("Bottom"), _("Left"), _("Right")):
+ m.append((i,))
+
+ self.systemTabCombo.set_active(int(self.cfg.get("systemTabPos", section = "GUI"))-1)
+ self.pkgTabCombo.set_active(int(self.cfg.get("packageTabPos", section = "GUI"))-1)
+
+ self.window.show_all()
+
+ def _save(self):
+ """Sets all options in the Config-instance."""
+
+ for box, val in self.checkboxes.iteritems():
+ if isinstance(val, tuple):
+ self.cfg.set_boolean(val[0], self.tree.get_widget(box).get_active(), section = val[1])
+ else:
+ self.cfg.set_boolean(val, self.tree.get_widget(box).get_active())
+
+ for edit, val in self.edits.iteritems():
+ if isinstance(val,tuple):
+ self.cfg.set(val[0], self.tree.get_widget(edit).get_text(), section = val[1])
+ else:
+ self.cfg.set(val,self.tree.get_widget(edit).get_text())
+
+ if system.has_set_support():
+ self.cfg.set("updatesets", ", ".join(sorted(name for enabled, markup, descr, name in self.setList.get_model() if enabled)))
+
+ font = self.consoleFontBtn.get_font_name()
+ self.cfg.set("consolefont", font, section = "GUI")
+ self.console_fn(font)
+
+ self.cfg.set("titlelength", str(self.titleLengthSpinBtn.get_value_as_int()), section = "GUI")
+
+ pkgPos = self.pkgTabCombo.get_active()+1
+ sysPos = self.systemTabCombo.get_active()+1
+
+ self.cfg.set("packageTabPos", str(pkgPos), section = "GUI")
+ self.cfg.set("systemTabPos", str(sysPos), section = "GUI")
+
+ self.tabpos_fn(map(self.tabpos.get, (pkgPos, sysPos)))
+
+ self.linkbtn_fn(self.cfg.get("browserCmd", section="GUI"))
+
+ self.catmodel_fn()
+
+ def fill_setlist (self):
+ store = gtk.ListStore(bool, str, str, str)
+
+ enabled = [x.strip() for x in self.cfg.get("updatesets").split(",")]
+
+ for set, descr in system.get_sets(description = True):
+ store.append([set in enabled, "<i>%s</i>" % set, descr, set])
+
+ tCell = gtk.CellRendererToggle()
+ tCell.set_property("activatable", True)
+ tCell.connect("toggled", self.cb_check_toggled) # emulate the normal toggle behavior ...
+
+ sCell = gtk.CellRendererText()
+
+ col = gtk.TreeViewColumn(_("Package Set"), tCell, active = 0)
+ col.pack_start(sCell)
+ col.add_attribute(sCell, "markup", 1)
+ self.setList.append_column(col)
+
+ self.setList.append_column(gtk.TreeViewColumn(_("Description"), sCell, text = 2))
+
+ self.setList.set_model(store)
+
+ def cb_ok_clicked(self, button):
+ """Saves, writes to config-file and closes the window."""
+ self._save()
+ try:
+ self.cfg.write()
+ except IOError, e:
+ io_ex_dialog(e)
+
+ self.window.destroy()
- def cb_cancel_clicked (self, button):
- """Just closes - w/o saving."""
- self.window.destroy()
+ def cb_cancel_clicked (self, button):
+ """Just closes - w/o saving."""
+ self.window.destroy()
- def cb_check_toggled (self, cell, path):
- # for whatever reason we have to define normal toggle behavior explicitly
- store = self.setList.get_model()
- store[path][0] = not store[path][0]
- return True
+ def cb_check_toggled (self, cell, path):
+ # for whatever reason we have to define normal toggle behavior explicitly
+ store = self.setList.get_model()
+ store[path][0] = not store[path][0]
+ return True
diff --git a/portato/gui/windows/search.py b/portato/gui/windows/search.py
index e776dd1..415cbfe 100644
--- a/portato/gui/windows/search.py
+++ b/portato/gui/windows/search.py
@@ -17,59 +17,59 @@ from .basic import AbstractDialog
from ...helper import debug
class SearchWindow (AbstractDialog):
- """A window showing the results of a search process."""
-
- def __init__ (self, parent, list, jump_to):
- """Constructor.
+ """A window showing the results of a search process."""
+
+ def __init__ (self, parent, list, jump_to):
+ """Constructor.
- @param parent: parent-window
- @type parent: gtk.Window
- @param list: list of results to show
- @type list: string[]
- @param jump_to: function to call if "OK"-Button is hit
- @type jump_to: function(string)"""
-
- AbstractDialog.__init__(self, parent)
-
- self.jump_to = jump_to # function to call for jumping
- self.list = list
- self.list.sort()
-
- # combo box
- self.searchList = self.tree.get_widget("searchList")
- self.build_sort_list()
- self.searchList.get_selection().select_path(0)
+ @param parent: parent-window
+ @type parent: gtk.Window
+ @param list: list of results to show
+ @type list: string[]
+ @param jump_to: function to call if "OK"-Button is hit
+ @type jump_to: function(string)"""
+
+ AbstractDialog.__init__(self, parent)
+
+ self.jump_to = jump_to # function to call for jumping
+ self.list = list
+ self.list.sort()
+
+ # combo box
+ self.searchList = self.tree.get_widget("searchList")
+ self.build_sort_list()
+ self.searchList.get_selection().select_path(0)
- # finished --> show
- self.window.show_all()
+ # finished --> show
+ self.window.show_all()
- def build_sort_list (self):
- """Builds the sort list."""
-
- store = gtk.ListStore(str)
- self.searchList.set_model(store)
+ def build_sort_list (self):
+ """Builds the sort list."""
+
+ store = gtk.ListStore(str)
+ self.searchList.set_model(store)
- # build categories
- for p in self.list:
- store.append(["%s/<b>%s</b>" % tuple(p.split("/"))])
+ # build categories
+ for p in self.list:
+ store.append(["%s/<b>%s</b>" % tuple(p.split("/"))])
- cell = gtk.CellRendererText()
- col = gtk.TreeViewColumn(_("Results"), cell, markup = 0)
- self.searchList.append_column(col)
+ cell = gtk.CellRendererText()
+ col = gtk.TreeViewColumn(_("Results"), cell, markup = 0)
+ self.searchList.append_column(col)
- def ok (self, *args):
- self.jump()
- self.close()
-
- def jump (self, *args):
- model, iter = self.searchList.get_selection().get_selected()
- self.jump_to(self.list[model.get_path(iter)[0]])
+ def ok (self, *args):
+ self.jump()
+ self.close()
+
+ def jump (self, *args):
+ model, iter = self.searchList.get_selection().get_selected()
+ self.jump_to(self.list[model.get_path(iter)[0]])
- def cb_key_pressed_combo (self, widget, event):
- """Emulates a ok-button-click."""
- keyname = gtk.gdk.keyval_name(event.keyval)
- if keyname == "Return": # take it as an "OK" if Enter is pressed
- self.jump()
- return True
- else:
- return False
+ def cb_key_pressed_combo (self, widget, event):
+ """Emulates a ok-button-click."""
+ keyname = gtk.gdk.keyval_name(event.keyval)
+ if keyname == "Return": # take it as an "OK" if Enter is pressed
+ self.jump()
+ return True
+ else:
+ return False
diff --git a/portato/gui/windows/splash.py b/portato/gui/windows/splash.py
index df19a9b..c27f74f 100644
--- a/portato/gui/windows/splash.py
+++ b/portato/gui/windows/splash.py
@@ -18,33 +18,33 @@ from .basic import Window
from ...constants import VERSION, APP_ICON
class SplashScreen (Window):
-
- def __init__ (self, startStr = ""):
- Window.__init__(self)
-
- self.image = self.tree.get_widget("image")
- self.genLabel = self.tree.get_widget("generalLabel")
- self.descrLabel = self.tree.get_widget("descrLabel")
-
- self.image.set_from_file(APP_ICON)
- self.genLabel.set_label("<b><big>Portato %s ...</big></b>" % VERSION)
-
- self.set_descr(startStr)
-
- def set_descr (self, string):
- self.descrLabel.set_label(_("... is starting up: %s") % string)
- self.do_iteration()
-
- def do_iteration (self):
- while gtk.events_pending():
- gtk.main_iteration()
-
- def show (self):
- self.window.show_all()
- self.do_iteration()
-
- def hide (self):
- self.window.hide()
- self.do_iteration()
-
- __call__ = set_descr
+
+ def __init__ (self, startStr = ""):
+ Window.__init__(self)
+
+ self.image = self.tree.get_widget("image")
+ self.genLabel = self.tree.get_widget("generalLabel")
+ self.descrLabel = self.tree.get_widget("descrLabel")
+
+ self.image.set_from_file(APP_ICON)
+ self.genLabel.set_label("<b><big>Portato %s ...</big></b>" % VERSION)
+
+ self.set_descr(startStr)
+
+ def set_descr (self, string):
+ self.descrLabel.set_label(_("... is starting up: %s") % string)
+ self.do_iteration()
+
+ def do_iteration (self):
+ while gtk.events_pending():
+ gtk.main_iteration()
+
+ def show (self):
+ self.window.show_all()
+ self.do_iteration()
+
+ def hide (self):
+ self.window.hide()
+ self.do_iteration()
+
+ __call__ = set_descr
diff --git a/portato/gui/windows/update.py b/portato/gui/windows/update.py
index e369c49..297f666 100644
--- a/portato/gui/windows/update.py
+++ b/portato/gui/windows/update.py
@@ -21,97 +21,97 @@ from ...helper import debug
class UpdateWindow (AbstractDialog):
- def __init__ (self, parent, packages, queue, jump_to):
- AbstractDialog.__init__(self, parent)
-
- self.queue = queue
- self.jump = jump_to
-
- self.packages = system.sort_package_list(packages)
-
- self.build_list()
-
- self.window.show_all()
-
- def build_list (self):
-
- store = gtk.ListStore(bool, str)
- self.view = self.tree.get_widget("packageList")
- self.view.set_model(store)
-
- cell = gtk.CellRendererText()
- tCell = gtk.CellRendererToggle()
- tCell.set_property("activatable", True)
- tCell.connect("toggled", self.cb_check_toggled) # emulate the normal toggle behavior ...
-
- self.view.append_column(gtk.TreeViewColumn(_("Enabled"), tCell, active = 0))
- self.view.append_column(gtk.TreeViewColumn(_("Package"), cell, text = 1))
-
- for p in self.packages:
- store.append([False, p.get_cpv()])
-
- def cb_set_size (self, *args):
- """
- This callback is called shortly before drawing.
- It calculates the optimal size of the window.
- The optimum is defined as: as large as possible w/o scrollbars
- """
-
- bb = self.tree.get_widget("updateBB")
- vals = (self.view.get_vadjustment().upper+bb.size_request()[1]+10, # max size of list + size of BB + constant
- self.parent.get_size()[1]) # size of the parent -> maximum size
- debug("Size values for the list and for the parent: %d / %d", *vals)
- val = int(min(vals))
- debug("Minimum value: %d", val)
- self.window.set_geometry_hints(self.window, min_height = val)
-
- def cb_select_all_clicked (self, btn):
- model = self.view.get_model()
- iter = model.get_iter_first()
-
- while iter:
- model.set_value(iter, 0, True)
- iter = model.iter_next(iter)
-
- return True
-
- def cb_install_clicked (self, btn):
- model = self.view.get_model()
- iter = model.get_iter_first()
- if iter is None: return
-
- items = []
- while iter:
- if model.get_value(iter, 0):
- items.append(model.get_value(iter, 1))
- iter = model.iter_next(iter)
-
- for item in items:
- try:
- try:
- self.queue.append(item, type = "install", oneshot = True)
- except PackageNotFoundException, e:
- if unmask_dialog(e[0]) == gtk.RESPONSE_YES :
- self.queue.append(item, type = "install", unmask = True, oneshot = True)
-
- except BlockedException, e:
- blocked_dialog(e[0], e[1])
-
- self.close()
- return True
-
- def cb_package_selected (self, view):
- sel = view.get_selection()
- store, it = sel.get_selected()
- if it:
- package = system.new_package(store.get_value(it, 1))
-
- self.jump(package.get_cp(), package.get_version())
-
- return True
-
- def cb_check_toggled (self, cell, path):
- # for whatever reason we have to define normal toggle behavior explicitly
- store = self.view.get_model()
- store[path][0] = not store[path][0]
- return True
+ def __init__ (self, parent, packages, queue, jump_to):
+ AbstractDialog.__init__(self, parent)
+
+ self.queue = queue
+ self.jump = jump_to
+
+ self.packages = system.sort_package_list(packages)
+
+ self.build_list()
+
+ self.window.show_all()
+
+ def build_list (self):
+
+ store = gtk.ListStore(bool, str)
+ self.view = self.tree.get_widget("packageList")
+ self.view.set_model(store)
+
+ cell = gtk.CellRendererText()
+ tCell = gtk.CellRendererToggle()
+ tCell.set_property("activatable", True)
+ tCell.connect("toggled", self.cb_check_toggled) # emulate the normal toggle behavior ...
+
+ self.view.append_column(gtk.TreeViewColumn(_("Enabled"), tCell, active = 0))
+ self.view.append_column(gtk.TreeViewColumn(_("Package"), cell, text = 1))
+
+ for p in self.packages:
+ store.append([False, p.get_cpv()])
+
+ def cb_set_size (self, *args):
+ """
+ This callback is called shortly before drawing.
+ It calculates the optimal size of the window.
+ The optimum is defined as: as large as possible w/o scrollbars
+ """
+
+ bb = self.tree.get_widget("updateBB")
+ vals = (self.view.get_vadjustment().upper+bb.size_request()[1]+10, # max size of list + size of BB + constant
+ self.parent.get_size()[1]) # size of the parent -> maximum size
+ debug("Size values for the list and for the parent: %d / %d", *vals)
+ val = int(min(vals))
+ debug("Minimum value: %d", val)
+ self.window.set_geometry_hints(self.window, min_height = val)
+
+ def cb_select_all_clicked (self, btn):
+ model = self.view.get_model()
+ iter = model.get_iter_first()
+
+ while iter:
+ model.set_value(iter, 0, True)
+ iter = model.iter_next(iter)
+
+ return True
+
+ def cb_install_clicked (self, btn):
+ model = self.view.get_model()
+ iter = model.get_iter_first()
+ if iter is None: return
+
+ items = []
+ while iter:
+ if model.get_value(iter, 0):
+ items.append(model.get_value(iter, 1))
+ iter = model.iter_next(iter)
+
+ for item in items:
+ try:
+ try:
+ self.queue.append(item, type = "install", oneshot = True)
+ except PackageNotFoundException, e:
+ if unmask_dialog(e[0]) == gtk.RESPONSE_YES :
+ self.queue.append(item, type = "install", unmask = True, oneshot = True)
+
+ except BlockedException, e:
+ blocked_dialog(e[0], e[1])
+
+ self.close()
+ return True
+
+ def cb_package_selected (self, view):
+ sel = view.get_selection()
+ store, it = sel.get_selected()
+ if it:
+ package = system.new_package(store.get_value(it, 1))
+
+ self.jump(package.get_cp(), package.get_version())
+
+ return True
+
+ def cb_check_toggled (self, cell, path):
+ # for whatever reason we have to define normal toggle behavior explicitly
+ store = self.view.get_model()
+ store[path][0] = not store[path][0]
+ return True
diff --git a/portato/gui/wrapper.py b/portato/gui/wrapper.py
index 2c492d4..a160e2f 100644
--- a/portato/gui/wrapper.py
+++ b/portato/gui/wrapper.py
@@ -16,316 +16,316 @@ import vte
from ..helper import debug
class GtkTree (object):
- """The implementation of the abstract tree."""
-
- def __init__ (self, tree, col = 0):
- """Constructor.
-
- @param tree: original tree
- @type tree: gtk.TreeStore
- @param col: the column where the cpv is stored
- @type col: int"""
-
- self.tree = tree
- self.cpv_col = col
- self.emergeIt = None
- self.unmergeIt = None
- self.updateIt = None
-
- def build_append_value (self, cpv, oneshot = False, update = False, downgrade = False, version = None, useChange = []):
- """
- Builds the list, which is going to be passed to append.
-
- @param cpv: the cpv
- @type cpv: string (cpv)
- @param oneshot: True if oneshot
- @type oneshot: boolean
- @param update: True if this is an update
- @type update: boolean
- @param downgrade: True if this is a downgrade
- @type downgrade: boolean
- @param version: the version we update from
- @type version: string
- @param useChange: list of changed useflags; use "-use" for removed and "+use" for added flags
- @type useChange: string[]
-
- @returns: the created list
- @rtype: list
- """
-
- string = ""
-
- if oneshot:
- string += "<i>%s</i>" % _("oneshot")
-
- if update:
- if oneshot: string += "; "
- if version is not None:
- string += "<i>%s</i>" % (_("updating from version %s") % version)
- else:
- string += "<i>%s</i>" % _("updating")
-
- elif downgrade:
- if oneshot: string += "; "
- if version is not None:
- string += "<i>%s</i>" % (_("downgrading from version %s") % version)
- else:
- string += "<i>%s</i>" % _("downgrading")
-
- if useChange:
- if update or downgrade or oneshot: string += "; "
- string += "<i><b>%s </b></i>" % _("IUSE changes:")
- useChange.sort()
- string += "<i>%s</i>" % " ".join(useChange)
-
- return [cpv, string, False]
-
- def set_in_progress (self, it, to = True):
- """
- Marks the queue where the given iterator belongs as being in progress.
-
- @param it: one iterator of the queue to mark to
- @type it: Iterator
- @param to: whether to enable or disable
- @type to: boolean
- """
-
- iter = self.first_iter(it)
- if to:
- self.tree.set_value(iter, 1, "<b>%s</b>" % _("(In Progress)"))
- else:
- self.tree.set_value(iter, 1, "")
-
- self.tree.set_value(iter, 2, to)
-
- def is_in_progress (self, it):
- """
- Returns whether the queue where the given iterator belongs to, is marked as "being in progress".
-
- @param it: the iterator
- @type it: Iterator
- @returns: whether the queue is marked "in progress"
- @rtype: boolean
- """
- return self.tree.get_value(it, 2)
-
- def get_emerge_it (self):
- """
- Returns an iterator signaling the top of the emerge section.
-
- @returns: emerge-iterator
- @rtype: Iterator
- """
- if self.emergeIt is None:
- self.emergeIt = self.append(None, ["<b>%s</b>" % _("Install"), "", False])
- return self.emergeIt
-
- def get_unmerge_it (self):
- """
- Returns an iterator signaling the top of the unmerge section.
-
- @returns: unmerge-iterator
- @rtype: Iterator
- """
- if self.unmergeIt is None:
- self.unmergeIt = self.append(None, ["<b>%s</b>" % _("Uninstall"), "", False])
-
- return self.unmergeIt
-
- def get_update_it (self):
- """
- Returns an iterator signaling the top of the update section.
-
- @returns: unmerge-iterator
- @rtype: Iterator
- """
- if self.updateIt is None:
- self.updateIt = self.append(None, ["<b>%s</b>" % _("Update"), "", False])
-
- return self.updateIt
-
- def first_iter (self, it):
- """
- Returns the iterator at the top.
-
- @param it: the iterator
- @type it: Iterator
- @returns: the top iterator
- @rtype: Iterator
- """
- return self.tree.get_iter_from_string(self.tree.get_string_from_iter(it).split(":")[0])
-
- def is_in (self, it, in_it):
- return in_it and self.iter_equal(self.first_iter(it), in_it)
-
- def is_in_emerge (self, it):
- """
- Checks whether an iterator is part of the "Emerge" section.
-
- @param it: the iterator to check
- @type it: Iterator
- @returns: True if the iter is part; False otherwise
- @rtype: boolean
- """
- return self.is_in(it, self.emergeIt)
-
- def is_in_unmerge (self, it):
- """
- Checks whether an iterator is part of the "Unmerge" section.
-
- @param it: the iterator to check
- @type it: Iterator
- @returns: True if the iter is part; False otherwise
- @rtype: boolean
- """
- return self.is_in(it, self.unmergeIt)
-
- def is_in_update (self, it):
- """
- Checks whether an iterator is part of the "Update" section.
-
- @param it: the iterator to check
- @type it: Iterator
- @returns: True if the iter is part; False otherwise
- @rtype: boolean
- """
- return self.is_in(it, self.updateIt)
-
- def iter_has_parent (self, it):
- """
- Returns whether the actual iterator has a parent.
- @param it: the iterator
- @type it: Iterator
- @returns: True if it has a parent it, else False.
- @rtype: boolean
- """
- return (self.tree.iter_parent(it) != None)
-
- def parent_iter (self, it):
- """
- Returns the parent iter.
-
- @param it: the iterator
- @type it: Iterator
- @returns: Parent iterator or None if the current it has no parent.
- @rtype: Iterator; None
- """
- return self.tree.iter_parent(it)
-
- def first_child_iter (self, it):
- """
- Returns the first child iter.
-
- @param it: the iterator
- @type it: Iterator
- @returns: First child iterator or None if the current it has no children.
- @rtype: Iterator; None
- """
-
- return self.tree.iter_children(it)
-
- def iter_has_children (self, it):
- """
- Returns whether the actual iterator has children.
-
- @param it: the iterator
- @type it: Iterator
- @returns: True if it has children, else False.
- @rtype: boolean
- """
-
- return self.tree.iter_has_child(it)
-
- def next_iter (self, it):
- """
- Returns the next iter.
-
- @param it: the iterator
- @type it: Iterator
- @returns: Next iterator or None if the current iter is the last one.
- @rtype: Iterator; None
- """
- return self.tree.iter_next(it)
-
- def get_value (self, it, column):
- """
- Returns the value of the specific column at the given iterator.
-
- @param it: the iterator
- @type it: Iterator
- @param column: the column of the iterator from where to get the value
- @type column: int
- @returns: the value
- @rtype: anything
- """
-
- return self.tree.get_value(it, column)
-
- def iter_equal (self, it, other_it):
- """
- Checks whether to iterators are equal.
-
- @param it: the one iterator to compare
- @type it: Iterator
- @param other_it: the other iterator to compare
- @type other_it: Iterator
- @returns: True if both iterators are equal; False otherwise
- @rtype boolean
- """
- return self.tree.get_string_from_iter(it) == self.tree.get_string_from_iter(other_it)
-
- def append (self, parent = None, values = None):
- """
- Appends some values right after the given parent. If parent is None, it is appended as the first element.
-
- @param parent: the iterator to append the values right after; if None it symbolizes the top
- @type parent: Iterator
- @param values: a list of values which are going to be appended to the tree
- @type values: list
- @returns: Iterator pointing to the newly appended stuff
- @rtype: Iterator
- """
-
- return self.tree.append(parent, values)
-
- def remove (self, it):
- """
- Removes an iterator out of the tree.
- @attention: The iterator can point to anything hereafter. Do not reuse!
-
- @param it: iterator to remove
- @type it: Iterator
- """
-
- if self.emergeIt and self.iter_equal(it, self.emergeIt) : self.emergeIt = None
- elif self.unmergeIt and self.iter_equal(it, self.unmergeIt) : self.unmergeIt = None
- elif self.updateIt and self.iter_equal(it, self.updateIt) : self.updateIt = None
-
- self.tree.remove(it)
-
- def get_original (self):
- """
- Returns the original tree-object.
-
- @returns: original tree-object
- @rtype: tree-object
- """
- return self.tree
-
- def get_cpv_column (self):
- """
- Returns the number of the column where the cpv's are stored.
-
- @returns: column with cpv's
- @rtype: int
- """
- return self.cpv_col
+ """The implementation of the abstract tree."""
+
+ def __init__ (self, tree, col = 0):
+ """Constructor.
+
+ @param tree: original tree
+ @type tree: gtk.TreeStore
+ @param col: the column where the cpv is stored
+ @type col: int"""
+
+ self.tree = tree
+ self.cpv_col = col
+ self.emergeIt = None
+ self.unmergeIt = None
+ self.updateIt = None
+
+ def build_append_value (self, cpv, oneshot = False, update = False, downgrade = False, version = None, useChange = []):
+ """
+ Builds the list, which is going to be passed to append.
+
+ @param cpv: the cpv
+ @type cpv: string (cpv)
+ @param oneshot: True if oneshot
+ @type oneshot: boolean
+ @param update: True if this is an update
+ @type update: boolean
+ @param downgrade: True if this is a downgrade
+ @type downgrade: boolean
+ @param version: the version we update from
+ @type version: string
+ @param useChange: list of changed useflags; use "-use" for removed and "+use" for added flags
+ @type useChange: string[]
+
+ @returns: the created list
+ @rtype: list
+ """
+
+ string = ""
+
+ if oneshot:
+ string += "<i>%s</i>" % _("oneshot")
+
+ if update:
+ if oneshot: string += "; "
+ if version is not None:
+ string += "<i>%s</i>" % (_("updating from version %s") % version)
+ else:
+ string += "<i>%s</i>" % _("updating")
+
+ elif downgrade:
+ if oneshot: string += "; "
+ if version is not None:
+ string += "<i>%s</i>" % (_("downgrading from version %s") % version)
+ else:
+ string += "<i>%s</i>" % _("downgrading")
+
+ if useChange:
+ if update or downgrade or oneshot: string += "; "
+ string += "<i><b>%s </b></i>" % _("IUSE changes:")
+ useChange.sort()
+ string += "<i>%s</i>" % " ".join(useChange)
+
+ return [cpv, string, False]
+
+ def set_in_progress (self, it, to = True):
+ """
+ Marks the queue where the given iterator belongs as being in progress.
+
+ @param it: one iterator of the queue to mark to
+ @type it: Iterator
+ @param to: whether to enable or disable
+ @type to: boolean
+ """
+
+ iter = self.first_iter(it)
+ if to:
+ self.tree.set_value(iter, 1, "<b>%s</b>" % _("(In Progress)"))
+ else:
+ self.tree.set_value(iter, 1, "")
+
+ self.tree.set_value(iter, 2, to)
+
+ def is_in_progress (self, it):
+ """
+ Returns whether the queue where the given iterator belongs to, is marked as "being in progress".
+
+ @param it: the iterator
+ @type it: Iterator
+ @returns: whether the queue is marked "in progress"
+ @rtype: boolean
+ """
+ return self.tree.get_value(it, 2)
+
+ def get_emerge_it (self):
+ """
+ Returns an iterator signaling the top of the emerge section.
+
+ @returns: emerge-iterator
+ @rtype: Iterator
+ """
+ if self.emergeIt is None:
+ self.emergeIt = self.append(None, ["<b>%s</b>" % _("Install"), "", False])
+ return self.emergeIt
+
+ def get_unmerge_it (self):
+ """
+ Returns an iterator signaling the top of the unmerge section.
+
+ @returns: unmerge-iterator
+ @rtype: Iterator
+ """
+ if self.unmergeIt is None:
+ self.unmergeIt = self.append(None, ["<b>%s</b>" % _("Uninstall"), "", False])
+
+ return self.unmergeIt
+
+ def get_update_it (self):
+ """
+ Returns an iterator signaling the top of the update section.
+
+ @returns: unmerge-iterator
+ @rtype: Iterator
+ """
+ if self.updateIt is None:
+ self.updateIt = self.append(None, ["<b>%s</b>" % _("Update"), "", False])
+
+ return self.updateIt
+
+ def first_iter (self, it):
+ """
+ Returns the iterator at the top.
+
+ @param it: the iterator
+ @type it: Iterator
+ @returns: the top iterator
+ @rtype: Iterator
+ """
+ return self.tree.get_iter_from_string(self.tree.get_string_from_iter(it).split(":")[0])
+
+ def is_in (self, it, in_it):
+ return in_it and self.iter_equal(self.first_iter(it), in_it)
+
+ def is_in_emerge (self, it):
+ """
+ Checks whether an iterator is part of the "Emerge" section.
+
+ @param it: the iterator to check
+ @type it: Iterator
+ @returns: True if the iter is part; False otherwise
+ @rtype: boolean
+ """
+ return self.is_in(it, self.emergeIt)
+
+ def is_in_unmerge (self, it):
+ """
+ Checks whether an iterator is part of the "Unmerge" section.
+
+ @param it: the iterator to check
+ @type it: Iterator
+ @returns: True if the iter is part; False otherwise
+ @rtype: boolean
+ """
+ return self.is_in(it, self.unmergeIt)
+
+ def is_in_update (self, it):
+ """
+ Checks whether an iterator is part of the "Update" section.
+
+ @param it: the iterator to check
+ @type it: Iterator
+ @returns: True if the iter is part; False otherwise
+ @rtype: boolean
+ """
+ return self.is_in(it, self.updateIt)
+
+ def iter_has_parent (self, it):
+ """
+ Returns whether the actual iterator has a parent.
+ @param it: the iterator
+ @type it: Iterator
+ @returns: True if it has a parent it, else False.
+ @rtype: boolean
+ """
+ return (self.tree.iter_parent(it) != None)
+
+ def parent_iter (self, it):
+ """
+ Returns the parent iter.
+
+ @param it: the iterator
+ @type it: Iterator
+ @returns: Parent iterator or None if the current it has no parent.
+ @rtype: Iterator; None
+ """
+ return self.tree.iter_parent(it)
+
+ def first_child_iter (self, it):
+ """
+ Returns the first child iter.
+
+ @param it: the iterator
+ @type it: Iterator
+ @returns: First child iterator or None if the current it has no children.
+ @rtype: Iterator; None
+ """
+
+ return self.tree.iter_children(it)
+
+ def iter_has_children (self, it):
+ """
+ Returns whether the actual iterator has children.
+
+ @param it: the iterator
+ @type it: Iterator
+ @returns: True if it has children, else False.
+ @rtype: boolean
+ """
+
+ return self.tree.iter_has_child(it)
+
+ def next_iter (self, it):
+ """
+ Returns the next iter.
+
+ @param it: the iterator
+ @type it: Iterator
+ @returns: Next iterator or None if the current iter is the last one.
+ @rtype: Iterator; None
+ """
+ return self.tree.iter_next(it)
+
+ def get_value (self, it, column):
+ """
+ Returns the value of the specific column at the given iterator.
+
+ @param it: the iterator
+ @type it: Iterator
+ @param column: the column of the iterator from where to get the value
+ @type column: int
+ @returns: the value
+ @rtype: anything
+ """
+
+ return self.tree.get_value(it, column)
+
+ def iter_equal (self, it, other_it):
+ """
+ Checks whether to iterators are equal.
+
+ @param it: the one iterator to compare
+ @type it: Iterator
+ @param other_it: the other iterator to compare
+ @type other_it: Iterator
+ @returns: True if both iterators are equal; False otherwise
+ @rtype boolean
+ """
+ return self.tree.get_string_from_iter(it) == self.tree.get_string_from_iter(other_it)
+
+ def append (self, parent = None, values = None):
+ """
+ Appends some values right after the given parent. If parent is None, it is appended as the first element.
+
+ @param parent: the iterator to append the values right after; if None it symbolizes the top
+ @type parent: Iterator
+ @param values: a list of values which are going to be appended to the tree
+ @type values: list
+ @returns: Iterator pointing to the newly appended stuff
+ @rtype: Iterator
+ """
+
+ return self.tree.append(parent, values)
+
+ def remove (self, it):
+ """
+ Removes an iterator out of the tree.
+ @attention: The iterator can point to anything hereafter. Do not reuse!
+
+ @param it: iterator to remove
+ @type it: Iterator
+ """
+
+ if self.emergeIt and self.iter_equal(it, self.emergeIt) : self.emergeIt = None
+ elif self.unmergeIt and self.iter_equal(it, self.unmergeIt) : self.unmergeIt = None
+ elif self.updateIt and self.iter_equal(it, self.updateIt) : self.updateIt = None
+
+ self.tree.remove(it)
+
+ def get_original (self):
+ """
+ Returns the original tree-object.
+
+ @returns: original tree-object
+ @rtype: tree-object
+ """
+ return self.tree
+
+ def get_cpv_column (self):
+ """
+ Returns the number of the column where the cpv's are stored.
+
+ @returns: column with cpv's
+ @rtype: int
+ """
+ return self.cpv_col
class GtkConsole (vte.Terminal):
- """The implementation of the abstract Console for GTK."""
-
- def reset (self):
- """
- Resets the terminal.
- """
- vte.Terminal.reset(self, True, True)
+ """The implementation of the abstract Console for GTK."""
+
+ def reset (self):
+ """
+ Resets the terminal.
+ """
+ vte.Terminal.reset(self, True, True)
diff --git a/portato/helper.py b/portato/helper.py
index 145716e..2363adb 100644
--- a/portato/helper.py
+++ b/portato/helper.py
@@ -17,126 +17,126 @@ from __future__ import absolute_import
import os, signal, logging, grp
-debug = logging.getLogger("portatoLogger").debug
-info = logging.getLogger("portatoLogger").info
-warning = logging.getLogger("portatoLogger").warning
-error = logging.getLogger("portatoLogger").error
-critical = logging.getLogger("portatoLogger").critical
+debug = logging.getLogger("portatoLogger").debug
+info = logging.getLogger("portatoLogger").info
+warning = logging.getLogger("portatoLogger").warning
+error = logging.getLogger("portatoLogger").error
+critical = logging.getLogger("portatoLogger").critical
def N_ (s):
- return s
+ return s
def set_log_level (lvl):
- logging.getLogger("portatoLogger").setLevel(lvl)
+ logging.getLogger("portatoLogger").setLevel(lvl)
def send_signal_to_group (sig):
- """Sends a signal to all processes of our process group (w/o ourselves).
-
- @param sig: signal number to send
- @type sig: int"""
-
- def handler (sig, stack):
- """Ignores the signal exactly one time and then restores the default."""
- signal.signal(sig, signal.SIG_DFL)
-
- signal.signal(sig, handler)
-
- pgid = os.getpgrp()
- os.killpg(pgid, sig)
+ """Sends a signal to all processes of our process group (w/o ourselves).
+
+ @param sig: signal number to send
+ @type sig: int"""
+
+ def handler (sig, stack):
+ """Ignores the signal exactly one time and then restores the default."""
+ signal.signal(sig, signal.SIG_DFL)
+
+ signal.signal(sig, handler)
+
+ pgid = os.getpgrp()
+ os.killpg(pgid, sig)
def paren_reduce(mystr):
- """
- Take a string and convert all paren enclosed entities into sublists, optionally
- futher splitting the list elements by spaces.
-
- This function is copied from portage.
-
- Example usage:
- >>> paren_reduce('foobar foo ( bar baz )')
- ['foobar', 'foo', ['bar', 'baz']]
-
- @param mystr: The string to reduce
- @type mystr: String
- @rtype: Array
- @return: The reduced string in an array
- """
- mylist = []
- while mystr:
- left_paren = mystr.find("(")
- has_left_paren = left_paren != -1
- right_paren = mystr.find(")")
- has_right_paren = right_paren != -1
- if not has_left_paren and not has_right_paren:
- freesec = mystr
- subsec = None
- tail = ""
- elif mystr[0] == ")":
- return [mylist,mystr[1:]]
- elif has_left_paren and not has_right_paren:
- error(_("Invalid dependency string"))
- return []
- elif has_left_paren and left_paren < right_paren:
- freesec,subsec = mystr.split("(",1)
- subsec,tail = paren_reduce(subsec)
- else:
- subsec,tail = mystr.split(")",1)
- subsec = filter(None, subsec.split(" "))
- return [mylist+subsec,tail]
- mystr = tail
- if freesec:
- mylist = mylist + filter(None, freesec.split(" "))
- if subsec is not None:
- mylist = mylist + [subsec]
- return mylist
+ """
+ Take a string and convert all paren enclosed entities into sublists, optionally
+ futher splitting the list elements by spaces.
+
+ This function is copied from portage.
+
+ Example usage:
+ >>> paren_reduce('foobar foo ( bar baz )')
+ ['foobar', 'foo', ['bar', 'baz']]
+
+ @param mystr: The string to reduce
+ @type mystr: String
+ @rtype: Array
+ @return: The reduced string in an array
+ """
+ mylist = []
+ while mystr:
+ left_paren = mystr.find("(")
+ has_left_paren = left_paren != -1
+ right_paren = mystr.find(")")
+ has_right_paren = right_paren != -1
+ if not has_left_paren and not has_right_paren:
+ freesec = mystr
+ subsec = None
+ tail = ""
+ elif mystr[0] == ")":
+ return [mylist,mystr[1:]]
+ elif has_left_paren and not has_right_paren:
+ error(_("Invalid dependency string"))
+ return []
+ elif has_left_paren and left_paren < right_paren:
+ freesec,subsec = mystr.split("(",1)
+ subsec,tail = paren_reduce(subsec)
+ else:
+ subsec,tail = mystr.split(")",1)
+ subsec = filter(None, subsec.split(" "))
+ return [mylist+subsec,tail]
+ mystr = tail
+ if freesec:
+ mylist = mylist + filter(None, freesec.split(" "))
+ if subsec is not None:
+ mylist = mylist + [subsec]
+ return mylist
def flatten (listOfLists):
- """Flattens the given list of lists.
+ """Flattens the given list of lists.
- @param listOfLists: the list of lists to flatten
- @type listOfLists: list of lists
- @returns: flattend list
- @rtype: list"""
+ @param listOfLists: the list of lists to flatten
+ @type listOfLists: list of lists
+ @returns: flattend list
+ @rtype: list"""
- if not isinstance(listOfLists, list):
- return [listOfLists]
+ if not isinstance(listOfLists, list):
+ return [listOfLists]
- ret = []
- for r in listOfLists:
- ret.extend(flatten(r))
+ ret = []
+ for r in listOfLists:
+ ret.extend(flatten(r))
- return ret
+ return ret
def unique_array(s):
- """Stolen from portage_utils:
- lifted from python cookbook, credit: Tim Peters
- Return a list of the elements in s in arbitrary order, sans duplicates"""
- # assume all elements are hashable, if so, it's linear
- try:
- return list(set(s))
- except TypeError:
- pass
-
- # so much for linear. abuse sort.
- try:
- t = list(s)
- t.sort()
- except TypeError:
- pass
- else:
- n = len(s)
- assert n > 0
- last = t[0]
- lasti = i = 1
- while i < n:
- if t[i] != last:
- t[lasti] = last = t[i]
- lasti += 1
- i += 1
- return t[:lasti]
-
- # blah. back to original portage.unique_array
- u = []
- for x in s:
- if x not in u:
- u.append(x)
- return u
+ """Stolen from portage_utils:
+ lifted from python cookbook, credit: Tim Peters
+ Return a list of the elements in s in arbitrary order, sans duplicates"""
+ # assume all elements are hashable, if so, it's linear
+ try:
+ return list(set(s))
+ except TypeError:
+ pass
+
+ # so much for linear. abuse sort.
+ try:
+ t = list(s)
+ t.sort()
+ except TypeError:
+ pass
+ else:
+ n = len(s)
+ assert n > 0
+ last = t[0]
+ lasti = i = 1
+ while i < n:
+ if t[i] != last:
+ t[lasti] = last = t[i]
+ lasti += 1
+ i += 1
+ return t[:lasti]
+
+ # blah. back to original portage.unique_array
+ u = []
+ for x in s:
+ if x not in u:
+ u.append(x)
+ return u
diff --git a/portato/plistener.py b/portato/plistener.py
index fde54fc..6eb476e 100644
--- a/portato/plistener.py
+++ b/portato/plistener.py
@@ -16,120 +16,120 @@ import os
from subprocess import Popen
try:
- import pynotify
+ import pynotify
except ImportError:
- pynotify = None
+ pynotify = None
from .constants import APP
from .helper import debug, warning
class PListener (object):
- """This class handles the communication between the "listener" and the GUI.
- This listener starts programs as the user while the GUI runs as root.
-
- @ivar _recv: listener socket
- @type _recv: int
- @ivar _send: sender socket
- @type _send: int"""
-
- def set_recv (self, mem, sig, rw):
- self._mem = mem
- self._sig = sig
- self._rw = rw
-
- while True:
- try:
- try:
- self._sig.P()
- self._rw.P()
- len = self._mem.read(NumberOfBytes = 4)
- string = self._mem.read(NumberOfBytes = int(len), offset = 4)
- finally:
- self._rw.V()
-
- data = string.split("\0")
- debug("Listner received: %s", data)
-
- if data[0] == "notify":
- self.do_notify(*data[1:])
- elif data[0] == "cmd":
- self.do_cmd(data[1:])
- elif data[0] == "close":
- break
- except KeyboardInterrupt:
- debug("Got KeyboardInterrupt. Aborting.")
- break
-
- self._mem.remove()
- self._sig.remove()
- self._rw.remove()
-
- self._mem = None
- self._sig = None
- self._rw = None
-
- def do_cmd (self, cmdlist):
- """Starts a command as the user.
-
- @param cmdlist: list of command (options)
- @type cmdlist: string[]"""
-
- Popen(cmdlist)
-
- def do_notify(self, base, descr, icon, urgency = None):
- """Displays a notify.
- This will do nothing if pynotify is not present and/or root is running the listener."""
-
- if pynotify and os.getuid() != 0:
- if not pynotify.is_initted():
- pynotify.init(APP)
-
- n = pynotify.Notification(base, descr, icon)
- if urgency is not None and urgency != "":
- n.set_urgency(int(urgency))
- n.show()
-
- def set_send (self, mem = None, sig = None, rw = None):
- if mem is None or sig is None or rw is None:
- warning(_("Listener has not been started."))
- self._mem = None
- self._sig = None
- self._rw = None
- else:
- import shm_wrapper as shm
-
- self._mem = shm.SharedMemoryHandle(mem)
- self._sig = shm.SemaphoreHandle(sig)
- self._rw = shm.SemaphoreHandle(rw)
-
- def __send (self, string):
- self._rw.P()
- self._sig.Z()
- try:
- self._mem.write("%4d%s" % (len(string), string))
- self._sig.V()
- finally:
- self._rw.V()
-
- def send_notify (self, base = "", descr = "", icon = "", urgency = None):
- if self._sig is None:
- self.do_notify(base, descr, icon, urgency)
- else:
- string = "\0".join(["notify", base, descr, icon])
-
- if urgency is not None:
- string += "\0%d" % urgency
- else:
- string += "\0"
-
- self.__send(string)
-
- def send_cmd (self, cmdlist):
- if self._sig is None:
- self.do_cmd(cmdlist)
- else:
- self.__send("\0".join(["cmd"] +cmdlist))
-
- def close (self):
- if self._sig is not None:
- self.__send("close")
+ """This class handles the communication between the "listener" and the GUI.
+ This listener starts programs as the user while the GUI runs as root.
+
+ @ivar _recv: listener socket
+ @type _recv: int
+ @ivar _send: sender socket
+ @type _send: int"""
+
+ def set_recv (self, mem, sig, rw):
+ self._mem = mem
+ self._sig = sig
+ self._rw = rw
+
+ while True:
+ try:
+ try:
+ self._sig.P()
+ self._rw.P()
+ len = self._mem.read(NumberOfBytes = 4)
+ string = self._mem.read(NumberOfBytes = int(len), offset = 4)
+ finally:
+ self._rw.V()
+
+ data = string.split("\0")
+ debug("Listner received: %s", data)
+
+ if data[0] == "notify":
+ self.do_notify(*data[1:])
+ elif data[0] == "cmd":
+ self.do_cmd(data[1:])
+ elif data[0] == "close":
+ break
+ except KeyboardInterrupt:
+ debug("Got KeyboardInterrupt. Aborting.")
+ break
+
+ self._mem.remove()
+ self._sig.remove()
+ self._rw.remove()
+
+ self._mem = None
+ self._sig = None
+ self._rw = None
+
+ def do_cmd (self, cmdlist):
+ """Starts a command as the user.
+
+ @param cmdlist: list of command (options)
+ @type cmdlist: string[]"""
+
+ Popen(cmdlist)
+
+ def do_notify(self, base, descr, icon, urgency = None):
+ """Displays a notify.
+ This will do nothing if pynotify is not present and/or root is running the listener."""
+
+ if pynotify and os.getuid() != 0:
+ if not pynotify.is_initted():
+ pynotify.init(APP)
+
+ n = pynotify.Notification(base, descr, icon)
+ if urgency is not None and urgency != "":
+ n.set_urgency(int(urgency))
+ n.show()
+
+ def set_send (self, mem = None, sig = None, rw = None):
+ if mem is None or sig is None or rw is None:
+ warning(_("Listener has not been started."))
+ self._mem = None
+ self._sig = None
+ self._rw = None
+ else:
+ import shm_wrapper as shm
+
+ self._mem = shm.SharedMemoryHandle(mem)
+ self._sig = shm.SemaphoreHandle(sig)
+ self._rw = shm.SemaphoreHandle(rw)
+
+ def __send (self, string):
+ self._rw.P()
+ self._sig.Z()
+ try:
+ self._mem.write("%4d%s" % (len(string), string))
+ self._sig.V()
+ finally:
+ self._rw.V()
+
+ def send_notify (self, base = "", descr = "", icon = "", urgency = None):
+ if self._sig is None:
+ self.do_notify(base, descr, icon, urgency)
+ else:
+ string = "\0".join(["notify", base, descr, icon])
+
+ if urgency is not None:
+ string += "\0%d" % urgency
+ else:
+ string += "\0"
+
+ self.__send(string)
+
+ def send_cmd (self, cmdlist):
+ if self._sig is None:
+ self.do_cmd(cmdlist)
+ else:
+ self.__send("\0".join(["cmd"] +cmdlist))
+
+ def close (self):
+ if self._sig is not None:
+ self.__send("close")
diff --git a/portato/plugin.py b/portato/plugin.py
index befc06d..34004cb 100644
--- a/portato/plugin.py
+++ b/portato/plugin.py
@@ -29,524 +29,524 @@ from .backend import system
from . import plugins as plugin_module
class PluginLoadException (Exception):
- """
- Exception signaling a failed plugin loading.
- """
- pass
+ """
+ Exception signaling a failed plugin loading.
+ """
+ pass
class Menu (object):
- """
- One single menu entry.
+ """
+ One single menu entry.
- :IVariables:
+ :IVariables:
- label : string
- The label of the entry. Can have underscores to define the shortcut.
+ label : string
+ The label of the entry. Can have underscores to define the shortcut.
- call
- The function to call, if the entry is clicked.
- """
- __slots__ = ("label", "call")
+ call
+ The function to call, if the entry is clicked.
+ """
+ __slots__ = ("label", "call")
- def __init__ (self, label, call):
- self.label = label
- self.call = call
+ def __init__ (self, label, call):
+ self.label = label
+ self.call = call
class Call (object):
- """
- This class represents an object, which is attached to a specified hook.
+ """
+ This class represents an object, which is attached to a specified hook.
- :IVariables:
+ :IVariables:
- plugin : `Plugin`
- The plugin where this call belongs to.
+ plugin : `Plugin`
+ The plugin where this call belongs to.
- hook : string
- The name of the corresponding hook.
+ hook : string
+ The name of the corresponding hook.
- call
- The function to call.
+ call
+ The function to call.
- type : string
- This is either ``before``, ``after`` or ``override`` and defines the type of the call:
+ type : string
+ This is either ``before``, ``after`` or ``override`` and defines the type of the call:
- before
- access before the original function
- override
- access *instead of* the original function. **USE THIS ONLY IF YOU KNOW WHAT YOU ARE DOING**
- after
- access after the original function has been called
+ before
+ access before the original function
+ override
+ access *instead of* the original function. **USE THIS ONLY IF YOU KNOW WHAT YOU ARE DOING**
+ after
+ access after the original function has been called
- Default: ``before``
+ Default: ``before``
- dep : string
- This defines a plugin which should be executed after/before this one.
- ``"*"`` means all and ``"-*"`` means none.
- """
- __slots__ = ("plugin", "hook", "call", "type", "dep")
+ dep : string
+ This defines a plugin which should be executed after/before this one.
+ ``"*"`` means all and ``"-*"`` means none.
+ """
+ __slots__ = ("plugin", "hook", "call", "type", "dep")
- def __init__ (self, plugin, hook, call, type = "before", dep = None):
- self.plugin = plugin
- self.hook = hook
- self.call = call
- self.type = type
- self.dep = dep
+ def __init__ (self, plugin, hook, call, type = "before", dep = None):
+ self.plugin = plugin
+ self.hook = hook
+ self.call = call
+ self.type = type
+ self.dep = dep
class Hook (object):
- """
- Representing a hook with all the `Call` s for the different types.
- """
-
- __slots__ = ("before", "override", "after")
+ """
+ Representing a hook with all the `Call` s for the different types.
+ """
+
+ __slots__ = ("before", "override", "after")
- def __init__ (self):
- self.before = []
- self.override = None
- self.after = []
+ def __init__ (self):
+ self.before = []
+ self.override = None
+ self.after = []
class Plugin (object):
- """
- This is the main plugin object. It is used where ever a plugin is wanted, and it is the one, which needs to be subclassed by plugin authors.
-
- :CVariables:
-
- STAT_DISABLED : status
- Status: Disabled.
-
- STAT_TEMP_ENABLED : status
- Status: Enabled for this session only.
-
- STAT_ENABLED : status
- Status: Enabled.
-
- STAT_TEMP_DISABLED : status
- Status: Disabled for this session only.
-
- STAT_HARD_DISABLED : status
- Status: Forced disabled by program (i.e. because of errors in the plugin).
- """
-
- (STAT_DISABLED, STAT_TEMP_ENABLED, STAT_ENABLED, STAT_TEMP_DISABLED) = range(4)
- STAT_HARD_DISABLED = -1
-
- def __init__ (self, disable = False):
- """
- :param disable: Forcefully disable the plugin
- :type disable: bool
- """
- self.__menus = [] #: List of `Menu`
- self.__calls = [] #: List of `Call`
- self._unresolved_deps = False #: Does this plugin has unresolved dependencies?
-
- self.status = self.STAT_ENABLED #: The status of this plugin
-
- if disable:
- self.status = self.STAT_HARD_DISABLED
-
- def _init (self):
- """
- Method called from outside to init the extension parts of this plugin.
- If the current status is `STAT_HARD_DISABLED` or there are unresolved dependencies, the init process is not started.
- """
-
- for d in self.deps:
- if not system.find_packages(d, pkgSet=system.SET_INSTALLED, with_version = False):
- self._unresolved_deps = True
- break
-
- if self.status != self.STAT_HARD_DISABLED and not self._unresolved_deps:
- self.init()
-
- def init (self):
- """
- This method is called by `_init` and should be overriden by the plugin author.
-
- :precond: No unresolved deps and the status is not `STAT_HARD_DISABLED`.
- """
- pass
-
- @property
- def author (self):
- """
- Returns the plugin's author.
- The author is given by the ``__author__`` variable.
-
- :rtype: string
- """
- return getattr(self, "__author__", "")
-
- @property
- def description (self):
- """
- Returns the description of this plugin.
- It is given by either a ``__description__`` variable or by the normal class docstring.
-
- :rtype: string
- """
- if hasattr(self, "__description__"):
- return self.__description__
- else:
- doc = getattr(self, "__doc__", "")
-
- if not doc or doc == Plugin.__doc__:
- return ""
- else:
- return doc
-
- @property
- def name (self):
- """
- The name of the plugin. If no ``__name__`` variable is given, the class name is taken.
-
- :rtype: string
- """
- return getattr(self, "__name__", self.__class__.__name__)
-
- @property
- def menus (self):
- """
- Returns an iterator over the menus for this plugin.
-
- :rtype: iter<`Menu`>
- """
- return iter(self.__menus)
-
- @property
- def calls (self):
- """
- Returns an iterator over the registered calls for this plugin.
-
- :rtype: iter<`Call`>
- """
- return iter(self.__calls)
-
- @property
- def deps (self):
- """
- Returns an iterator of the dependencies or ``[]`` if there are none.
- The dependencies are given in the ``__dependency__`` variable.
-
- :rtype: [] or iter<string>
- """
- if hasattr(self, "__dependency__"):
- return iter(self.__dependency__)
- else:
- return []
-
- @property
- def enabled (self):
- """
- Returns ``True`` if the plugin is enabled.
-
- :rtype: boolean
- :see: `status`
- """
- return (self.status in (self.STAT_ENABLED, self.STAT_TEMP_ENABLED))
-
- def add_menu (self, label, callable):
- """
- Adds a new menu item for this plugin.
-
- :see: `Menu`
- """
- self.__menus.append(Menu(label, callable))
-
- def add_call (self, hook, callable, type = "before", dep = None):
- """
- Adds a new call for this plugin.
-
- :see: `Call`
- """
- self.__calls.append(Call(self, hook, callable, type, dep))
+ """
+ This is the main plugin object. It is used where ever a plugin is wanted, and it is the one, which needs to be subclassed by plugin authors.
+
+ :CVariables:
+
+ STAT_DISABLED : status
+ Status: Disabled.
+
+ STAT_TEMP_ENABLED : status
+ Status: Enabled for this session only.
+
+ STAT_ENABLED : status
+ Status: Enabled.
+
+ STAT_TEMP_DISABLED : status
+ Status: Disabled for this session only.
+
+ STAT_HARD_DISABLED : status
+ Status: Forced disabled by program (i.e. because of errors in the plugin).
+ """
+
+ (STAT_DISABLED, STAT_TEMP_ENABLED, STAT_ENABLED, STAT_TEMP_DISABLED) = range(4)
+ STAT_HARD_DISABLED = -1
+
+ def __init__ (self, disable = False):
+ """
+ :param disable: Forcefully disable the plugin
+ :type disable: bool
+ """
+ self.__menus = [] #: List of `Menu`
+ self.__calls = [] #: List of `Call`
+ self._unresolved_deps = False #: Does this plugin has unresolved dependencies?
+
+ self.status = self.STAT_ENABLED #: The status of this plugin
+
+ if disable:
+ self.status = self.STAT_HARD_DISABLED
+
+ def _init (self):
+ """
+ Method called from outside to init the extension parts of this plugin.
+ If the current status is `STAT_HARD_DISABLED` or there are unresolved dependencies, the init process is not started.
+ """
+
+ for d in self.deps:
+ if not system.find_packages(d, pkgSet=system.SET_INSTALLED, with_version = False):
+ self._unresolved_deps = True
+ break
+
+ if self.status != self.STAT_HARD_DISABLED and not self._unresolved_deps:
+ self.init()
+
+ def init (self):
+ """
+ This method is called by `_init` and should be overriden by the plugin author.
+
+ :precond: No unresolved deps and the status is not `STAT_HARD_DISABLED`.
+ """
+ pass
+
+ @property
+ def author (self):
+ """
+ Returns the plugin's author.
+ The author is given by the ``__author__`` variable.
+
+ :rtype: string
+ """
+ return getattr(self, "__author__", "")
+
+ @property
+ def description (self):
+ """
+ Returns the description of this plugin.
+ It is given by either a ``__description__`` variable or by the normal class docstring.
+
+ :rtype: string
+ """
+ if hasattr(self, "__description__"):
+ return self.__description__
+ else:
+ doc = getattr(self, "__doc__", "")
+
+ if not doc or doc == Plugin.__doc__:
+ return ""
+ else:
+ return doc
+
+ @property
+ def name (self):
+ """
+ The name of the plugin. If no ``__name__`` variable is given, the class name is taken.
+
+ :rtype: string
+ """
+ return getattr(self, "__name__", self.__class__.__name__)
+
+ @property
+ def menus (self):
+ """
+ Returns an iterator over the menus for this plugin.
+
+ :rtype: iter<`Menu`>
+ """
+ return iter(self.__menus)
+
+ @property
+ def calls (self):
+ """
+ Returns an iterator over the registered calls for this plugin.
+
+ :rtype: iter<`Call`>
+ """
+ return iter(self.__calls)
+
+ @property
+ def deps (self):
+ """
+ Returns an iterator of the dependencies or ``[]`` if there are none.
+ The dependencies are given in the ``__dependency__`` variable.
+
+ :rtype: [] or iter<string>
+ """
+ if hasattr(self, "__dependency__"):
+ return iter(self.__dependency__)
+ else:
+ return []
+
+ @property
+ def enabled (self):
+ """
+ Returns ``True`` if the plugin is enabled.
+
+ :rtype: boolean
+ :see: `status`
+ """
+ return (self.status in (self.STAT_ENABLED, self.STAT_TEMP_ENABLED))
+
+ def add_menu (self, label, callable):
+ """
+ Adds a new menu item for this plugin.
+
+ :see: `Menu`
+ """
+ self.__menus.append(Menu(label, callable))
+
+ def add_call (self, hook, callable, type = "before", dep = None):
+ """
+ Adds a new call for this plugin.
+
+ :see: `Call`
+ """
+ self.__calls.append(Call(self, hook, callable, type, dep))
class PluginQueue (object):
- """
- Class managing and loading the plugins.
-
- :IVariables:
-
- plugins : `Plugin` []
- The list of managed plugins.
-
- hooks : string -> `Hook`
- For each hook name map to a `Hook` object holding the corresponding `Call` objects.
- """
-
- def __init__ (self):
- """
- Constructor.
- """
-
- self.plugins = []
- self.hooks = defaultdict(Hook)
-
- def get_plugins (self, list_disabled = True):
- """
- Returns the plugins.
-
- :param list_disabled: Also list disabled plugins.
- :type list_disabled: boolean
-
- :rtype: iter<`Plugin`>
- """
- return (x for x in self.plugins if (x.enabled or list_disabled))
-
- def load (self):
- """
- Load the plugins.
-
- This method scans the `portato.constants.PLUGIN_DIR` for python modules and tries to load them. If the modules are real plugins,
- they have called `register` and thus the plugins are added.
- """
-
- # look them up
- plugins = []
- for f in os.listdir(PLUGIN_DIR):
- path = osp.join(PLUGIN_DIR, f)
- if osp.isdir(path):
- if osp.isfile(osp.join(path, "__init__.py")):
- plugins.append(f)
- else:
- debug("'%s' is not a plugin: __init__.py missing", path)
- else:
- if f.endswith(".py"):
- plugins.append(f[:-3])
- elif f.endswith(".pyc") or f.endswith(".pyo"):
- pass # ignore .pyc and .pyo
- else:
- debug("'%s' is not a plugin: not a .py file", path)
-
- # some magic ...
- plugin_module.__path__.insert(0, PLUGIN_DIR.rstrip("/")) # make the plugins loadable as "portato.plugins.name"
- # add Plugin and register to the builtins, so the plugins always have the correct version :)
- plugin_module.__builtins__["Plugin"] = Plugin
- plugin_module.__builtins__["register"] = register
-
- for p in plugins: # import them
- try:
- exec "from portato.plugins import %s" % p in {}
- except PluginLoadException, e:
- error(_("Loading plugin '%(plugin)s' failed: %(error)s"), {"plugin" : p, "error" : e.message})
- except:
- tb = traceback.format_exc()
- error(_("Loading plugin '%(plugin)s' failed: %(error)s"), {"plugin" : p, "error" : tb})
-
- self._organize()
-
- def add (self, plugin, disable = False):
- """
- Adds a plugin to the internal list.
-
- :Parameters:
-
- plugin : `Plugin`
- ``Plugin`` subclass or instance to add. If a class is passed, it is instantiated.
-
- disable : boolean
- Disable the plugin.
-
- :raise PluginLoadException: passed plugin is not of class `Plugin`
- """
-
- if callable(plugin) and Plugin in plugin.__bases__:
- p = plugin(disable = disable) # need an instance and not the class
- elif isinstance(plugin, Plugin):
- p = plugin
- if disable:
- p.status = p.STAT_HARD_DISABLED
- else:
- raise PluginLoadException, "Is neither a subclass nor an instance of Plugin."
-
- p._init()
-
- self.plugins.append(p)
-
- if p.status == p.STAT_HARD_DISABLED:
- msg = _("Plugin is disabled!")
- elif p._unresolved_deps:
- msg = _("Plugin has unresolved dependencies - disabled!")
- else:
- msg = ""
-
- info("%s %s", _("Plugin '%s' loaded.") % p.name, msg)
-
- def hook (self, hook, *hargs, **hkwargs):
- """
- The decorator to use in the program.
- All parameters except ``hook`` are passed to plugins.
-
- :param hook: the name of the hook
- :type hook: string
- """
-
- def hook_decorator (func):
- """
- The real decorator.
- """
- h = self.hooks[hook]
-
- active = Hook()
-
- # remove disabled
- for type in ("before", "after"):
- calls = getattr(h, type)
- aCalls = getattr(active, type)
- for call in calls:
- if call.plugin.enabled:
- aCalls.append(call)
-
- if h.override and h.override.plugin.enabled:
- active.override = h.override
-
- @wraps(func)
- def wrapper (*args, **kwargs):
- ret = None
-
- # before
- for call in active.before:
- debug("Accessing hook '%(hook)s' of plugin '%(plugin)s' (before).", {"hook" : hook, "plugin": call.plugin.name})
- call.call(*hargs, **hkwargs)
-
- if active.override: # override
- info(_("Overriding hook '%(hook)s' with plugin '%(plugin)s'."), {"hook": hook, "plugin": active.override.plugin.name})
- ret = active.override.call(*hargs, **hkwargs)
- else: # normal
- ret = func(*args, **kwargs)
-
- # after
- for call in active.after:
- debug("Accessing hook '%(hook)s' of plugin '%(plugin)s' (after).", {"hook": hook, "plugin": call.plugin.name})
- call.call(*hargs, **hkwargs)
-
- return ret
-
- return wrapper
-
- return hook_decorator
-
- def _organize (self):
- """
- Organizes the lists of `Call` in a way, that all dependencies are fullfilled.
- """
- unresolved_before = defaultdict(list)
- unresolved_after = defaultdict(list)
- star_before = defaultdict(Hook) # should be _before_ all other
- star_after = defaultdict(Hook) # should be _after_ all other
-
- for plugin in self.plugins: # plugins
- for call in plugin.calls: # hooks in plugin
- if call.type == "before":
- if call.dep is None: # no dependency -> straight add
- self.hooks[call.hook].before.append(call)
- elif call.dep == "*":
- self.hooks[call.hook].before.insert(0, call)
- elif call.dep == "-*":
- star_before[call.hook].append(call)
- else:
- named = [x.plugin.name for x in self.hooks[call.hook].before]
- if call.dep in named:
- self.hooks[call.hook].before.insert(named.index(call.dep), call)
- else:
- unresolved_before[call.hook].append(call)
-
- elif call.type == "after":
- if call.dep is None: # no dependency -> straight add
- self.hooks[call.hook].after.append(call)
- elif call.dep == "*":
- star_after[call.hook].append(call)
- elif call.dep == "-*":
- self.hooks[call.hook].after.insert(0, call)
- else:
- named = [x.plugin.name for x in self.hooks[call.hook].after]
- if call.dep in named:
- self.hooks[call.hook].after.insert(named.index(call.dep)+1, call)
- else:
- unresolved_after[call.hook].append(call)
-
- # type = "override"
- elif call.type == "override":
- if self.hooks[call.hook].override:
- warning(_("For hook '%(hook)s' an override is already defined by plugin '%(plugin)s'!"), {"hook": call.hook, "plugin": self.hooks[call.hook].override.plugin.name})
- warning(_("It is now replaced by the one from plugin '%s'!"), call.plugin.name)
-
- self.hooks[call.hook].override = call
- continue
-
- self._resolve_unresolved(unresolved_before, unresolved_after)
-
- for hook, calls in star_before.iteritems():
- self.hooks[hook].before.extend(calls) # append the list
-
- for hook, calls in star_after.iteritems():
- self.hooks[hook].after.extend(calls) # append the list
-
-
- def _resolve_unresolved (self, before, after):
- def resolve(hook, list, type, add):
- if not list:
- return
-
- callList = getattr(self.hooks[hook], type)
- named = [x.plugin.name for x in callList]
-
- while list and named:
- newNamed = [] # use newNamed, so in each iteration only the plugins inserted last are searched
- for call in list[:]:
- if call.dep in named:
- callList.insert(named.index(call.dep)+add, call)
- list.remove(call)
- newNamed.append(call.plugin.name)
-
- named = newNamed
-
- for l in list:
- callList.append(l)
- info(_("Dependant '%(dep)s' for '%(hook)s' in plugin '%(plugin)s' not found! Adding nevertheless."), {"hook": hook, "plugin": l.plugin.name, "dep": l.dep})
-
- for hook in before:
- resolve(hook, before[hook], "before", 0)
-
- for hook in after:
- resolve(hook, after[hook], "after", 1)
+ """
+ Class managing and loading the plugins.
+
+ :IVariables:
+
+ plugins : `Plugin` []
+ The list of managed plugins.
+
+ hooks : string -> `Hook`
+ For each hook name map to a `Hook` object holding the corresponding `Call` objects.
+ """
+
+ def __init__ (self):
+ """
+ Constructor.
+ """
+
+ self.plugins = []
+ self.hooks = defaultdict(Hook)
+
+ def get_plugins (self, list_disabled = True):
+ """
+ Returns the plugins.
+
+ :param list_disabled: Also list disabled plugins.
+ :type list_disabled: boolean
+
+ :rtype: iter<`Plugin`>
+ """
+ return (x for x in self.plugins if (x.enabled or list_disabled))
+
+ def load (self):
+ """
+ Load the plugins.
+
+ This method scans the `portato.constants.PLUGIN_DIR` for python modules and tries to load them. If the modules are real plugins,
+ they have called `register` and thus the plugins are added.
+ """
+
+ # look them up
+ plugins = []
+ for f in os.listdir(PLUGIN_DIR):
+ path = osp.join(PLUGIN_DIR, f)
+ if osp.isdir(path):
+ if osp.isfile(osp.join(path, "__init__.py")):
+ plugins.append(f)
+ else:
+ debug("'%s' is not a plugin: __init__.py missing", path)
+ else:
+ if f.endswith(".py"):
+ plugins.append(f[:-3])
+ elif f.endswith(".pyc") or f.endswith(".pyo"):
+ pass # ignore .pyc and .pyo
+ else:
+ debug("'%s' is not a plugin: not a .py file", path)
+
+ # some magic ...
+ plugin_module.__path__.insert(0, PLUGIN_DIR.rstrip("/")) # make the plugins loadable as "portato.plugins.name"
+ # add Plugin and register to the builtins, so the plugins always have the correct version :)
+ plugin_module.__builtins__["Plugin"] = Plugin
+ plugin_module.__builtins__["register"] = register
+
+ for p in plugins: # import them
+ try:
+ exec "from portato.plugins import %s" % p in {}
+ except PluginLoadException, e:
+ error(_("Loading plugin '%(plugin)s' failed: %(error)s"), {"plugin" : p, "error" : e.message})
+ except:
+ tb = traceback.format_exc()
+ error(_("Loading plugin '%(plugin)s' failed: %(error)s"), {"plugin" : p, "error" : tb})
+
+ self._organize()
+
+ def add (self, plugin, disable = False):
+ """
+ Adds a plugin to the internal list.
+
+ :Parameters:
+
+ plugin : `Plugin`
+ ``Plugin`` subclass or instance to add. If a class is passed, it is instantiated.
+
+ disable : boolean
+ Disable the plugin.
+
+ :raise PluginLoadException: passed plugin is not of class `Plugin`
+ """
+
+ if callable(plugin) and Plugin in plugin.__bases__:
+ p = plugin(disable = disable) # need an instance and not the class
+ elif isinstance(plugin, Plugin):
+ p = plugin
+ if disable:
+ p.status = p.STAT_HARD_DISABLED
+ else:
+ raise PluginLoadException, "Is neither a subclass nor an instance of Plugin."
+
+ p._init()
+
+ self.plugins.append(p)
+
+ if p.status == p.STAT_HARD_DISABLED:
+ msg = _("Plugin is disabled!")
+ elif p._unresolved_deps:
+ msg = _("Plugin has unresolved dependencies - disabled!")
+ else:
+ msg = ""
+
+ info("%s %s", _("Plugin '%s' loaded.") % p.name, msg)
+
+ def hook (self, hook, *hargs, **hkwargs):
+ """
+ The decorator to use in the program.
+ All parameters except ``hook`` are passed to plugins.
+
+ :param hook: the name of the hook
+ :type hook: string
+ """
+
+ def hook_decorator (func):
+ """
+ The real decorator.
+ """
+ h = self.hooks[hook]
+
+ active = Hook()
+
+ # remove disabled
+ for type in ("before", "after"):
+ calls = getattr(h, type)
+ aCalls = getattr(active, type)
+ for call in calls:
+ if call.plugin.enabled:
+ aCalls.append(call)
+
+ if h.override and h.override.plugin.enabled:
+ active.override = h.override
+
+ @wraps(func)
+ def wrapper (*args, **kwargs):
+ ret = None
+
+ # before
+ for call in active.before:
+ debug("Accessing hook '%(hook)s' of plugin '%(plugin)s' (before).", {"hook" : hook, "plugin": call.plugin.name})
+ call.call(*hargs, **hkwargs)
+
+ if active.override: # override
+ info(_("Overriding hook '%(hook)s' with plugin '%(plugin)s'."), {"hook": hook, "plugin": active.override.plugin.name})
+ ret = active.override.call(*hargs, **hkwargs)
+ else: # normal
+ ret = func(*args, **kwargs)
+
+ # after
+ for call in active.after:
+ debug("Accessing hook '%(hook)s' of plugin '%(plugin)s' (after).", {"hook": hook, "plugin": call.plugin.name})
+ call.call(*hargs, **hkwargs)
+
+ return ret
+
+ return wrapper
+
+ return hook_decorator
+
+ def _organize (self):
+ """
+ Organizes the lists of `Call` in a way, that all dependencies are fullfilled.
+ """
+ unresolved_before = defaultdict(list)
+ unresolved_after = defaultdict(list)
+ star_before = defaultdict(Hook) # should be _before_ all other
+ star_after = defaultdict(Hook) # should be _after_ all other
+
+ for plugin in self.plugins: # plugins
+ for call in plugin.calls: # hooks in plugin
+ if call.type == "before":
+ if call.dep is None: # no dependency -> straight add
+ self.hooks[call.hook].before.append(call)
+ elif call.dep == "*":
+ self.hooks[call.hook].before.insert(0, call)
+ elif call.dep == "-*":
+ star_before[call.hook].append(call)
+ else:
+ named = [x.plugin.name for x in self.hooks[call.hook].before]
+ if call.dep in named:
+ self.hooks[call.hook].before.insert(named.index(call.dep), call)
+ else:
+ unresolved_before[call.hook].append(call)
+
+ elif call.type == "after":
+ if call.dep is None: # no dependency -> straight add
+ self.hooks[call.hook].after.append(call)
+ elif call.dep == "*":
+ star_after[call.hook].append(call)
+ elif call.dep == "-*":
+ self.hooks[call.hook].after.insert(0, call)
+ else:
+ named = [x.plugin.name for x in self.hooks[call.hook].after]
+ if call.dep in named:
+ self.hooks[call.hook].after.insert(named.index(call.dep)+1, call)
+ else:
+ unresolved_after[call.hook].append(call)
+
+ # type = "override"
+ elif call.type == "override":
+ if self.hooks[call.hook].override:
+ warning(_("For hook '%(hook)s' an override is already defined by plugin '%(plugin)s'!"), {"hook": call.hook, "plugin": self.hooks[call.hook].override.plugin.name})
+ warning(_("It is now replaced by the one from plugin '%s'!"), call.plugin.name)
+
+ self.hooks[call.hook].override = call
+ continue
+
+ self._resolve_unresolved(unresolved_before, unresolved_after)
+
+ for hook, calls in star_before.iteritems():
+ self.hooks[hook].before.extend(calls) # append the list
+
+ for hook, calls in star_after.iteritems():
+ self.hooks[hook].after.extend(calls) # append the list
+
+
+ def _resolve_unresolved (self, before, after):
+ def resolve(hook, list, type, add):
+ if not list:
+ return
+
+ callList = getattr(self.hooks[hook], type)
+ named = [x.plugin.name for x in callList]
+
+ while list and named:
+ newNamed = [] # use newNamed, so in each iteration only the plugins inserted last are searched
+ for call in list[:]:
+ if call.dep in named:
+ callList.insert(named.index(call.dep)+add, call)
+ list.remove(call)
+ newNamed.append(call.plugin.name)
+
+ named = newNamed
+
+ for l in list:
+ callList.append(l)
+ info(_("Dependant '%(dep)s' for '%(hook)s' in plugin '%(plugin)s' not found! Adding nevertheless."), {"hook": hook, "plugin": l.plugin.name, "dep": l.dep})
+
+ for hook in before:
+ resolve(hook, before[hook], "before", 0)
+
+ for hook in after:
+ resolve(hook, after[hook], "after", 1)
__plugins = None
def load_plugins():
- """
- Loads the plugins.
- """
-
- global __plugins
- if __plugins is None:
- __plugins = PluginQueue()
- __plugins.load()
-
+ """
+ Loads the plugins.
+ """
+
+ global __plugins
+ if __plugins is None:
+ __plugins = PluginQueue()
+ __plugins.load()
+
def get_plugin_queue():
- """
- Returns the actual `PluginQueue`. If it is ``None``, they are not being loaded yet.
+ """
+ Returns the actual `PluginQueue`. If it is ``None``, they are not being loaded yet.
- :rtype: `PluginQueue` or ``None``"""
- return __plugins
+ :rtype: `PluginQueue` or ``None``"""
+ return __plugins
def hook(hook, *args, **kwargs):
- """
- Shortcut to `PluginQueue.hook`. If no `PluginQueue` is loaded, this does nothing.
- """
- if __plugins is None:
- def pseudo_decorator(f):
- return f
- return pseudo_decorator
- else:
- return __plugins.hook(hook, *args, **kwargs)
+ """
+ Shortcut to `PluginQueue.hook`. If no `PluginQueue` is loaded, this does nothing.
+ """
+ if __plugins is None:
+ def pseudo_decorator(f):
+ return f
+ return pseudo_decorator
+ else:
+ return __plugins.hook(hook, *args, **kwargs)
def register (plugin, disable = False):
- """
- Registers a plugin.
+ """
+ Registers a plugin.
- :see: `PluginQueue.add`
- """
- if __plugins is not None:
- __plugins.add(plugin, disable)
+ :see: `PluginQueue.add`
+ """
+ if __plugins is not None:
+ __plugins.add(plugin, disable)
diff --git a/portato/session.py b/portato/session.py
index 5d1a640..9f08ff7 100644
--- a/portato/session.py
+++ b/portato/session.py
@@ -19,104 +19,104 @@ from .constants import SESSION_DIR
from .helper import debug, info
class Session (object):
- """
- A small class allowing to save certain states of a program.
- This class works in a quite abstract manner, as it works with handlers, which
- define what options to use out of the config file and how to apply them to the program.
-
- Note: This class currently does not work with boolean config options. If you
- want to define boolean values, use 0 and 1. This is future proof.
- """
-
- # the current session format version
- VERSION = 1
-
- def __init__ (self, file):
- """
- 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.
- """
-
- self._cfg = None
- self._handlers = []
-
- if not (os.path.exists(SESSION_DIR) and os.path.isdir(SESSION_DIR)):
- os.mkdir(SESSION_DIR)
- self._cfg = ConfigParser(os.path.join(SESSION_DIR, file))
- info(_("Loading session from '%s'.") % self._cfg.file)
- try:
- self._cfg.parse()
- except IOError, e:
- if e.errno == 2: pass
- else: raise
-
- # add version check
- self.add_handler(([("version", "session")], self.check_version, lambda: self.VERSION))
-
- def add_handler (self, (options, load_fn, save_fn), default = None):
- """
- Adds a handler to this session. A handler is a three-tuple consisting of:
- - a list of (key,section) values
- - a function getting number of option arguments and applying them to the program
- - a function returning the number of option return values - getting them out of the program
- """
- self._handlers.append((options, load_fn, save_fn, default))
-
- def load (self):
- """
- Loads and applies all values of the session.
- """
- for options, lfn, sfn, default in self._handlers:
- try:
- loaded = [self._cfg.get(*x) for x in options]
- except KeyError: # does not exist -> ignore
- debug("No values for %s.", options)
- else:
- debug("Loading %s with values %s.", options, loaded)
- lfn(*loaded)
- continue
-
- if default:
- debug("Loading %s with defaults %s.", options, default)
- lfn(*default)
-
- def save (self):
- """
- Saves all options into the file.
- """
-
- for options, lfn, sfn, default in self._handlers:
- vals = sfn()
-
- # map into list if necessairy
- if not hasattr(vals, "__iter__"):
- vals = [vals]
- debug("Saving %s with values %s", options, vals)
-
- for value, (option, section) in zip(vals, options):
- self.set(option, str(value), section)
-
- self._cfg.write()
-
- def set (self, key, value, section):
- try:
- self._cfg.add(key, value, section, with_blankline = False)
- except SectionNotFoundException:
- self._cfg.add_section(section)
- self._cfg.add(key, value, section, with_blankline = False)
-
- def get (self, key, section):
- try:
- return self._cfg.get(key, section)
- except KeyError:
- return None
-
- def get_boolean (self, key, section):
- try:
- return self._cfg.get_boolean(key, section)
- except KeyError:
- return None
-
- def check_version (self, vers):
- pass # do nothing atm
+ """
+ A small class allowing to save certain states of a program.
+ This class works in a quite abstract manner, as it works with handlers, which
+ define what options to use out of the config file and how to apply them to the program.
+
+ Note: This class currently does not work with boolean config options. If you
+ want to define boolean values, use 0 and 1. This is future proof.
+ """
+
+ # the current session format version
+ VERSION = 1
+
+ def __init__ (self, file):
+ """
+ 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.
+ """
+
+ self._cfg = None
+ self._handlers = []
+
+ if not (os.path.exists(SESSION_DIR) and os.path.isdir(SESSION_DIR)):
+ os.mkdir(SESSION_DIR)
+ self._cfg = ConfigParser(os.path.join(SESSION_DIR, file))
+ info(_("Loading session from '%s'.") % self._cfg.file)
+ try:
+ self._cfg.parse()
+ except IOError, e:
+ if e.errno == 2: pass
+ else: raise
+
+ # add version check
+ self.add_handler(([("version", "session")], self.check_version, lambda: self.VERSION))
+
+ def add_handler (self, (options, load_fn, save_fn), default = None):
+ """
+ Adds a handler to this session. A handler is a three-tuple consisting of:
+ - a list of (key,section) values
+ - a function getting number of option arguments and applying them to the program
+ - a function returning the number of option return values - getting them out of the program
+ """
+ self._handlers.append((options, load_fn, save_fn, default))
+
+ def load (self):
+ """
+ Loads and applies all values of the session.
+ """
+ for options, lfn, sfn, default in self._handlers:
+ try:
+ loaded = [self._cfg.get(*x) for x in options]
+ except KeyError: # does not exist -> ignore
+ debug("No values for %s.", options)
+ else:
+ debug("Loading %s with values %s.", options, loaded)
+ lfn(*loaded)
+ continue
+
+ if default:
+ debug("Loading %s with defaults %s.", options, default)
+ lfn(*default)
+
+ def save (self):
+ """
+ Saves all options into the file.
+ """
+
+ for options, lfn, sfn, default in self._handlers:
+ vals = sfn()
+
+ # map into list if necessairy
+ if not hasattr(vals, "__iter__"):
+ vals = [vals]
+ debug("Saving %s with values %s", options, vals)
+
+ for value, (option, section) in zip(vals, options):
+ self.set(option, str(value), section)
+
+ self._cfg.write()
+
+ def set (self, key, value, section):
+ try:
+ self._cfg.add(key, value, section, with_blankline = False)
+ except SectionNotFoundException:
+ self._cfg.add_section(section)
+ self._cfg.add(key, value, section, with_blankline = False)
+
+ def get (self, key, section):
+ try:
+ return self._cfg.get(key, section)
+ except KeyError:
+ return None
+
+ def get_boolean (self, key, section):
+ try:
+ return self._cfg.get_boolean(key, section)
+ except KeyError:
+ return None
+
+ def check_version (self, vers):
+ pass # do nothing atm
diff --git a/portato/waiting_queue.py b/portato/waiting_queue.py
index 32839d3..bc2c50c 100644
--- a/portato/waiting_queue.py
+++ b/portato/waiting_queue.py
@@ -17,47 +17,47 @@ from Queue import Queue
class WaitingQueue (Queue):
- def __init__ (self, setTrue = True, threadClass = Thread):
- if not issubclass(threadClass, Thread):
- raise ValueError, "Only subclasses of threading.Thread are allowed."
-
- Queue.__init__(self)
- self.event = Event()
- self.counter = 0
- self.threadClass = threadClass
-
- if setTrue:
- self.event.set() # true at the beginning
-
- waitingThread = self.threadClass(name = "Waiting-Queue-Thread", target = self.runThread)
- waitingThread.setDaemon(True)
- waitingThread.start()
-
- def put (self, method, *args, **kwargs):
- self.counter += 1;
-
- if "caller" in kwargs:
- name = "Waiting Thread #%d (called by:%s)" % (self.counter, kwargs["caller"])
- del kwargs["caller"]
- else:
- name = "Waiting Thread #%d" % self.counter
-
- t = self.threadClass(name = name, target = method, args = args, kwargs = kwargs)
- t.setDaemon(True)
- Queue.put(self, t, False)
-
- def runThread (self):
- while True:
- self.event.wait()
- t = self.get(True)
- self.event.clear()
- t.run()
-
- def next (self):
- self.event.set()
-
- def clear (self):
- self.mutex.acquire()
- self.queue.clear()
- self.mutex.release()
- self.event.set()
+ def __init__ (self, setTrue = True, threadClass = Thread):
+ if not issubclass(threadClass, Thread):
+ raise ValueError, "Only subclasses of threading.Thread are allowed."
+
+ Queue.__init__(self)
+ self.event = Event()
+ self.counter = 0
+ self.threadClass = threadClass
+
+ if setTrue:
+ self.event.set() # true at the beginning
+
+ waitingThread = self.threadClass(name = "Waiting-Queue-Thread", target = self.runThread)
+ waitingThread.setDaemon(True)
+ waitingThread.start()
+
+ def put (self, method, *args, **kwargs):
+ self.counter += 1;
+
+ if "caller" in kwargs:
+ name = "Waiting Thread #%d (called by:%s)" % (self.counter, kwargs["caller"])
+ del kwargs["caller"]
+ else:
+ name = "Waiting Thread #%d" % self.counter
+
+ t = self.threadClass(name = name, target = method, args = args, kwargs = kwargs)
+ t.setDaemon(True)
+ Queue.put(self, t, False)
+
+ def runThread (self):
+ while True:
+ self.event.wait()
+ t = self.get(True)
+ self.event.clear()
+ t.run()
+
+ def next (self):
+ self.event.set()
+
+ def clear (self):
+ self.mutex.acquire()
+ self.queue.clear()
+ self.mutex.release()
+ self.event.set()
diff --git a/setup.py b/setup.py
index 57d5961..6de58b0 100644
--- a/setup.py
+++ b/setup.py
@@ -16,23 +16,23 @@ from distutils.core import setup, Extension
from portato.constants import VERSION, DATA_DIR, ICON_DIR, PLUGIN_DIR, TEMPLATE_DIR
def plugin_list (*args):
- """Creates a list of correct plugin pathes out of the arguments."""
- return [("plugins/%s.py" % x) for x in args]
+ """Creates a list of correct plugin pathes out of the arguments."""
+ return [("plugins/%s.py" % x) for x in args]
packages = ["portato", "portato.gui", "portato.gui.windows", "portato.plugins", "portato.backend", "portato.backend.portage"]
data_files = [
- (TEMPLATE_DIR, [os.path.join("portato/gui/templates",x) for x in os.listdir("portato/gui/templates") if x.endswith(".glade")]),
- (ICON_DIR, ["icons/portato-icon.png"]),
- (PLUGIN_DIR, plugin_list("gpytage", "notify", "etc_proposals", "reload_portage"))]
+ (TEMPLATE_DIR, [os.path.join("portato/gui/templates",x) for x in os.listdir("portato/gui/templates") if x.endswith(".glade")]),
+ (ICON_DIR, ["icons/portato-icon.png"]),
+ (PLUGIN_DIR, plugin_list("gpytage", "notify", "etc_proposals", "reload_portage"))]
# do the distutils setup
setup(name="Portato",
- version = VERSION,
- description = "GTK-Frontend to Portage",
- license = "GPLv2",
- url = "http://portato.origo.ethz.ch/",
- author = "René 'Necoro' Neumann",
- author_email = "necoro@necoro.net",
- packages = packages,
- data_files = data_files
- )
+ version = VERSION,
+ description = "GTK-Frontend to Portage",
+ license = "GPLv2",
+ url = "http://portato.origo.ethz.ch/",
+ author = "René 'Necoro' Neumann",
+ author_email = "necoro@necoro.net",
+ packages = packages,
+ data_files = data_files
+ )