Author: phd
Date: 2005-11-04 08:21:51 +0000 (Fri, 04 Nov 2005)
New Revision: 1225
Modified:
home/phd/SQLObject/paramstyles/setup.py
home/phd/SQLObject/paramstyles/sqlobject/conftest.py
home/phd/SQLObject/paramstyles/sqlobject/dbconnection.py
home/phd/SQLObject/paramstyles/sqlobject/declarative.py
home/phd/SQLObject/paramstyles/sqlobject/events.py
home/phd/SQLObject/paramstyles/sqlobject/main.py
home/phd/SQLObject/paramstyles/sqlobject/tests/test_events.py
home/phd/SQLObject/paramstyles/sqlobject/tests/test_index_get.py
Log:
Merged patches from the revisions 1208:1224 from the trunk
Modified: home/phd/SQLObject/paramstyles/setup.py
===================================================================
--- home/phd/SQLObject/paramstyles/setup.py 2005-11-04 08:19:22 UTC (rev 1224)
+++ home/phd/SQLObject/paramstyles/setup.py 2005-11-04 08:21:51 UTC (rev 1225)
@@ -7,8 +7,9 @@
except ImportError:
from distutils.core import setup
-subpackages = ['firebird', 'include', 'inheritance', 'mysql', 'postgres',
- 'sqlite', 'sybase', 'maxdb', 'util', 'manager', 'mssql']
+subpackages = ['firebird', 'include', 'include.pydispatch', 'inheritance',
+ 'manager', 'maxdb', 'mssql', 'mysql', 'postgres', 'sqlite',
+ 'sybase', 'util']
import sys
# patch distutils if it can't cope with the "classifiers" keyword
Modified: home/phd/SQLObject/paramstyles/sqlobject/conftest.py
===================================================================
--- home/phd/SQLObject/paramstyles/sqlobject/conftest.py 2005-11-04 08:19:22 UTC (rev 1224)
+++ home/phd/SQLObject/paramstyles/sqlobject/conftest.py 2005-11-04 08:21:51 UTC (rev 1225)
@@ -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: home/phd/SQLObject/paramstyles/sqlobject/dbconnection.py
===================================================================
--- home/phd/SQLObject/paramstyles/sqlobject/dbconnection.py 2005-11-04 08:19:22 UTC (rev 1224)
+++ home/phd/SQLObject/paramstyles/sqlobject/dbconnection.py 2005-11-04 08:21:51 UTC (rev 1225)
@@ -928,8 +928,10 @@
"""
This routine can be used to run a function in a transaction,
rolling the transaction back if any exception is raised from
- that function, and committing otherwise. Use like::
+ that function, and committing otherwise.
+ Use like::
+
sqlhub.doInTransaction(process_request, os.environ)
This will run ``process_request(os.environ)``. The return
Modified: home/phd/SQLObject/paramstyles/sqlobject/declarative.py
===================================================================
--- home/phd/SQLObject/paramstyles/sqlobject/declarative.py 2005-11-04 08:19:22 UTC (rev 1224)
+++ home/phd/SQLObject/paramstyles/sqlobject/declarative.py 2005-11-04 08:21:51 UTC (rev 1225)
@@ -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: home/phd/SQLObject/paramstyles/sqlobject/events.py
===================================================================
--- home/phd/SQLObject/paramstyles/sqlobject/events.py 2005-11-04 08:19:22 UTC (rev 1224)
+++ home/phd/SQLObject/paramstyles/sqlobject/events.py 2005-11-04 08:21:51 UTC (rev 1225)
@@ -1,3 +1,5 @@
+import sys
+import types
from sqlobject.include.pydispatch import dispatcher
from weakref import ref
@@ -4,7 +6,7 @@
subclassClones = {}
-def listen(receiver, soClass, signal, alsoSubclasses=True):
+def listen(receiver, soClass, signal, alsoSubclasses=True, weak=True):
"""
Listen for the given ``signal`` on the SQLObject subclass
``soClass``, calling ``receiver()`` when ``send(soClass, signal,
@@ -13,7 +15,7 @@
If ``alsoSubclasses`` is true, receiver will also be called when
an event is fired on any subclass.
"""
- dispatcher.connect(receiver, signal=signal, sender=soClass)
+ dispatcher.connect(receiver, signal=signal, sender=soClass, weak=weak)
weakReceiver = ref(receiver)
subclassClones.setdefault(soClass, []).append((weakReceiver, signal))
@@ -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,7 +156,124 @@
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):
- __all__.append('value')
+ __all__.append(name)
Modified: home/phd/SQLObject/paramstyles/sqlobject/main.py
===================================================================
--- home/phd/SQLObject/paramstyles/sqlobject/main.py 2005-11-04 08:19:22 UTC (rev 1224)
+++ home/phd/SQLObject/paramstyles/sqlobject/main.py 2005-11-04 08:21:51 UTC (rev 1225)
@@ -297,7 +297,6 @@
def addColumn(cls, columnDef, changeSchema=False, connection=None):
post_funcs = []
- print "Send on", cls.soClass, columnDef.name
cls.send(events.AddColumnSignal, cls.soClass, connection,
columnDef.name, columnDef, changeSchema, post_funcs)
sqlmeta = cls
Modified: home/phd/SQLObject/paramstyles/sqlobject/tests/test_events.py
===================================================================
--- home/phd/SQLObject/paramstyles/sqlobject/tests/test_events.py 2005-11-04 08:19:22 UTC (rev 1224)
+++ home/phd/SQLObject/paramstyles/sqlobject/tests/test_events.py 2005-11-04 08:21:51 UTC (rev 1225)
@@ -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
Modified: home/phd/SQLObject/paramstyles/sqlobject/tests/test_index_get.py
===================================================================
--- home/phd/SQLObject/paramstyles/sqlobject/tests/test_index_get.py 2005-11-04 08:19:22 UTC (rev 1224)
+++ home/phd/SQLObject/paramstyles/sqlobject/tests/test_index_get.py 2005-11-04 08:21:51 UTC (rev 1225)
@@ -1,46 +1,46 @@
from sqlobject import *
from sqlobject.tests.dbtest import *
-class Person(SQLObject):
+class PersonIndexGet(SQLObject):
firstName = StringCol()
lastName = StringCol()
age = IntCol(alternateID=True)
nameIndex = DatabaseIndex(firstName, lastName, unique=True)
def test_1():
- setupClass(Person)
+ setupClass(PersonIndexGet)
- Person(firstName='Eric', lastName='Idle', age=62)
- Person(firstName='Terry', lastName='Gilliam', age=65)
- Person(firstName='John', lastName='Cleese', age=66)
+ PersonIndexGet(firstName='Eric', lastName='Idle', age=62)
+ PersonIndexGet(firstName='Terry', lastName='Gilliam', age=65)
+ PersonIndexGet(firstName='John', lastName='Cleese', age=66)
- Person.get(1)
- Person.nameIndex.get('Terry', 'Gilliam')
- Person.nameIndex.get(firstName='John', lastName='Cleese')
+ PersonIndexGet.get(1)
+ PersonIndexGet.nameIndex.get('Terry', 'Gilliam')
+ PersonIndexGet.nameIndex.get(firstName='John', lastName='Cleese')
try:
- print Person.nameIndex.get(firstName='Graham', lastName='Chapman')
+ print PersonIndexGet.nameIndex.get(firstName='Graham', lastName='Chapman')
except Exception, e:
pass
else:
raise AssertError
try:
- print Person.nameIndex.get('Terry', lastName='Gilliam')
+ print PersonIndexGet.nameIndex.get('Terry', lastName='Gilliam')
except Exception, e:
pass
else:
raise AssertError
try:
- print Person.nameIndex.get('Terry', 'Gilliam', 65)
+ print PersonIndexGet.nameIndex.get('Terry', 'Gilliam', 65)
except Exception, e:
pass
else:
raise AssertError
try:
- print Person.nameIndex.get('Terry')
+ print PersonIndexGet.nameIndex.get('Terry')
except Exception, e:
pass
else:
|