This is the mail archive of the archer@sourceware.org mailing list for the Archer project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

frame filters


One of the last tasks for killing archer-tromey-python is dealing with
the frame filtering and new-backtrace code.  Phil expressed an interest
in working on this, so I'm sending my current patch and some notes about
how I think we ought to proceed.

The old code had a few problems:

* The command was called "new-backtrace", but it would be nicer if it
  were just the default.
* It didn't integrate with MI.
* Problems pointed out by Alexander Larsson on this list.
* No way to enable or disable filters -- this is a mistake we made with
  pretty-printing that we should not repeat.

This patch takes a stab at the last couple of problems.

First, it separates frames from frame-printing.  I think this should
help with Alexander's issues.

It defines an API for "frame wrappers".  A frame filter can return any
object satisfying this API.  A frame wrapper is a lot like a gdb.Frame,
but a few methods are replaced with simpler-to-use methods, and it adds
an optional 'children' method.  The idea here is that if a filter
collapses some frames, it should keep the originals around as children.
That way we can print a "full" stack trace like:

   frame
   frame
   collapsed frame
      original frame
      original frame
   frame
   ...

Maybe we should just add methods to gdb.Frame to avoid the middle-man.
I am not certain about this.


A frame filter is now a subclass of Parameter, so it always has a name
and a user-visible setting.  The parameter's value is an integer; users
can use these to order or disable frame filters.  (The "lame" stuff in
the diff is just to work around an order-of-initialization problem that
I think is fixed on trunk, I just didn't do a merge when I was hacking
on this.)


I think the way forward is to move some of the new stuff into C and make
it work with both MI and the CLI:

* Extend "backtrace" to accept some new qualifiers, not just "full".
  new-backtrace has a couple here: "reverse" (though this should just be
  reimplemented as a frame filter if that is possible -- then we don't
  need this one), "raw" (akin to print/r), and "all" (show the child
  frames as well).

* Change the CLI backtrace command to use frame filters when enabled and
  available.  Add new printing functions to print a Python object using
  the various frame filter methods.

* Add qualifier-like arguments to MI's -stack-list-frames et al.  I
  don't think "reverse" is that important here, but at least "raw" is.

* Make other MI commands filter-aware as needed, e.g.,
  -stack-info-depth.

* Change MI frame-printing to use filters as appropriate.  This may
  necessitate changes to the frame filter API, I am not sure.  MI should
  probably always print the child frames.

* In order to test this, write a few real-world frame filters.  At
  least, adapt the Gtk and Python ones.

