/wimpiggy/util.py
Python | 126 lines | 106 code | 8 blank | 12 comment | 2 complexity | f6c9355c416c361109901405d997c8c8 MD5 | raw file
Possible License(s): GPL-2.0
1# This file is part of Parti. 2# Copyright (C) 2008, 2009 Nathaniel Smith <njs@pobox.com> 3# Parti is released under the terms of the GNU GPL v2, or, at your option, any 4# later version. See the file COPYING for details. 5 6import traceback 7import sys 8import gobject 9 10class AutoPropGObjectMixin(object): 11 """Mixin for automagic property support in GObjects. 12 13 Make sure this is the first entry on your parent list, so super().__init__ 14 will work right.""" 15 def __init__(self): 16 super(AutoPropGObjectMixin, self).__init__() 17 self._gproperties = {} 18 19 def _munge_property_name(self, name): 20 return name.replace("-", "_") 21 22 def do_get_property(self, pspec): 23 getter = "do_get_property_" + self._munge_property_name(pspec.name) 24 if hasattr(self, getter): 25 return getattr(self, getter)(pspec.name) 26 return self._gproperties.get(pspec.name) 27 28 def do_set_property(self, pspec, value): 29 self._internal_set_property(pspec.name, value) 30 31 # Exposed for subclasses that wish to set readonly properties -- 32 # .set_property (the public api) will fail, but the property can still be 33 # modified via this method. 34 def _internal_set_property(self, name, value): 35 setter = "do_set_property_" + self._munge_property_name(name) 36 if hasattr(self, setter): 37 getattr(self, setter)(name, value) 38 else: 39 self._gproperties[name] = value 40 self.notify(name) 41 42 43def dump_exc(): 44 """Call this from a except: clause to print a nice traceback.""" 45 print "".join(traceback.format_exception(*sys.exc_info())) 46 47 48# A simple little class whose instances we can stick random bags of attributes 49# on. 50class AdHocStruct(object): 51 def __repr__(self): 52 return ("<%s object, contents: %r>" 53 % (type(self).__name__, self.__dict__)) 54 55def n_arg_signal(n): 56 return (gobject.SIGNAL_RUN_LAST, 57 gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,) * n) 58no_arg_signal = n_arg_signal(0) 59one_arg_signal = n_arg_signal(1) 60 61 62# Collects the results from signal handlers for a given signal into a list, 63# ignoring all handlers that return None. (This filtering is useful because 64# the intended use of this method is to "poll" all connected objects, so it's 65# pretty useless to call a default do_* method... but even if such a method is 66# not defined, a default implementation will still be called automatically, 67# and that implementation simply returns None.) 68def non_none_list_accumulator(ihint, return_accu, handler_return): 69 if return_accu is None: 70 return_accu = [] 71 if handler_return is not None: 72 return_accu += [handler_return] 73 return True, return_accu 74 75 76# *Fully* exits the gtk main loop, even in the presence of recursive calls to 77# gtk.main(). Useful when you really just want to quit, and don't want to 78# care if you're inside a recursive mainloop. 79def gtk_main_quit_really(): 80 # We used to call gtk.main_quit() repeatedly, but this doesn't actually 81 # work -- gtk.main_quit() always marks the *current* level of main loop 82 # for destruction, so it's actually idempotent. We have to call 83 # gtk.main_quit once, and then return to the main loop, and then call it 84 # again, and then return to the main loop, etc. So we use a trick: We 85 # register a function that gtk should call 'forever' (i.e., as long as the 86 # main loop is running!) 87 def gtk_main_quit_forever(): 88 # We import gtk inside here, rather than at the top of the file, 89 # because importing gtk has the side-effect of trying to connect to 90 # the X server (and this process may block, may cause us to later be 91 # killed if the X server goes away, etc.), and we don't want to impose 92 # that on every user of wimpiggy.util. 93 import gtk 94 gtk.main_quit() 95 # So long as there are more nested main loops, re-register ourselves 96 # to be called again: 97 if gtk.main_level() > 1: 98 return True 99 else: 100 # But when we've just quit the outermost main loop, then 101 # unregister ourselves so that it's possible to start the 102 # main-loop again if desired: 103 return False 104 gobject.timeout_add(0, gtk_main_quit_forever) 105 106# If a user hits control-C, and we are currently executing Python code below 107# the main loop, then the exception will get swallowed up. (If we're just 108# idling in the main loop, then it will pass the exception along, but it won't 109# propagate it from Python code. Sigh.) But sys.excepthook will still get 110# called with such exceptions. 111def gtk_main_quit_on_fatal_exceptions_enable(): 112 oldhook = sys.excepthook 113 def gtk_main_quit_on_fatal_exception(type, val, tb): 114 if issubclass(type, (KeyboardInterrupt, SystemExit)): 115 print "Shutting down main-loop" 116 gtk_main_quit_really() 117 if issubclass(type, RuntimeError) and "recursion" in val.message: 118 # We weren't getting tracebacks from this -- maybe calling oldhook 119 # was hitting the limit again or something? -- so try this 120 # instead. (I don't know why print_exception wouldn't trigger the 121 # same problem as calling oldhook, though.) 122 print traceback.print_exception(type, val, tb) 123 print "Maximum recursion depth exceeded" 124 else: 125 return oldhook(type, val, tb) 126 sys.excepthook = gtk_main_quit_on_fatal_exception