[SQL-CVS] r1220 - in SQLObject/trunk/sqlobject: . tests
SQLObject is a Python ORM.
Brought to you by:
ianbicking,
phd
|
From: <sub...@co...> - 2005-11-04 05:20:02
|
Author: ianb
Date: 2005-11-04 05:19:49 +0000 (Fri, 04 Nov 2005)
New Revision: 1220
Modified:
SQLObject/trunk/sqlobject/conftest.py
SQLObject/trunk/sqlobject/declarative.py
SQLObject/trunk/sqlobject/events.py
SQLObject/trunk/sqlobject/tests/test_events.py
Log:
Added tests for column adding events; fixed a bug with inheriting events (they weren't inherited immediately); added some debugging routines for events; added an option to py.test for debugging events
Modified: SQLObject/trunk/sqlobject/conftest.py
===================================================================
--- SQLObject/trunk/sqlobject/conftest.py 2005-11-03 22:18:32 UTC (rev 1219)
+++ SQLObject/trunk/sqlobject/conftest.py 2005-11-04 05:19:49 UTC (rev 1220)
@@ -45,7 +45,12 @@
Option('-O', '--SQL-output',
action="store_true", dest="show_sql_output", default=False,
help="Show output from SQL statements (when capturing "
- "stdout the output is only displayed when a test fails)"))
+ "stdout the output is only displayed when a test fails)"),
+ Option('-E', '--events',
+ action="store_true", dest="debug_events", default=False,
+ help="Debug events (print information about events as they are "
+ "sent)"),
+ )
class SQLObjectClass(py.test.collect.Class):
def run(self):
@@ -55,3 +60,8 @@
return super(SQLObjectClass, self).run()
Class = SQLObjectClass
+
+def setup_tests():
+ if option.debug_events:
+ from sqlobject import events
+ events.debug_events()
Modified: SQLObject/trunk/sqlobject/declarative.py
===================================================================
--- SQLObject/trunk/sqlobject/declarative.py 2005-11-03 22:18:32 UTC (rev 1219)
+++ SQLObject/trunk/sqlobject/declarative.py 2005-11-04 05:19:49 UTC (rev 1220)
@@ -90,10 +90,13 @@
def __new__(meta, class_name, bases, new_attrs):
post_funcs = []
+ early_funcs = []
events.send(events.ClassCreateSignal,
bases[0], class_name, bases, new_attrs,
- post_funcs)
+ post_funcs, early_funcs)
cls = type.__new__(meta, class_name, bases, new_attrs)
+ for func in early_funcs:
+ func(cls)
if new_attrs.has_key('__classinit__'):
cls.__classinit__ = staticmethod(cls.__classinit__.im_func)
cls.__classinit__(cls, new_attrs)
Modified: SQLObject/trunk/sqlobject/events.py
===================================================================
--- SQLObject/trunk/sqlobject/events.py 2005-11-03 22:18:32 UTC (rev 1219)
+++ SQLObject/trunk/sqlobject/events.py 2005-11-04 05:19:49 UTC (rev 1220)
@@ -1,3 +1,5 @@
+import sys
+import types
from sqlobject.include.pydispatch import dispatcher
from weakref import ref
@@ -30,21 +32,25 @@
class ClassCreateSignal(Signal):
"""
- Signal raised before class creation. The sender is the superclass
+ Signal raised after class creation. The sender is the superclass
(in case of multiple superclasses, the first superclass). The
- arguments are ``(new_class_name, bases, new_attrs, post_funcs)``.
- ``new_attrs`` is a dictionary and may be modified (but
- ``new_class_name`` and ``bases`` are immutable). ``post_funcs``
- is an initially-empty list that can have callbacks appended to it.
+ arguments are ``(new_class_name, bases, new_attrs, post_funcs,
+ early_funcs)``. ``new_attrs`` is a dictionary and may be modified
+ (but ``new_class_name`` and ``bases`` are immutable).
+ ``post_funcs`` is an initially-empty list that can have callbacks
+ appended to it.
Note: at the time this event is called, the new class has not yet
been created. The functions in ``post_funcs`` will be called
after the class is created, with the single arguments of
- ``(new_class)``.
+ ``(new_class)``. Also, ``early_funcs`` will be called at the
+ soonest possible time after class creation (``post_funcs`` is
+ called after the class's ``__classinit__``).
"""
-def _makeSubclassConnections(new_class_name, bases, new_attrs, post_funcs):
- post_funcs.append(_makeSubclassConnectionsPost)
+def _makeSubclassConnections(new_class_name, bases, new_attrs,
+ post_funcs, early_funcs):
+ early_funcs.insert(0, _makeSubclassConnectionsPost)
def _makeSubclassConnectionsPost(new_class):
for cls in new_class.__bases__:
@@ -150,6 +156,123 @@
after the table has been dropped.
"""
+############################################################
+## Event Debugging
+############################################################
+
+def summarize_events_by_sender(sender=None, output=None, indent=0):
+ """
+ Prints out a summary of the senders and listeners in the system,
+ for debugging purposes.
+ """
+ if output is None:
+ output = sys.stdout
+ if sender is None:
+ send_list = [
+ (deref(dispatcher.senders.get(sid)), listeners)
+ for sid, listeners in dispatcher.connections.items()
+ if deref(dispatcher.senders.get(sid))]
+ for sender, listeners in sorted_items(send_list):
+ real_sender = deref(sender)
+ if not real_sender:
+ continue
+ header = 'Sender: %r' % real_sender
+ print >> output, (' '*indent) + header
+ print >> output, (' '*indent) + '='*len(header)
+ summarize_events_by_sender(real_sender, output=output, indent=indent+2)
+ else:
+ for signal, receivers in sorted_items(dispatcher.connections.get(id(sender), [])):
+ receivers = [deref(r) for r in receivers if deref(r)]
+ header = 'Signal: %s (%i receivers)' % (sort_name(signal),
+ len(receivers))
+ print >> output, (' '*indent) + header
+ print >> output, (' '*indent) + '-'*len(header)
+ for receiver in sorted(receivers, key=sort_name):
+ print >> output, (' '*indent) + ' ' + nice_repr(receiver)
+
+def deref(value):
+ if isinstance(value, dispatcher.WEAKREF_TYPES):
+ return value()
+ else:
+ return value
+
+def sorted_items(a_dict):
+ if isinstance(a_dict, dict):
+ a_dict = a_dict.items()
+ return sorted(a_dict, key=lambda t: sort_name(t[0]))
+
+def sort_name(value):
+ if isinstance(value, type):
+ return value.__name__
+ elif isinstance(value, types.FunctionType):
+ return value.func_name
+ else:
+ return str(value)
+
+_real_dispatcher_send = dispatcher.send
+_real_dispatcher_sendExact = dispatcher.sendExact
+_real_dispatcher_disconnect = dispatcher.disconnect
+_real_dispatcher_connect = dispatcher.connect
+_debug_enabled = False
+def debug_events():
+ global _debug_enabled, send
+ if _debug_enabled:
+ return
+ _debug_enabled = True
+ dispatcher.send = send = _debug_send
+ dispatcher.sendExact = _debug_sendExact
+ dispatcher.disconnect = _debug_disconnect
+ dispatcher.connect = _debug_connect
+
+def _debug_send(signal=dispatcher.Any, sender=dispatcher.Anonymous,
+ *arguments, **named):
+ print "send %s from %s: %s" % (
+ nice_repr(signal), nice_repr(sender), fmt_args(*arguments, **named))
+ return _real_dispatcher_send(signal, sender, *arguments, **named)
+
+def _debug_sendExact(signal=dispatcher.Any, sender=dispatcher.Anonymous,
+ *arguments, **named):
+ print "sendExact %s from %s: %s" % (
+ nice_repr(signal), nice_repr(sender), fmt_args(*arguments, **name))
+ return _real_dispatcher_sendExact(signal, sender, *arguments, **named)
+
+def _debug_connect(receiver, signal=dispatcher.Any, sender=dispatcher.Any,
+ weak=True):
+ print "connect %s to %s signal %s" % (
+ nice_repr(receiver), nice_repr(signal), nice_repr(sender))
+ return _real_dispatcher_connect(receiver, signal, sender, weak)
+
+def _debug_disconnect(receiver, signal=dispatcher.Any, sender=dispatcher.Any,
+ weak=True):
+ print "disconnecting %s from %s signal %s" % (
+ nice_repr(receiver), nice_repr(signal), nice_repr(sender))
+ return disconnect(receiver, signal, sender, weak)
+
+def fmt_args(*arguments, **name):
+ args = [repr(a) for a in arguments]
+ args.extend([
+ '%s=%r' % (n, v) for n, v in sorted(name.items())])
+ return ', '.join(args)
+
+def nice_repr(v):
+ """
+ Like repr(), but nicer for debugging here.
+ """
+ if isinstance(v, (types.ClassType, type)):
+ return v.__module__ + '.' + v.__name__
+ elif isinstance(v, types.FunctionType):
+ if '__name__' in v.func_globals:
+ if getattr(sys.modules[v.func_globals['__name__']],
+ v.func_name, None) is v:
+ return '%s.%s' % (v.func_globals['__name__'], v.func_name)
+ return repr(v)
+ elif isinstance(v, types.MethodType):
+ return '%s.%s of %s' % (
+ nice_repr(v.im_class), v.im_func.func_name,
+ nice_repr(v.im_self))
+ else:
+ return repr(v)
+
__all__ = ['listen', 'send']
for name, value in globals().items():
if isinstance(value, type) and issubclass(value, Signal):
Modified: SQLObject/trunk/sqlobject/tests/test_events.py
===================================================================
--- SQLObject/trunk/sqlobject/tests/test_events.py 2005-11-03 22:18:32 UTC (rev 1219)
+++ SQLObject/trunk/sqlobject/tests/test_events.py 2005-11-04 05:19:49 UTC (rev 1220)
@@ -27,7 +27,7 @@
class EventTesterSub2(EventTesterSub1):
pass
assert len(watcher.log) == 2
- assert len(watcher.log[0]) == 4
+ assert len(watcher.log[0]) == 5
assert watcher.log[0][0] == 'EventTesterSub1'
assert watcher.log[0][1] == (EventTester,)
assert isinstance(watcher.log[0][2], dict)
@@ -60,13 +60,15 @@
(f, {'name': 'bar2'}),
(f, {'name': 'bar3'})]
-def disbaled_test_add_column():
+def test_add_column():
setupClass(EventTester)
- watcher = make_listen(events.RowUpdateSignal)
+ watcher = make_listen(events.AddColumnSignal)
+ events.summarize_events_by_sender()
class NewEventTester(EventTester):
name2 = StringCol()
- assert watcher.log == [
- (NewEventTester, NewEventTester._connection,
- 'name2', NewEventTester.sqlmeta.columns['name2'],
- True, [])]
-
+ expect = (
+ NewEventTester, None,
+ 'name2', NewEventTester.sqlmeta.columnDefinitions['name2'],
+ False, [])
+ print zip(watcher.log[1], expect)
+ assert watcher.log[1] == expect
|