* Then I think we should look at making the frame filter abstraction
  more than just a display thing.  That means adding filtering support
  to "up", "down", and "frame".  These would call the `select' method on
  the appropriate filtered frame, which would have to eventually
  delegate to some real frame.  These would also need "raw" arguments of
  some form (e.g, "up/r").

Tom

diff --git a/gdb/data-directory/Makefile.in b/gdb/data-directory/Makefile.in
index c9ff0de..aa09b97 100644
--- a/gdb/data-directory/Makefile.in
+++ b/gdb/data-directory/Makefile.in
@@ -51,22 +51,20 @@ SYSCALLS_FILES = \
 PYTHON_DIR = python
 PYTHON_INSTALL_DIR = $(DESTDIR)$(GDB_DATADIR)/$(PYTHON_DIR)
 PYTHON_FILES = \
-	gdb/FrameIterator.py \
-	gdb/FrameWrapper.py \
-	gdb/__init__.py \
-	gdb/backtrace.py \
-	gdb/command/__init__.py \
 	gdb/command/alias.py \
 	gdb/command/backtrace.py \
 	gdb/command/ignore_errors.py \
+	gdb/command/__init__.py	\
 	gdb/command/pahole.py \
 	gdb/command/pretty_printers.py \
-	gdb/command/require.py \
 	gdb/command/upto.py \
-	gdb/function/__init__.py \
+	gdb/frame.py \
 	gdb/function/caller_is.py \
+	gdb/function/__init__.py \
 	gdb/function/in_scope.py \
-	gdb/printing.py \
+	gdb/__init__.py	\
+	gdb/libgcj/v10/printers.py \
+	gdb/printing.py	\
 	gdb/types.py
 
 FLAGS_TO_PASS = \
diff --git a/gdb/python/lib/gdb/FrameIterator.py b/gdb/python/lib/gdb/FrameIterator.py
deleted file mode 100644
index 5654546..0000000
--- a/gdb/python/lib/gdb/FrameIterator.py
+++ /dev/null
@@ -1,33 +0,0 @@
-# Iterator over frames.
-
-# Copyright (C) 2008, 2009 Free Software Foundation, Inc.
-
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program.  If not, see <http://www.gnu.org/licenses/>.
-
-class FrameIterator:
-    """An iterator that iterates over frames."""
-
-    def __init__ (self, frame):
-        "Initialize a FrameIterator.  FRAME is the starting frame."
-        self.frame = frame
-
-    def __iter__ (self):
-        return self
-
-    def next (self):
-        result = self.frame
-        if result is None:
-            raise StopIteration
-        self.frame = result.older ()
-        return result
diff --git a/gdb/python/lib/gdb/FrameWrapper.py b/gdb/python/lib/gdb/FrameWrapper.py
deleted file mode 100644
index b790a54..0000000
--- a/gdb/python/lib/gdb/FrameWrapper.py
+++ /dev/null
@@ -1,112 +0,0 @@
-# Wrapper API for frames.
-
-# Copyright (C) 2008, 2009 Free Software Foundation, Inc.
-
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program.  If not, see <http://www.gnu.org/licenses/>.
-
-import gdb
-
-# FIXME: arguably all this should be on Frame somehow.
-class FrameWrapper:
-    def __init__ (self, frame):
-        self.frame = frame;
-
-    def write_symbol (self, stream, sym, block):
-        if len (sym.linkage_name):
-            nsym, is_field_of_this = gdb.lookup_symbol (sym.linkage_name, block)
-            if nsym.addr_class != gdb.SYMBOL_LOC_REGISTER:
-                sym = nsym
-
-        stream.write (sym.print_name + "=")
-        try:
-            val = self.read_var (sym)
-            if val != None:
-                val = str (val)
-        # FIXME: would be nice to have a more precise exception here.
-        except RuntimeError, text:
-            val = text
-        if val == None:
-            stream.write ("???")
-        else:
-            stream.write (str (val))
-
-    def print_frame_locals (self, stream, func):
-        if not func:
-            return
-
-        first = True
-        block = func.value
-
-        for sym in block:
-            if sym.is_argument:
-                continue;
-
-            self.write_symbol (stream, sym, block)
-            stream.write ('\n')
-
-    def print_frame_args (self, stream, func):
-        if not func:
-            return
-
-        first = True
-        block = func.value
-
-        for sym in block:
-            if not sym.is_argument:
-                continue;
-
-            if not first:
-                stream.write (", ")
-
-            self.write_symbol (stream, sym, block)
-            first = False
-
-    # FIXME: this should probably just be a method on gdb.Frame.
-    # But then we need stream wrappers.
-    def describe (self, stream, full):
-        if self.type () == gdb.DUMMY_FRAME:
-            stream.write (" <function called from gdb>\n")
-        elif self.type () == gdb.SIGTRAMP_FRAME:
-            stream.write (" <signal handler called>\n")
-        else:
-            sal = self.find_sal ()
-            pc = self.pc ()
-            name = self.name ()
-            if not name:
-                name = "??"
-            if pc != sal.pc or not sal.symtab:
-                stream.write (" 0x%08x in" % pc)
-            stream.write (" " + name + " (")
-
-            func = self.function ()
-            self.print_frame_args (stream, func)
-
-            stream.write (")")
-
-            if sal.symtab and sal.symtab.filename:
-                stream.write (" at " + sal.symtab.filename)
-                stream.write (":" + str (sal.line))
-
-            if not self.name () or (not sal.symtab or not sal.symtab.filename):
-                lib = gdb.solib_address (pc)
-                if lib:
-                    stream.write (" from " + lib)
-
-            stream.write ("\n")
-
-            if full:
-                self.print_frame_locals (stream, func)
-
-    def __getattr__ (self, name):
-        return getattr (self.frame, name)
diff --git a/gdb/python/lib/gdb/backtrace.py b/gdb/python/lib/gdb/backtrace.py
deleted file mode 100644
index 6bb4fb1..0000000
--- a/gdb/python/lib/gdb/backtrace.py
+++ /dev/null
@@ -1,42 +0,0 @@
-# Filtering backtrace.
-
-# Copyright (C) 2008, 2011 Free Software Foundation, Inc.
-
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program.  If not, see <http://www.gnu.org/licenses/>.
-
-import gdb
-import itertools
-
-# Our only exports.
-__all__ = ['push_frame_filter', 'create_frame_filter']
-
-frame_filter = None
-
-def push_frame_filter (constructor):
-    """Register a new backtrace filter class with the 'backtrace' command.
-The filter will be passed an iterator as an argument.  The iterator
-will return gdb.Frame-like objects.  The filter should in turn act as
-an iterator returning such objects."""
-    global frame_filter
-    if frame_filter == None:
-        frame_filter = constructor
-    else:
-        frame_filter = lambda iterator, filter = frame_filter: constructor (filter (iterator))
-
-def create_frame_filter (iter):
-    global frame_filter
-    if frame_filter is None:
-        return iter
-    return frame_filter (iter)
-
diff --git a/gdb/python/lib/gdb/command/backtrace.py b/gdb/python/lib/gdb/command/backtrace.py
index 2aa5b90..f9b995e 100644
--- a/gdb/python/lib/gdb/command/backtrace.py
+++ b/gdb/python/lib/gdb/command/backtrace.py
@@ -1,6 +1,6 @@
 # New backtrace command.
 
-# Copyright (C) 2008, 2009, 2011 Free Software Foundation, Inc.
+# Copyright (C) 2008, 2009, 2010, 2011 Free Software Foundation, Inc.
 
 # This program is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
@@ -16,13 +16,11 @@
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 import gdb
-import gdb.backtrace
+import gdb.frame
 import itertools
-from gdb.FrameIterator import FrameIterator
-from gdb.FrameWrapper import FrameWrapper
 import sys
 
-class ReverseBacktraceParameter (gdb.Parameter):
+class ReverseBacktraceParameter(gdb.Parameter):
     """The new-backtrace command can show backtraces in 'reverse' order.
 This means that the innermost frame will be printed last.
 Note that reverse backtraces are more expensive to compute."""
@@ -31,76 +29,119 @@ Note that reverse backtraces are more expensive to compute."""
     show_doc = "Show whether backtraces will be printed in reverse order."
 
     def __init__(self):
-        gdb.Parameter.__init__ (self, "reverse-backtrace",
-                                gdb.COMMAND_STACK, gdb.PARAM_BOOLEAN)
+        super(ReverseBacktraceParameter, self).__init__("reverse-backtrace",
+                                                        gdb.COMMAND_STACK,
+                                                        gdb.PARAM_BOOLEAN)
         # Default to compatibility with gdb.
         self.value = False
 
-class FilteringBacktrace (gdb.Command):
+class FilteringBacktrace(gdb.Command):
     """Print backtrace of all stack frames, or innermost COUNT frames.
+
+Usage: new-backtrace [COUNT|QUALIFIER]...
+
 With a negative argument, print outermost -COUNT frames.
-Use of the 'full' qualifier also prints the values of the local variables.
-Use of the 'raw' qualifier avoids any filtering by loadable modules.
+
+Valid qualifiers are:
+
+   full        Also print the values of the local variables.
+   raw         Avoid any filtering by loadable modules.
+   reverse     Reverse the stack trace.  If a reverse trace was
+               already selected by `set reverse-backtrace', then an
+               ordinary stack trace is done.  Note that reverse
+               backtraces are more expensive to compute.
+   all         Show frames that have been filtered out.
 """
 
-    def __init__ (self):
+    def __init__(self):
         # FIXME: this is not working quite well enough to replace
         # "backtrace" yet.
-        gdb.Command.__init__ (self, "new-backtrace", gdb.COMMAND_STACK)
+        super(FilteringBacktrace, self).__init__("new-backtrace",
+                                                 gdb.COMMAND_STACK)
         self.reverse = ReverseBacktraceParameter()
 
-    def reverse_iter (self, iter):
-        result = []
-        for item in iter:
-            result.append (item)
+    def reverse_iter(self, iter):
+        result = list(iter)
         result.reverse()
         return result
 
-    def final_n (self, iter, x):
-        result = []
-        for item in iter:
-            result.append (item)
+    def final_n(self, iter, x):
+        result = list(iter)
         return result[x:]
 
-    def invoke (self, arg, from_tty):
+    def invoke(self, arg, from_tty):
         i = 0
         count = 0
         filter = True
         full = False
+        reverse = self.reverse.value
+        showall = False
 
-        for word in arg.split (" "):
+        for word in arg.split(" "):
             if word == '':
                 continue
             elif word == 'raw':
                 filter = False
             elif word == 'full':
                 full = True
+            elif word == 'reverse':
+                reverse = not reverse
+            elif word == 'all':
+                showall = True
             else:
-                count = int (word)
+                count = int(word)
 
         # FIXME: provide option to start at selected frame
         # However, should still number as if starting from newest
-        newest_frame = gdb.selected_thread ().newest_frame ()
-        iter = itertools.imap (FrameWrapper,
-                               FrameIterator (newest_frame))
+        # FIXME: try/catch and wrap in gdb error
+        newest_frame = gdb.newest_frame()
+        iter = itertools.imap(gdb.frame.FrameWrapper,
+                              gdb.frame.FrameIterator(newest_frame))
         if filter:
-            iter = gdb.backtrace.create_frame_filter (iter)
+            iter = gdb.frame.create_frame_filter(iter)
 
         # Now wrap in an iterator that numbers the frames.
-        iter = itertools.izip (itertools.count (0), iter)
+        iter = itertools.izip(itertools.count(0), iter)
 
         # Reverse if the user wanted that.
-        if self.reverse.value:
-            iter = self.reverse_iter (iter)
+        if reverse:
+            iter = self.reverse_iter(iter)
 
         # Extract sub-range user wants.
         if count < 0:
-            iter = self.final_n (iter, count)
+            iter = self.final_n(iter, count)
         elif count > 0:
-            iter = itertools.islice (iter, 0, count)
+            iter = itertools.islice(iter, 0, count)
 
         for pair in iter:
-            sys.stdout.write ("#%-2d" % pair[0])
-            pair[1].describe (sys.stdout, full)
+            sys.stdout.write("#%-2d" % pair[0])
+            gdb.frame.print_frame(pair[1], sys.stdout, full)
+            if showall:
+                for f in pair[1].children():
+                    gdb.frame.print_frame(f, sys.stdout, full, '    ')
 
 FilteringBacktrace()
+
+def lame():
+    class _Holder(gdb.Command):
+        def __init__(self, what):
+            super(_Holder, self).__init__(what + " backtrace filter",
+                                          gdb.COMMAND_STACK,
+                                          prefix = True)
+
+        def invoke(self, arg, from_tty):
+            # FIXME
+            pass
+
+    _Holder("set")
+    _Holder("show")
+
+    class ShowFilter(gdb.Command):
+        def __init__(self):
+            super(ShowFilter, self).__init__("show backtrace filters",
+                                             gdb.COMMAND_STACK)
+
+        def invoke(self, arg, from_tty):
+            gdb.frame.print_filters()
+
+    ShowFilter()
diff --git a/gdb/python/lib/gdb/command/upto.py b/gdb/python/lib/gdb/command/upto.py
index faf54ed..5126531 100644
--- a/gdb/python/lib/gdb/command/upto.py
+++ b/gdb/python/lib/gdb/command/upto.py
@@ -1,6 +1,6 @@
 # upto command.
 
-# Copyright (C) 2009 Free Software Foundation, Inc.
+# Copyright (C) 2009, 2010 Free Software Foundation, Inc.
 
 # This program is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
@@ -16,9 +16,8 @@
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 import gdb
+import gdb.frame
 import re
-from gdb.FrameIterator import FrameIterator
-from gdb.FrameWrapper import FrameWrapper
 
 class UptoPrefix (gdb.Command):
     def __init__ (self):
@@ -32,15 +31,15 @@ class UptoImplementation (gdb.Command):
 
     def search (self):
         saved = gdb.selected_frame ()
-        iter = FrameIterator (saved)
+        iter = gdb.frame.FrameIterator (saved)
         found = False
         try:
             for frame in iter:
                 frame.select ()
                 try:
                     if self.filter (frame):
-                        wrapper = FrameWrapper (frame)
-                        wrapper.describe (sys.stdout, False)
+                        wrapper = gdb.frame.FrameWrapper (frame)
+                        gdb.frame.print_frame(wrapper, sys.stdout)
                         return
                 except:
                     pass
diff --git a/gdb/python/lib/gdb/frame.py b/gdb/python/lib/gdb/frame.py
new file mode 100644
index 0000000..4743cd9
--- /dev/null
+++ b/gdb/python/lib/gdb/frame.py
@@ -0,0 +1,238 @@
+# Frame-related utilities.
+
+# Copyright (C) 2008, 2009, 2010, 2011 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+import gdb
+import cStringIO
+
+class FrameIterator(object):
+    """An iterator that iterates over frames."""
+
+    def __init__ (self, frame):
+        "Initialize a FrameIterator.  FRAME is the starting frame."
+        super(FrameIterator, self).__init__()
+        self.frame = frame
+
+    def __iter__ (self):
+        return self
+
+    def next (self):
+        result = self.frame
+        if result is None:
+            raise StopIteration
+        self.frame = result.older ()
+        return result
+
+class _SymWrapper(object):
+    def __init__(self, frame, block, sym):
+        super(_SymWrapper, self).__init__()
+        self.frame = frame
+        self.block = block
+        if len(sym.linkage_name):
+            nsym, is_field_of_this = gdb.lookup_symbol (sym.linkage_name, block)
+            if nsym.addr_class != gdb.SYMBOL_LOC_REGISTER:
+                sym = nsym
+        self.sym = sym
+
+    def name(self):
+        return self.sym.print_name
+
+    def value(self):
+        try:
+            return self.frame.read_var(self.sym)
+        except RuntimeError, text:
+            return text
+
+class _BlockIterator(object):
+    def __init__(self, frame, block, just_args):
+        super(_BlockIterator, self).__init__()
+        self.frame = frame
+        self.block = block
+        self.just_args = just_args
+        self.iter = iter(self.block)
+
+    def __iter__(self):
+        return self
+
+    def next(self):
+        while True:
+            try:
+                result = self.iter.next()
+                if result.is_argument == self.just_args:
+                    return _SymWrapper(self.frame, self.block, result)
+            except StopIteration:
+                if self.block.function is not None:
+                    raise StopIteration
+                self.block = self.block.superblock
+                self.iter = iter(self.block)
+
+class FrameWrapper(object):
+    """A wrapper for a gdb.Frame object that presents a simpler interface.
+A FrameWrapper forwards most methods to the underlying Frame.
+It omits a few methods, and adds some others.
+Any object conforming to this interface may be returned by a frame filter."""
+
+    def __init__(self, frame):
+        super(FrameWrapper, self).__init__()
+        self.frame = frame
+
+    def name(self):
+        name = self.frame.name()
+        if name is None:
+            name = '??'
+        return name
+
+    def type(self):
+        return self.frame.type()
+
+    def older(self):
+        return self.frame.older()
+
+    def arguments(self):
+        try:
+            block = self.frame.block()
+            return _BlockIterator(self.frame, block, True)
+        except RuntimeError:
+            # It is ok if block() fails.
+            return []
+
+    def locals(self):
+        try:
+            block = self.frame.block()
+            return _BlockIterator(self.frame, block, False)
+        except RuntimeError:
+            # It is ok if block() fails.
+            return []
+
+    def children(self):
+        if hasattr(self.frame, 'children'):
+            return self.frame.children()
+        return []
+
+    def file_and_line(self):
+        sal = self.frame.find_sal()
+        if sal.symtab and sal.symtab.filename:
+            return (sal.symtab.filename, sal.line)
+        return (None, None)
+
+    def library(self):
+        pc = self.frame.pc()
+        return gdb.solib_name(pc)
+
+def _print_symbol(stream, sym, sep):
+    stream.write(sym.name())
+    stream.write(sep)
+    val = sym.value()
+    if val is None:
+        stream.write('???')
+    else:
+        stream.write(str(val))
+
+def _print_args(frame, stream):
+    stream.write(' (')
+    first = True
+    for arg in frame.arguments():
+        if not first:
+            stream.write(', ')
+        first = False
+        _print_symbol(stream, arg, '=')
+    stream.write(')')
+
+def _print_locals(frame, stream):
+    for var in frame.locals():
+        _print_symbol(stream, var, ' = ')
+        stream.write('\n')
+
+def print_frame(frame, stream, full = False, spaces = ''):
+    stream.write(spaces)
+    if frame.type() == gdb.DUMMY_FRAME:
+        stream.write(" <function called from gdb>\n")
+    elif frame.type() == gdb.SIGTRAMP_FRAME:
+        stream.write(" <signal handler called>\n")
+    elif frame.type() == gdb.ARCH_FRAME:
+        stream.write(" <cross-architecture call>\n")
+    else:
+        stream.write(' ')
+        stream.write(frame.name())
+        _print_args(frame, stream)
+        (filename, line) = frame.file_and_line()
+        if filename is not None:
+            stream.write('\n')
+            stream.write(spaces)
+            stream.write('    at ')
+            stream.write(filename)
+            stream.write(':')
+            stream.write(str(line))
+        else:
+            lib = frame.library()
+            if lib is not None:
+                stream.write(' from ')
+                stream.write(lib)
+        stream.write('\n')
+        if full:
+            nstr = cStringIO.StringIO()
+            _print_locals(frame, nstr)
+            for line in nstr.getvalue().splitlines():
+                stream.write(spaces)
+                stream.write('        ')
+                stream.write(line)
+                stream.write('\n')
+            nstr.close()
+ 
+_frame_filters = {}
+
+class FrameFilter(gdb.Parameter):
+    def __init__(self, name):
+        super(FrameFilter, self).__init__('backtrace filter ' + name,
+                                          gdb.COMMAND_STACK,
+                                          gdb.PARAM_ZINTEGER)
+        self.name = name
+        self.value = 1
+        global _frame_filters
+        _frame_filters[name] = self
+
+    def filter(iter):
+        return iter
+
+def _get_value(elt):
+    return elt.value
+
+def _value_gt_0(elt):
+    return elt.value > 0
+
+def create_frame_filter(iter):
+    global _frame_filters
+    elts = filter(_value_gt_0, _frame_filters.values())
+    # FIXME to make it stable, should also sort by name as secondary key
+    elts.sort(key = _get_value, reverse = True)
+    for filt in elts:
+        iter = elts.filter(iter)
+    return iter
+
+def print_filters():
+    global _frame_filters
+    elts = _frame_filters.values()
+    elts.sort(key = _get_value, reverse = True)
+    if not len(elts):
+        print 'No frame filters.'
+    else:
+        # It would be nice to print a short doc string here.
+        print 'Priority\tName'
+        for f in elts:
+            if f.value > 0:
+                print '%-8d\t%s' % (f.value, f.name)
+            else:
+                print 'Disabled\t%s' % f.name


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]