0001from __future__ import print_function
0002import sys
0003import types
0004from pydispatch import dispatcher
0005from weakref import ref
0006
0007
0008subclassClones = {}
0009
0010def listen(receiver, soClass, signal, alsoSubclasses=True, weak=True):
0011    """
0012    Listen for the given ``signal`` on the SQLObject subclass
0013    ``soClass``, calling ``receiver()`` when ``send(soClass, signal,
0014    ...)`` is called.
0015
0016    If ``alsoSubclasses`` is true, receiver will also be called when
0017    an event is fired on any subclass.
0018    """
0019    dispatcher.connect(receiver, signal=signal, sender=soClass, weak=weak)
0020    weakReceiver = ref(receiver)
0021    subclassClones.setdefault(soClass, []).append((weakReceiver, signal))
0022
0023# We export this function:
0024send = dispatcher.send
0025
0026class Signal(object):
0027    """
0028    Base event for all SQLObject events.
0029
0030    In general the sender for these methods is the class, not the
0031    instance.
0032    """
0033
0034class ClassCreateSignal(Signal):
0035    """
0036    Signal raised after class creation.  The sender is the superclass
0037    (in case of multiple superclasses, the first superclass).  The
0038    arguments are ``(new_class_name, bases, new_attrs, post_funcs,
0039    early_funcs)``.  ``new_attrs`` is a dictionary and may be modified
0040    (but ``new_class_name`` and ``bases`` are immutable).
0041    ``post_funcs`` is an initially-empty list that can have callbacks
0042    appended to it.
0043
0044    Note: at the time this event is called, the new class has not yet
0045    been created.  The functions in ``post_funcs`` will be called
0046    after the class is created, with the single arguments of
0047    ``(new_class)``.  Also, ``early_funcs`` will be called at the
0048    soonest possible time after class creation (``post_funcs`` is
0049    called after the class's ``__classinit__``).
0050    """
0051
0052def _makeSubclassConnections(new_class_name, bases, new_attrs,
0053                             post_funcs, early_funcs):
0054    early_funcs.insert(0, _makeSubclassConnectionsPost)
0055
0056def _makeSubclassConnectionsPost(new_class):
0057    for cls in new_class.__bases__:
0058        for weakReceiver, signal in subclassClones.get(cls, []):
0059            receiver = weakReceiver()
0060            if not receiver:
0061                continue
0062            listen(receiver, new_class, signal)
0063
0064dispatcher.connect(_makeSubclassConnections, signal=ClassCreateSignal)
0065
0066# @@: Should there be a class reload event?  This would allow modules
0067# to be reloaded, possibly.  Or it could even be folded into
0068# ClassCreateSignal, since anything that listens to that needs to pay
0069# attention to reloads (or else it is probably buggy).
0070
0071class RowCreateSignal(Signal):
0072    """
0073    Called before an instance is created, with the class as the
0074    sender.  Called with the arguments ``(instance, kwargs, post_funcs)``.
0075    There may be a ``connection`` argument.  ``kwargs``may be usefully
0076    modified.  ``post_funcs`` is a list of callbacks, intended to have
0077    functions appended to it, and are called with the arguments
0078    ``(new_instance)``.
0079
0080    Note: this is not called when an instance is created from an
0081    existing database row.
0082    """
0083class RowCreatedSignal(Signal):
0084    """
0085    Called after an instance is created, with the class as the
0086    sender.  Called with the arguments ``(instance, kwargs, post_funcs)``.
0087    There may be a ``connection`` argument.  ``kwargs``may be usefully
0088    modified.  ``post_funcs`` is a list of callbacks, intended to have
0089    functions appended to it, and are called with the arguments
0090    ``(new_instance)``.
0091
0092    Note: this is not called when an instance is created from an
0093    existing database row.
0094    """
0095# @@: An event for getting a row?  But for each row, when doing a
0096# select?  For .sync, .syncUpdate, .expire?
0097
0098class RowDestroySignal(Signal):
0099    """
0100    Called before an instance is deleted.  Sender is the instance's
0101    class.  Arguments are ``(instance, post_funcs)``.
0102
0103    ``post_funcs`` is a list of callbacks, intended to have
0104    functions appended to it, and are called with arguments ``(instance)``.
0105    If any of the post_funcs raises an exception, the deletion is only
0106    affected if this will prevent a commit.
0107
0108    You cannot cancel the delete, but you can raise an exception (which will
0109    probably cancel the delete, but also cause an uncaught exception if not
0110    expected).
0111
0112    Note: this is not called when an instance is destroyed through
0113    garbage collection.
0114
0115    @@: Should this allow ``instance`` to be a primary key, so that a
0116    row can be deleted without first fetching it?
0117    """
0118
0119class RowDestroyedSignal(Signal):
0120    """
0121    Called after an instance is deleted.  Sender is the instance's
0122    class.  Arguments are ``(instance)``.
0123
0124    This is called before the post_funcs of RowDestroySignal
0125
0126    Note: this is not called when an instance is destroyed through
0127    garbage collection.
0128    """
0129
0130class RowUpdateSignal(Signal):
0131    """
0132    Called when an instance is updated through a call to ``.set()``
0133    (or a column attribute assignment).  The arguments are
0134    ``(instance, kwargs)``.  ``kwargs`` can be modified.  This is run
0135    *before* the instance is updated; if you want to look at the
0136    current values, simply look at ``instance``.
0137    """
0138
0139class RowUpdatedSignal(Signal):
0140    """
0141    Called when an instance is updated through a call to ``.set()``
0142    (or a column attribute assignment).  The arguments are
0143    ``(instance, post_funcs)``. ``post_funcs`` is a list of callbacks,
0144    intended to have functions appended to it, and are called with the
0145    arguments ``(new_instance)``. This is run *after* the instance is
0146    updated; Works better with lazyUpdate = True.
0147    """
0148
0149class AddColumnSignal(Signal):
0150    """
0151    Called when a column is added to a class, with arguments ``(cls,
0152    connection, column_name, column_definition, changeSchema,
0153    post_funcs)``.  This is called *after* the column has been added,
0154    and is called for each column after class creation.
0155
0156    post_funcs are called with ``(cls, so_column_obj)``
0157    """
0158
0159class DeleteColumnSignal(Signal):
0160    """
0161    Called when a column is removed from a class, with the arguments
0162    ``(cls, connection, column_name, so_column_obj, post_funcs)``.
0163    Like ``AddColumnSignal`` this is called after the action has been
0164    performed, and is called for subclassing (when a column is
0165    implicitly removed by setting it to ``None``).
0166
0167    post_funcs are called with ``(cls, so_column_obj)``
0168    """
0169
0170# @@: Signals for indexes and joins?  These are mostly event consumers,
0171# though.
0172
0173class CreateTableSignal(Signal):
0174    """
0175    Called when a table is created.  If ``ifNotExists==True`` and the
0176    table exists, this event is not called.
0177
0178    Called with ``(cls, connection, extra_sql, post_funcs)``.
0179    ``extra_sql`` is a list (which can be appended to) of extra SQL
0180    statements to be run after the table is created.  ``post_funcs``
0181    functions are called with ``(cls, connection)`` after the table
0182    has been created.  Those functions are *not* called simply when
0183    constructing the SQL.
0184    """
0185
0186class DropTableSignal(Signal):
0187    """
0188    Called when a table is dropped.  If ``ifExists==True`` and the
0189    table doesn't exist, this event is not called.
0190
0191    Called with ``(cls, connection, extra_sql, post_funcs)``.
0192    ``post_funcs`` functions are called with ``(cls, connection)``
0193    after the table has been dropped.
0194    """
0195
0196############################################################
0197## Event Debugging
0198############################################################
0199
0200def summarize_events_by_sender(sender=None, output=None, indent=0):
0201    """
0202    Prints out a summary of the senders and listeners in the system,
0203    for debugging purposes.
0204    """
0205    if output is None:
0206        output = sys.stdout
0207    leader = ' ' * indent
0208    if sender is None:
0209        send_list = [
0210            (deref(dispatcher.senders.get(sid)), listeners)
0211            for sid, listeners in dispatcher.connections.items()
0212            if deref(dispatcher.senders.get(sid))]
0213        for sender, listeners in sorted_items(send_list):
0214            real_sender = deref(sender)
0215            if not real_sender:
0216                continue
0217            header = 'Sender: %r' % real_sender
0218            print(leader + header, file=output)
0219            print(leader + ('=' * len(header)), file=output)
0220            summarize_events_by_sender(real_sender, output=output, indent=indent+2)
0221    else:
0222        for signal, receivers in sorted_items(dispatcher.connections.get(id(sender), [])):
0223            receivers = [deref(r) for r in receivers if deref(r)]
0224            header = 'Signal: %s (%i receivers)' % (sort_name(signal),
0225                                                    len(receivers))
0226            print(leader + header, file=output)
0227            print(leader + ('-' * len(header)), file=output)
0228            for receiver in sorted(receivers, key=sort_name):
0229                print(leader + '  ' + nice_repr(receiver), file=output)
0230
0231def deref(value):
0232    if isinstance(value, dispatcher.WEAKREF_TYPES):
0233        return value()
0234    else:
0235        return value
0236
0237def sorted_items(a_dict):
0238    if isinstance(a_dict, dict):
0239        a_dict = a_dict.items()
0240    return sorted(a_dict, key=lambda t: sort_name(t[0]))
0241
0242def sort_name(value):
0243    if isinstance(value, type):
0244        return value.__name__
0245    elif isinstance(value, types.FunctionType):
0246        return value.func_name
0247    else:
0248        return str(value)
0249
0250_real_dispatcher_send = dispatcher.send
0251_real_dispatcher_sendExact = dispatcher.sendExact
0252_real_dispatcher_disconnect = dispatcher.disconnect
0253_real_dispatcher_connect = dispatcher.connect
0254_debug_enabled = False
0255def debug_events():
0256    global _debug_enabled, send
0257    if _debug_enabled:
0258        return
0259    _debug_enabled = True
0260    dispatcher.send = send = _debug_send
0261    dispatcher.sendExact = _debug_sendExact
0262    dispatcher.disconnect = _debug_disconnect
0263    dispatcher.connect = _debug_connect
0264
0265def _debug_send(signal=dispatcher.Any, sender=dispatcher.Anonymous,
0266                *arguments, **named):
0267    print("send %s from %s: %s" % (
0268          nice_repr(signal), nice_repr(sender),
0269          fmt_args(*arguments, **named)))
0270    return _real_dispatcher_send(signal, sender, *arguments, **named)
0271
0272def _debug_sendExact(signal=dispatcher.Any, sender=dispatcher.Anonymous,
0273                     *arguments, **named):
0274    print("sendExact %s from %s: %s" % (
0275          nice_repr(signal), nice_repr(sender), fmt_args(*arguments, **name)))
0276    return _real_dispatcher_sendExact(signal, sender, *arguments, **named)
0277
0278def _debug_connect(receiver, signal=dispatcher.Any, sender=dispatcher.Any,
0279                   weak=True):
0280    print("connect %s to %s signal %s" % (
0281          nice_repr(receiver), nice_repr(signal), nice_repr(sender)))
0282    return _real_dispatcher_connect(receiver, signal, sender, weak)
0283
0284def _debug_disconnect(receiver, signal=dispatcher.Any, sender=dispatcher.Any,
0285                      weak=True):
0286    print("disconnecting %s from %s signal %s" % (
0287          nice_repr(receiver), nice_repr(signal), nice_repr(sender)))
0288    return disconnect(receiver, signal, sender, weak)
0289
0290def fmt_args(*arguments, **name):
0291    args = [repr(a) for a in arguments]
0292    args.extend([
0293        '%s=%r' % (n, v) for n, v in sorted(name.items())])
0294    return ', '.join(args)
0295
0296def nice_repr(v):
0297    """
0298    Like repr(), but nicer for debugging here.
0299    """
0300    if isinstance(v, (types.ClassType, type)):
0301        return v.__module__ + '.' + v.__name__
0302    elif isinstance(v, types.FunctionType):
0303        if '__name__' in v.func_globals:
0304            if getattr(sys.modules[v.func_globals['__name__']],
0305                       v.func_name, None) is v:
0306                return '%s.%s' % (v.func_globals['__name__'], v.func_name)
0307        return repr(v)
0308    elif isinstance(v, types.MethodType):
0309        return '%s.%s of %s' % (
0310            nice_repr(v.im_class), v.im_func.func_name,
0311            nice_repr(v.im_self))
0312    else:
0313        return repr(v)
0314
0315
0316__all__ = ['listen', 'send']
0317for name, value in globals().items():
0318    if isinstance(value, type) and issubclass(value, Signal):
0319        __all__.append(name)