diff options
author | Simon Schampijer <simon@schampijer.de> | 2011-10-29 13:28:50 (GMT) |
---|---|---|
committer | Simon Schampijer <simon@schampijer.de> | 2011-10-29 13:28:50 (GMT) |
commit | 786fb3cabab0bb2e4afe3d39838a70fc45e771c3 (patch) | |
tree | b0b75793563c75281aa3a6ecf3f0d3149a71169e | |
parent | 1a118a7cc1f333ac34239bdb3e8bda2d5e28bd69 (diff) | |
parent | a7777b81e9a305d6df8b85e80f2ef0018fc8bde2 (diff) |
Merge branch 'master' of git.sugarlabs.org:~erikos/sugar-toolkit/sugar-toolkit-gtk3
-rw-r--r-- | configure.ac | 1 | ||||
-rw-r--r-- | src/sugar3/Makefile.am | 8 | ||||
-rw-r--r-- | src/sugar3/__init__.py | 30 | ||||
-rw-r--r-- | src/sugar3/activity/activity.py | 1 | ||||
-rw-r--r-- | src/sugar3/dispatch/Makefile.am | 9 | ||||
-rw-r--r-- | src/sugar3/dispatch/__init__.py | 9 | ||||
-rw-r--r-- | src/sugar3/dispatch/dispatcher.py | 191 | ||||
-rw-r--r-- | src/sugar3/dispatch/license.txt | 66 | ||||
-rw-r--r-- | src/sugar3/dispatch/saferef.py | 250 | ||||
-rw-r--r-- | src/sugar3/logger.py | 199 | ||||
-rw-r--r-- | src/sugar3/sugar-menu.c | 63 | ||||
-rw-r--r-- | src/sugar3/sugar-menu.h | 57 |
12 files changed, 760 insertions, 124 deletions
diff --git a/configure.ac b/configure.ac index e815274..e284cfc 100644 --- a/configure.ac +++ b/configure.ac @@ -46,5 +46,6 @@ src/sugar3/bundle/Makefile src/sugar3/graphics/Makefile src/sugar3/presence/Makefile src/sugar3/datastore/Makefile +src/sugar3/dispatch/Makefile po/Makefile.in ]) diff --git a/src/sugar3/Makefile.am b/src/sugar3/Makefile.am index 4c32796..37d146a 100644 --- a/src/sugar3/Makefile.am +++ b/src/sugar3/Makefile.am @@ -1,8 +1,10 @@ -SUBDIRS = activity bundle graphics presence datastore +SUBDIRS = activity bundle graphics presence datastore dispatch sugardir = $(pythondir)/sugar3 sugar_PYTHON = \ + __init__.py \ env.py \ + logger.py \ network.py \ profile.py \ session.py \ @@ -46,9 +48,7 @@ libsugarext_la_SOURCES = \ sugar-grid.c \ sugar-grid.h \ sugar-key-grabber.c \ - sugar-key-grabber.h \ - sugar-menu.c \ - sugar-menu.h + sugar-key-grabber.h BUILT_SOURCES = \ sugar-marshal.c \ diff --git a/src/sugar3/__init__.py b/src/sugar3/__init__.py new file mode 100644 index 0000000..d24d665 --- /dev/null +++ b/src/sugar3/__init__.py @@ -0,0 +1,30 @@ +# Copyright (C) 2006-2007, Red Hat, Inc. +# Copyright (C) 2007-2008, One Laptop Per Child +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2 of the License, or (at your option) any later version. +# +# This library 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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. + +import os +import gettext + + +if 'SUGAR_PREFIX' in os.environ: + prefix = os.environ['SUGAR_PREFIX'] +else: + prefix = '/usr' + +locale_path = os.path.join(prefix, 'share', 'locale') + +gettext.bindtextdomain('sugar-base', locale_path) diff --git a/src/sugar3/activity/activity.py b/src/sugar3/activity/activity.py index ced8c0f..5652c21 100644 --- a/src/sugar3/activity/activity.py +++ b/src/sugar3/activity/activity.py @@ -267,6 +267,7 @@ class Activity(Window, Gtk.Container): # This code can be removed when we grow an xsettings daemon (the GTK+ # init routines will then automatically figure out the font settings) settings = Gtk.Settings.get_default() + settings.set_property('gtk-theme-name', 'sugar') settings.set_property('gtk-font-name', '%s %f' % (style.FONT_FACE, style.FONT_SIZE)) diff --git a/src/sugar3/dispatch/Makefile.am b/src/sugar3/dispatch/Makefile.am new file mode 100644 index 0000000..3cc8796 --- /dev/null +++ b/src/sugar3/dispatch/Makefile.am @@ -0,0 +1,9 @@ +sugardir = $(pythondir)/sugar3/dispatch +sugar_PYTHON = \ + __init__.py \ + dispatcher.py \ + saferef.py + +EXTRA_DIST = \ + license.txt + diff --git a/src/sugar3/dispatch/__init__.py b/src/sugar3/dispatch/__init__.py new file mode 100644 index 0000000..776b1bc --- /dev/null +++ b/src/sugar3/dispatch/__init__.py @@ -0,0 +1,9 @@ +"""Multi-consumer multi-producer dispatching mechanism + +Originally based on pydispatch (BSD) http://pypi.python.org/pypi/PyDispatcher/2.0.1 +See license.txt for original license. + +Heavily modified for Django's purposes. +""" + +from sugar.dispatch.dispatcher import Signal diff --git a/src/sugar3/dispatch/dispatcher.py b/src/sugar3/dispatch/dispatcher.py new file mode 100644 index 0000000..6855a5b --- /dev/null +++ b/src/sugar3/dispatch/dispatcher.py @@ -0,0 +1,191 @@ +import weakref +try: + set +except NameError: + from sets import Set as set # Python 2.3 fallback + +from sugar.dispatch import saferef + +WEAKREF_TYPES = (weakref.ReferenceType, saferef.BoundMethodWeakref) + +def _make_id(target): + if hasattr(target, 'im_func'): + return (id(target.im_self), id(target.im_func)) + return id(target) + +class Signal(object): + """Base class for all signals + + Internal attributes: + receivers -- { receriverkey (id) : weakref(receiver) } + """ + + def __init__(self, providing_args=None): + """providing_args -- A list of the arguments this signal can pass along in + a send() call. + """ + self.receivers = [] + if providing_args is None: + providing_args = [] + self.providing_args = set(providing_args) + + def connect(self, receiver, sender=None, weak=True, dispatch_uid=None): + """Connect receiver to sender for signal + + receiver -- a function or an instance method which is to + receive signals. Receivers must be + hashable objects. + + if weak is True, then receiver must be weak-referencable + (more precisely saferef.safeRef() must be able to create + a reference to the receiver). + + Receivers must be able to accept keyword arguments. + + If receivers have a dispatch_uid attribute, the receiver will + not be added if another receiver already exists with that + dispatch_uid. + + sender -- the sender to which the receiver should respond + Must either be of type Signal, or None to receive events + from any sender. + + weak -- whether to use weak references to the receiver + By default, the module will attempt to use weak + references to the receiver objects. If this parameter + is false, then strong references will be used. + + dispatch_uid -- an identifier used to uniquely identify a particular + instance of a receiver. This will usually be a string, though it + may be anything hashable. + + returns None + """ + if dispatch_uid: + lookup_key = (dispatch_uid, _make_id(sender)) + else: + lookup_key = (_make_id(receiver), _make_id(sender)) + + if weak: + receiver = saferef.safeRef(receiver, onDelete=self._remove_receiver) + + for r_key, _ in self.receivers: + if r_key == lookup_key: + break + else: + self.receivers.append((lookup_key, receiver)) + + def disconnect(self, receiver=None, sender=None, weak=True, dispatch_uid=None): + """Disconnect receiver from sender for signal + + receiver -- the registered receiver to disconnect. May be none if + dispatch_uid is specified. + sender -- the registered sender to disconnect + weak -- the weakref state to disconnect + dispatch_uid -- the unique identifier of the receiver to disconnect + + disconnect reverses the process of connect. + + If weak references are used, disconnect need not be called. + The receiver will be remove from dispatch automatically. + + returns None + """ + + if dispatch_uid: + lookup_key = (dispatch_uid, _make_id(sender)) + else: + lookup_key = (_make_id(receiver), _make_id(sender)) + + for idx, (r_key, _) in enumerate(self.receivers): + if r_key == lookup_key: + del self.receivers[idx] + + def send(self, sender, **named): + """Send signal from sender to all connected receivers. + + sender -- the sender of the signal + Either a specific object or None. + + named -- named arguments which will be passed to receivers. + + Returns a list of tuple pairs [(receiver, response), ... ]. + + If any receiver raises an error, the error propagates back + through send, terminating the dispatch loop, so it is quite + possible to not have all receivers called if a raises an + error. + """ + + responses = [] + if not self.receivers: + return responses + + for receiver in self._live_receivers(_make_id(sender)): + response = receiver(signal=self, sender=sender, **named) + responses.append((receiver, response)) + return responses + + def send_robust(self, sender, **named): + """Send signal from sender to all connected receivers catching errors + + sender -- the sender of the signal + Can be any python object (normally one registered with + a connect if you actually want something to occur). + + named -- named arguments which will be passed to receivers. + These arguments must be a subset of the argument names + defined in providing_args. + + Return a list of tuple pairs [(receiver, response), ... ], + may raise DispatcherKeyError + + if any receiver raises an error (specifically any subclass of Exception), + the error instance is returned as the result for that receiver. + """ + + responses = [] + if not self.receivers: + return responses + + # Call each receiver with whatever arguments it can accept. + # Return a list of tuple pairs [(receiver, response), ... ]. + for receiver in self._live_receivers(_make_id(sender)): + try: + response = receiver(signal=self, sender=sender, **named) + except Exception, err: + responses.append((receiver, err)) + else: + responses.append((receiver, response)) + return responses + + def _live_receivers(self, senderkey): + """Filter sequence of receivers to get resolved, live receivers + + This checks for weak references + and resolves them, then returning only live + receivers. + """ + none_senderkey = _make_id(None) + + for (receiverkey, r_senderkey), receiver in self.receivers: + if r_senderkey == none_senderkey or r_senderkey == senderkey: + if isinstance(receiver, WEAKREF_TYPES): + # Dereference the weak reference. + receiver = receiver() + if receiver is not None: + yield receiver + else: + yield receiver + + def _remove_receiver(self, receiver): + """Remove dead receivers from connections.""" + + to_remove = [] + for key, connected_receiver in self.receivers: + if connected_receiver == receiver: + to_remove.append(key) + for key in to_remove: + for idx, (r_key, _) in enumerate(self.receivers): + if r_key == key: + del self.receivers[idx] diff --git a/src/sugar3/dispatch/license.txt b/src/sugar3/dispatch/license.txt new file mode 100644 index 0000000..0272c28 --- /dev/null +++ b/src/sugar3/dispatch/license.txt @@ -0,0 +1,66 @@ +sugar.dispatch was originally forked from django.dispatch + +Copyright (c) Django Software Foundation and individual contributors. +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 3. Neither the name of Django nor the names of its contributors may be used + to endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +django.dispatch was originally forked from PyDispatcher. + +PyDispatcher License: + + Copyright (c) 2001-2003, Patrick K. O'Brien and Contributors + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials + provided with the distribution. + + The name of Patrick K. O'Brien, or the name of any Contributor, + may not be used to endorse or promote products derived from this + software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + COPYRIGHT HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + OF THE POSSIBILITY OF SUCH DAMAGE. + diff --git a/src/sugar3/dispatch/saferef.py b/src/sugar3/dispatch/saferef.py new file mode 100644 index 0000000..8bcfd8a --- /dev/null +++ b/src/sugar3/dispatch/saferef.py @@ -0,0 +1,250 @@ +""" +"Safe weakrefs", originally from pyDispatcher. + +Provides a way to safely weakref any function, including bound methods (which +aren't handled by the core weakref module). +""" + +import weakref, traceback + +def safeRef(target, onDelete = None): + """Return a *safe* weak reference to a callable target + + target -- the object to be weakly referenced, if it's a + bound method reference, will create a BoundMethodWeakref, + otherwise creates a simple weakref. + onDelete -- if provided, will have a hard reference stored + to the callable to be called after the safe reference + goes out of scope with the reference object, (either a + weakref or a BoundMethodWeakref) as argument. + """ + if hasattr(target, 'im_self'): + if target.im_self is not None: + # Turn a bound method into a BoundMethodWeakref instance. + # Keep track of these instances for lookup by disconnect(). + assert hasattr(target, 'im_func'), """safeRef target %r has im_self, but no im_func, don't know how to create reference"""%( target,) + reference = get_bound_method_weakref( + target=target, + onDelete=onDelete + ) + return reference + if callable(onDelete): + return weakref.ref(target, onDelete) + else: + return weakref.ref( target ) + +class BoundMethodWeakref(object): + """'Safe' and reusable weak references to instance methods + + BoundMethodWeakref objects provide a mechanism for + referencing a bound method without requiring that the + method object itself (which is normally a transient + object) is kept alive. Instead, the BoundMethodWeakref + object keeps weak references to both the object and the + function which together define the instance method. + + Attributes: + key -- the identity key for the reference, calculated + by the class's calculateKey method applied to the + target instance method + deletionMethods -- sequence of callable objects taking + single argument, a reference to this object which + will be called when *either* the target object or + target function is garbage collected (i.e. when + this object becomes invalid). These are specified + as the onDelete parameters of safeRef calls. + weakSelf -- weak reference to the target object + weakFunc -- weak reference to the target function + + Class Attributes: + _allInstances -- class attribute pointing to all live + BoundMethodWeakref objects indexed by the class's + calculateKey(target) method applied to the target + objects. This weak value dictionary is used to + short-circuit creation so that multiple references + to the same (object, function) pair produce the + same BoundMethodWeakref instance. + + """ + + _allInstances = weakref.WeakValueDictionary() + + def __new__( cls, target, onDelete=None, *arguments,**named ): + """Create new instance or return current instance + + Basically this method of construction allows us to + short-circuit creation of references to already- + referenced instance methods. The key corresponding + to the target is calculated, and if there is already + an existing reference, that is returned, with its + deletionMethods attribute updated. Otherwise the + new instance is created and registered in the table + of already-referenced methods. + """ + key = cls.calculateKey(target) + current =cls._allInstances.get(key) + if current is not None: + current.deletionMethods.append( onDelete) + return current + else: + base = super( BoundMethodWeakref, cls).__new__( cls ) + cls._allInstances[key] = base + base.__init__( target, onDelete, *arguments,**named) + return base + + def __init__(self, target, onDelete=None): + """Return a weak-reference-like instance for a bound method + + target -- the instance-method target for the weak + reference, must have im_self and im_func attributes + and be reconstructable via: + target.im_func.__get__( target.im_self ) + which is true of built-in instance methods. + onDelete -- optional callback which will be called + when this weak reference ceases to be valid + (i.e. either the object or the function is garbage + collected). Should take a single argument, + which will be passed a pointer to this object. + """ + def remove(weak, self=self): + """Set self.isDead to true when method or instance is destroyed""" + methods = self.deletionMethods[:] + del self.deletionMethods[:] + try: + del self.__class__._allInstances[ self.key ] + except KeyError: + pass + for function in methods: + try: + if callable( function ): + function( self ) + except Exception, e: + try: + traceback.print_exc() + except AttributeError, err: + print '''Exception during saferef %s cleanup function %s: %s'''%( + self, function, e + ) + self.deletionMethods = [onDelete] + self.key = self.calculateKey( target ) + self.weakSelf = weakref.ref(target.im_self, remove) + self.weakFunc = weakref.ref(target.im_func, remove) + self.selfName = str(target.im_self) + self.funcName = str(target.im_func.__name__) + + def calculateKey( cls, target ): + """Calculate the reference key for this reference + + Currently this is a two-tuple of the id()'s of the + target object and the target function respectively. + """ + return (id(target.im_self),id(target.im_func)) + calculateKey = classmethod( calculateKey ) + + def __str__(self): + """Give a friendly representation of the object""" + return """%s( %s.%s )"""%( + self.__class__.__name__, + self.selfName, + self.funcName, + ) + + __repr__ = __str__ + + def __nonzero__( self ): + """Whether we are still a valid reference""" + return self() is not None + + def __cmp__( self, other ): + """Compare with another reference""" + if not isinstance (other,self.__class__): + return cmp( self.__class__, type(other) ) + return cmp( self.key, other.key) + + def __call__(self): + """Return a strong reference to the bound method + + If the target cannot be retrieved, then will + return None, otherwise returns a bound instance + method for our object and function. + + Note: + You may call this method any number of times, + as it does not invalidate the reference. + """ + target = self.weakSelf() + if target is not None: + function = self.weakFunc() + if function is not None: + return function.__get__(target) + return None + +class BoundNonDescriptorMethodWeakref(BoundMethodWeakref): + """A specialized BoundMethodWeakref, for platforms where instance methods + are not descriptors. + + It assumes that the function name and the target attribute name are the + same, instead of assuming that the function is a descriptor. This approach + is equally fast, but not 100% reliable because functions can be stored on an + attribute named differenty than the function's name such as in: + + class A: pass + def foo(self): return "foo" + A.bar = foo + + But this shouldn't be a common use case. So, on platforms where methods + aren't descriptors (such as Jython) this implementation has the advantage + of working in the most cases. + """ + def __init__(self, target, onDelete=None): + """Return a weak-reference-like instance for a bound method + + target -- the instance-method target for the weak + reference, must have im_self and im_func attributes + and be reconstructable via: + target.im_func.__get__( target.im_self ) + which is true of built-in instance methods. + onDelete -- optional callback which will be called + when this weak reference ceases to be valid + (i.e. either the object or the function is garbage + collected). Should take a single argument, + which will be passed a pointer to this object. + """ + assert getattr(target.im_self, target.__name__) == target, \ + ("method %s isn't available as the attribute %s of %s" % + (target, target.__name__, target.im_self)) + super(BoundNonDescriptorMethodWeakref, self).__init__(target, onDelete) + + def __call__(self): + """Return a strong reference to the bound method + + If the target cannot be retrieved, then will + return None, otherwise returns a bound instance + method for our object and function. + + Note: + You may call this method any number of times, + as it does not invalidate the reference. + """ + target = self.weakSelf() + if target is not None: + function = self.weakFunc() + if function is not None: + # Using curry() would be another option, but it erases the + # "signature" of the function. That is, after a function is + # curried, the inspect module can't be used to determine how + # many arguments the function expects, nor what keyword + # arguments it supports, and pydispatcher needs this + # information. + return getattr(target, function.__name__) + return None + +def get_bound_method_weakref(target, onDelete): + """Instantiates the appropiate BoundMethodWeakRef, depending on the details of + the underlying class method implementation""" + if hasattr(target, '__get__'): + # target method is a descriptor, so the default implementation works: + return BoundMethodWeakref(target=target, onDelete=onDelete) + else: + # no luck, use the alternative implementation: + return BoundNonDescriptorMethodWeakref(target=target, onDelete=onDelete) diff --git a/src/sugar3/logger.py b/src/sugar3/logger.py new file mode 100644 index 0000000..275c57d --- /dev/null +++ b/src/sugar3/logger.py @@ -0,0 +1,199 @@ +# Copyright (C) 2007 Red Hat, Inc. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2 of the License, or (at your option) any later version. +# +# This library 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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. + +"""Logging service setup. + +STABLE. +""" + +import array +import collections +import errno +import logging +import sys +import os +import repr as repr_ +import decorator + +# Let's keep this module self contained so that it can be easily +# pasted in external sugar service like the datastore. + +# traces function calls, use SUGAR_LOGGER_LEVEL=trace to enable +TRACE = 5 +_LEVELS = { + 'error': logging.ERROR, + 'warning': logging.WARNING, + 'debug': logging.DEBUG, + 'info': logging.INFO, + 'trace': TRACE, + 'all': 0, +} +logging.addLevelName(TRACE, 'TRACE') + + +def get_logs_dir(): + profile = os.environ.get('SUGAR_PROFILE', 'default') + logs_dir = os.environ.get('SUGAR_LOGS_DIR', + os.path.join(os.path.expanduser('~'), + '.sugar', profile, 'logs')) + return logs_dir + + +def set_level(level): + if level in _LEVELS: + logging.getLogger('').setLevel(_LEVELS[level]) + return + + try: + logging.getLogger('').setLevel(int(level)) + except ValueError: + logging.warning('Invalid log level: %r', level) + + +# pylint: disable-msg=E1101,F0401 +def _except_hook(exctype, value, traceback): + # Attempt to provide verbose IPython tracebacks. + # Importing IPython is slow, so we import it lazily. + try: + from IPython.ultraTB import AutoFormattedTB + sys.excepthook = AutoFormattedTB(mode='Verbose', + color_scheme='NoColor') + except ImportError: + sys.excepthook = sys.__excepthook__ + + sys.excepthook(exctype, value, traceback) + + +def start(log_filename=None): + # remove existing handlers, or logging.basicConfig() won't have no effect. + root_logger = logging.getLogger('') + for handler in root_logger.handlers: + root_logger.removeHandler(handler) + + class SafeLogWrapper(object): + """Small file-like wrapper to gracefully handle ENOSPC errors when + logging.""" + + def __init__(self, stream): + self._stream = stream + + def write(self, s): + try: + self._stream.write(s) + except IOError, e: + # gracefully deal w/ disk full + if e.errno != errno.ENOSPC: + raise e + + def flush(self): + try: + self._stream.flush() + except IOError, e: + # gracefully deal w/ disk full + if e.errno != errno.ENOSPC: + raise e + + logging.basicConfig(level=logging.WARNING, + format="%(created)f %(levelname)s %(name)s: %(message)s", + stream=SafeLogWrapper(sys.stderr)) + + if 'SUGAR_LOGGER_LEVEL' in os.environ: + set_level(os.environ['SUGAR_LOGGER_LEVEL']) + + if log_filename: + try: + log_path = os.path.join(get_logs_dir(), log_filename + '.log') + + log_fd = os.open(log_path, os.O_WRONLY | os.O_CREAT) + os.dup2(log_fd, sys.stdout.fileno()) + os.dup2(log_fd, sys.stderr.fileno()) + os.close(log_fd) + + sys.stdout = SafeLogWrapper(sys.stdout) + sys.stderr = SafeLogWrapper(sys.stderr) + except OSError, e: + # if we're out of space, just continue + if e.errno != errno.ENOSPC: + raise e + + sys.excepthook = _except_hook + + +class TraceRepr(repr_.Repr): + + # better handling of subclasses of basic types, e.g. for DBus + _TYPES = [int, long, bool, tuple, list, array.array, set, frozenset, + collections.deque, dict, str] + + def repr1(self, x, level): + for t in self._TYPES: + if isinstance(x, t): + return getattr(self, 'repr_'+t.__name__)(x, level) + + return repr_.Repr.repr1(self, x, level) + + def repr_int(self, x, level): + return repr(x) + + def repr_bool(self, x, level): + return repr(x) + + +def trace(logger=None, logger_name=None, skip_args=None, skip_kwargs=None, + maxsize_list=30, maxsize_dict=30, maxsize_string=300): + + if skip_args is None: + skip_args = [] + + if skip_kwargs is None: + skip_kwargs = [] + + # size-limit repr() + trace_repr = TraceRepr() + trace_repr.maxlist = maxsize_list + trace_repr.maxdict = maxsize_dict + trace_repr.maxstring = maxsize_string + trace_repr.maxother = maxsize_string + trace_logger = logger or logging.getLogger(logger_name) + + def _trace(f, *args, **kwargs): + # don't do expensive formatting if loglevel TRACE is not enabled + enabled = trace_logger.isEnabledFor(TRACE) + if not enabled: + return f(*args, **kwargs) + + params_formatted = ", ".join( + [trace_repr.repr(a) + for (idx, a) in enumerate(args) if idx not in skip_args] + \ + ['%s=%s' % (k, trace_repr.repr(v)) + for (k, v) in kwargs.items() if k not in skip_kwargs]) + + trace_logger.log(TRACE, "%s(%s) invoked", f.__name__, + params_formatted) + + try: + res = f(*args, **kwargs) + except: + trace_logger.exception("Exception occured in %s", f.__name__) + raise + + trace_logger.log(TRACE, "%s(%s) returned %s", f.__name__, + params_formatted, trace_repr.repr(res)) + + return res + + return decorator.decorator(_trace) diff --git a/src/sugar3/sugar-menu.c b/src/sugar3/sugar-menu.c deleted file mode 100644 index f19dc4b..0000000 --- a/src/sugar3/sugar-menu.c +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (C) 2006-2007, Red Hat, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#include <gtk/gtkwindow.h> - -#include "sugar-menu.h" - -static void sugar_menu_class_init (SugarMenuClass *menu_class); -static void sugar_menu_init (SugarMenu *menu); - - -G_DEFINE_TYPE(SugarMenu, sugar_menu, GTK_TYPE_MENU) - -void -sugar_menu_set_active(SugarMenu *menu, gboolean active) -{ - GTK_MENU_SHELL(menu)->active = active; -} - -void -sugar_menu_embed(SugarMenu *menu, GtkContainer *parent) -{ - menu->orig_toplevel = GTK_MENU(menu)->toplevel; - - GTK_MENU(menu)->toplevel = gtk_widget_get_toplevel(GTK_WIDGET(parent)); - gtk_widget_reparent(GTK_WIDGET(menu), GTK_WIDGET(parent)); -} - -void -sugar_menu_unembed(SugarMenu *menu) -{ - if (menu->orig_toplevel) { - GTK_MENU(menu)->toplevel = menu->orig_toplevel; - gtk_widget_reparent(GTK_WIDGET(menu), GTK_WIDGET(menu->orig_toplevel)); - } -} - -static void -sugar_menu_class_init(SugarMenuClass *menu_class) -{ -} - -static void -sugar_menu_init(SugarMenu *menu) -{ - menu->orig_toplevel = NULL; -} diff --git a/src/sugar3/sugar-menu.h b/src/sugar3/sugar-menu.h deleted file mode 100644 index c3bb3d0..0000000 --- a/src/sugar3/sugar-menu.h +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (C) 2006-2007, Red Hat, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#ifndef __SUGAR_MENU_H__ -#define __SUGAR_MENU_H__ - -#include <gtk/gtk.h> - -G_BEGIN_DECLS - -typedef struct _SugarMenu SugarMenu; -typedef struct _SugarMenuClass SugarMenuClass; - -#define SUGAR_TYPE_MENU (sugar_menu_get_type()) -#define SUGAR_MENU(object) (G_TYPE_CHECK_INSTANCE_CAST((object), SUGAR_TYPE_MENU, SugarMenu)) -#define SUGAR_MENU_CLASS(klass) (G_TYPE_CHACK_CLASS_CAST((klass), SUGAR_TYPE_MENU, SugarMenuClass)) -#define SUGAR_IS_MENU(object) (G_TYPE_CHECK_INSTANCE_TYPE((object), SUGAR_TYPE_MENU)) -#define SUGAR_IS_MENU_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), SUGAR_TYPE_MENU)) -#define SUGAR_MENU_GET_CLASS(object) (G_TYPE_INSTANCE_GET_CLASS((object), SUGAR_TYPE_MENU, SugarMenuClass)) - -struct _SugarMenu { - GtkMenu base_instance; - - GtkWidget *orig_toplevel; - int min_width; -}; - -struct _SugarMenuClass { - GtkMenuClass base_class; -}; - -GType sugar_menu_get_type (void); -void sugar_menu_set_active (SugarMenu *menu, - gboolean active); -void sugar_menu_embed (SugarMenu *menu, - GtkContainer *parent); -void sugar_menu_unembed (SugarMenu *menu); - -G_END_DECLS - -#endif /* __SUGAR_MENU_H__ */ |