/wimpiggy/util.py

https://code.google.com/p/partiwm/ · Python · 126 lines · 61 code · 16 blank · 49 comment · 11 complexity · f6c9355c416c361109901405d997c8c8 MD5 · raw file

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