sqlobject 3.13.1__py2.py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- sqlobject/__init__.py +15 -0
- sqlobject/__version__.py +8 -0
- sqlobject/boundattributes.py +124 -0
- sqlobject/cache.py +376 -0
- sqlobject/classregistry.py +138 -0
- sqlobject/col.py +2187 -0
- sqlobject/compat.py +59 -0
- sqlobject/conftest.py +64 -0
- sqlobject/constraints.py +77 -0
- sqlobject/converters.py +278 -0
- sqlobject/dbconnection.py +1137 -0
- sqlobject/dberrors.py +54 -0
- sqlobject/declarative.py +211 -0
- sqlobject/events.py +365 -0
- sqlobject/firebird/__init__.py +8 -0
- sqlobject/firebird/firebirdconnection.py +463 -0
- sqlobject/include/__init__.py +1 -0
- sqlobject/include/hashcol.py +109 -0
- sqlobject/include/tests/__init__.py +1 -0
- sqlobject/include/tests/test_hashcol.py +84 -0
- sqlobject/index.py +189 -0
- sqlobject/inheritance/__init__.py +551 -0
- sqlobject/inheritance/iteration.py +106 -0
- sqlobject/inheritance/tests/__init__.py +1 -0
- sqlobject/inheritance/tests/test_aggregates.py +23 -0
- sqlobject/inheritance/tests/test_asdict.py +48 -0
- sqlobject/inheritance/tests/test_deep_inheritance.py +114 -0
- sqlobject/inheritance/tests/test_destroy_cascade.py +23 -0
- sqlobject/inheritance/tests/test_foreignKey.py +94 -0
- sqlobject/inheritance/tests/test_indexes.py +42 -0
- sqlobject/inheritance/tests/test_inheritance.py +164 -0
- sqlobject/inheritance/tests/test_inheritance_tree.py +50 -0
- sqlobject/joins.py +656 -0
- sqlobject/main.py +1880 -0
- sqlobject/manager/__init__.py +1 -0
- sqlobject/manager/command.py +1309 -0
- sqlobject/maxdb/__init__.py +8 -0
- sqlobject/maxdb/maxdbconnection.py +320 -0
- sqlobject/maxdb/readme.txt +27 -0
- sqlobject/mssql/__init__.py +8 -0
- sqlobject/mssql/mssqlconnection.py +417 -0
- sqlobject/mysql/__init__.py +8 -0
- sqlobject/mysql/mysqlconnection.py +562 -0
- sqlobject/postgres/__init__.py +8 -0
- sqlobject/postgres/pgconnection.py +619 -0
- sqlobject/sqlbuilder.py +1655 -0
- sqlobject/sqlite/__init__.py +8 -0
- sqlobject/sqlite/sqliteconnection.py +418 -0
- sqlobject/sresults.py +377 -0
- sqlobject/styles.py +174 -0
- sqlobject/sybase/__init__.py +8 -0
- sqlobject/sybase/sybaseconnection.py +183 -0
- sqlobject/tests/__init__.py +1 -0
- sqlobject/tests/dbtest.py +363 -0
- sqlobject/tests/test_ForeignKey.py +133 -0
- sqlobject/tests/test_ForeignKey_cascade.py +150 -0
- sqlobject/tests/test_NoneValuedResultItem.py +33 -0
- sqlobject/tests/test_SQLMultipleJoin.py +65 -0
- sqlobject/tests/test_SQLRelatedJoin.py +114 -0
- sqlobject/tests/test_SingleJoin.py +30 -0
- sqlobject/tests/test_aggregates.py +75 -0
- sqlobject/tests/test_aliases.py +49 -0
- sqlobject/tests/test_asdict.py +18 -0
- sqlobject/tests/test_auto.py +218 -0
- sqlobject/tests/test_basic.py +359 -0
- sqlobject/tests/test_blob.py +34 -0
- sqlobject/tests/test_boundattributes.py +67 -0
- sqlobject/tests/test_cache.py +57 -0
- sqlobject/tests/test_class_hash.py +22 -0
- sqlobject/tests/test_columns_order.py +18 -0
- sqlobject/tests/test_combining_joins.py +47 -0
- sqlobject/tests/test_comparison.py +24 -0
- sqlobject/tests/test_compat.py +10 -0
- sqlobject/tests/test_complex_sorting.py +77 -0
- sqlobject/tests/test_constraints.py +43 -0
- sqlobject/tests/test_converters.py +276 -0
- sqlobject/tests/test_create_drop.py +29 -0
- sqlobject/tests/test_csvexport.py +101 -0
- sqlobject/tests/test_csvimport.py +21 -0
- sqlobject/tests/test_cyclic_reference.py +55 -0
- sqlobject/tests/test_datetime.py +171 -0
- sqlobject/tests/test_dbconnection.py +17 -0
- sqlobject/tests/test_decimal.py +63 -0
- sqlobject/tests/test_declarative.py +83 -0
- sqlobject/tests/test_default_style.py +65 -0
- sqlobject/tests/test_delete.py +74 -0
- sqlobject/tests/test_distinct.py +36 -0
- sqlobject/tests/test_empty.py +22 -0
- sqlobject/tests/test_enum.py +75 -0
- sqlobject/tests/test_events.py +142 -0
- sqlobject/tests/test_exceptions.py +40 -0
- sqlobject/tests/test_expire.py +29 -0
- sqlobject/tests/test_groupBy.py +45 -0
- sqlobject/tests/test_identity.py +29 -0
- sqlobject/tests/test_indexes.py +100 -0
- sqlobject/tests/test_inheritance.py +35 -0
- sqlobject/tests/test_joins.py +160 -0
- sqlobject/tests/test_joins_conditional.py +143 -0
- sqlobject/tests/test_jsonbcol.py +53 -0
- sqlobject/tests/test_jsoncol.py +56 -0
- sqlobject/tests/test_lazy.py +165 -0
- sqlobject/tests/test_md5.py +13 -0
- sqlobject/tests/test_mysql.py +50 -0
- sqlobject/tests/test_new_joins.py +165 -0
- sqlobject/tests/test_parse_uri.py +147 -0
- sqlobject/tests/test_paste.py +92 -0
- sqlobject/tests/test_perConnection.py +20 -0
- sqlobject/tests/test_pickle.py +42 -0
- sqlobject/tests/test_picklecol.py +44 -0
- sqlobject/tests/test_postgres.py +82 -0
- sqlobject/tests/test_reparent_sqlmeta.py +33 -0
- sqlobject/tests/test_schema.py +30 -0
- sqlobject/tests/test_select.py +218 -0
- sqlobject/tests/test_select_through.py +63 -0
- sqlobject/tests/test_setters.py +23 -0
- sqlobject/tests/test_slice.py +59 -0
- sqlobject/tests/test_sorting.py +80 -0
- sqlobject/tests/test_sqlbuilder.py +134 -0
- sqlobject/tests/test_sqlbuilder_dbspecific.py +66 -0
- sqlobject/tests/test_sqlbuilder_importproxy.py +55 -0
- sqlobject/tests/test_sqlbuilder_joins_instances.py +90 -0
- sqlobject/tests/test_sqlite.py +160 -0
- sqlobject/tests/test_sqlmeta_idName.py +52 -0
- sqlobject/tests/test_sqlobject_admin.py +38 -0
- sqlobject/tests/test_string_id.py +79 -0
- sqlobject/tests/test_style.py +36 -0
- sqlobject/tests/test_subqueries.py +142 -0
- sqlobject/tests/test_transactions.py +122 -0
- sqlobject/tests/test_unicode.py +84 -0
- sqlobject/tests/test_uuidcol.py +40 -0
- sqlobject/tests/test_validation.py +125 -0
- sqlobject/tests/test_views.py +159 -0
- sqlobject/util/__init__.py +1 -0
- sqlobject/util/csvexport.py +210 -0
- sqlobject/util/csvimport.py +366 -0
- sqlobject/util/moduleloader.py +35 -0
- sqlobject/util/threadinglocal.py +6 -0
- sqlobject/versioning/__init__.py +120 -0
- sqlobject/versioning/test/__init__.py +0 -0
- sqlobject/versioning/test/test_version.py +183 -0
- sqlobject/views.py +151 -0
- sqlobject/wsgi_middleware.py +109 -0
- sqlobject-3.13.1.data/scripts/sqlobject-admin +36 -0
- sqlobject-3.13.1.data/scripts/sqlobject-convertOldURI +19 -0
- sqlobject-3.13.1.dist-info/METADATA +177 -0
- sqlobject-3.13.1.dist-info/RECORD +149 -0
- sqlobject-3.13.1.dist-info/WHEEL +6 -0
- sqlobject-3.13.1.dist-info/entry_points.txt +4 -0
- sqlobject-3.13.1.dist-info/top_level.txt +1 -0
sqlobject/dberrors.py
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"""dberrors: database exception classes for SQLObject.
|
|
2
|
+
|
|
3
|
+
These classes are dictated by the DB API v2.0, see:
|
|
4
|
+
https://wiki.python.org/moin/DatabaseProgramming
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from sqlobject.compat import PY2
|
|
8
|
+
|
|
9
|
+
if not PY2:
|
|
10
|
+
StandardError = Exception
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class Error(StandardError):
|
|
14
|
+
pass
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class Warning(StandardError):
|
|
18
|
+
pass
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class InterfaceError(Error):
|
|
22
|
+
pass
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class DatabaseError(Error):
|
|
26
|
+
pass
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class InternalError(DatabaseError):
|
|
30
|
+
pass
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class OperationalError(DatabaseError):
|
|
34
|
+
pass
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class ProgrammingError(DatabaseError):
|
|
38
|
+
pass
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class IntegrityError(DatabaseError):
|
|
42
|
+
pass
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class DataError(DatabaseError):
|
|
46
|
+
pass
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class NotSupportedError(DatabaseError):
|
|
50
|
+
pass
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class DuplicateEntryError(IntegrityError):
|
|
54
|
+
pass
|
sqlobject/declarative.py
ADDED
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Declarative objects.
|
|
3
|
+
|
|
4
|
+
Declarative objects have a simple protocol: you can use classes in
|
|
5
|
+
lieu of instances and they are equivalent, and any keyword arguments
|
|
6
|
+
you give to the constructor will override those instance variables.
|
|
7
|
+
(So if a class is received, we'll simply instantiate an instance with
|
|
8
|
+
no arguments).
|
|
9
|
+
|
|
10
|
+
You can provide a variable __unpackargs__ (a list of strings), and if
|
|
11
|
+
the constructor is called with non-keyword arguments they will be
|
|
12
|
+
interpreted as the given keyword arguments.
|
|
13
|
+
|
|
14
|
+
If __unpackargs__ is ('*', name), then all the arguments will be put
|
|
15
|
+
in a variable by that name.
|
|
16
|
+
|
|
17
|
+
You can define a __classinit__(cls, new_attrs) method, which will be
|
|
18
|
+
called when the class is created (including subclasses). Note: you
|
|
19
|
+
can't use super() in __classinit__ because the class isn't bound to a
|
|
20
|
+
name. As an analog to __classinit__, Declarative adds
|
|
21
|
+
__instanceinit__ which is called with the same argument (new_attrs).
|
|
22
|
+
This is like __init__, but after __unpackargs__ and other factors have
|
|
23
|
+
been taken into account.
|
|
24
|
+
|
|
25
|
+
If __mutableattributes__ is defined as a sequence of strings, these
|
|
26
|
+
attributes will not be shared between superclasses and their
|
|
27
|
+
subclasses. E.g., if you have a class variable that contains a list
|
|
28
|
+
and you append to that list, changes to subclasses will effect
|
|
29
|
+
superclasses unless you add the attribute here.
|
|
30
|
+
|
|
31
|
+
Also defines classinstancemethod, which acts as either a class method
|
|
32
|
+
or an instance method depending on where it is called.
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
import copy
|
|
36
|
+
from . import events
|
|
37
|
+
from sqlobject.compat import with_metaclass
|
|
38
|
+
|
|
39
|
+
import itertools
|
|
40
|
+
counter = itertools.count()
|
|
41
|
+
|
|
42
|
+
__all__ = ('classinstancemethod', 'DeclarativeMeta', 'Declarative')
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class classinstancemethod(object):
|
|
46
|
+
"""
|
|
47
|
+
Acts like a class method when called from a class, like an
|
|
48
|
+
instance method when called by an instance. The method should
|
|
49
|
+
take two arguments, 'self' and 'cls'; one of these will be None
|
|
50
|
+
depending on how the method was called.
|
|
51
|
+
"""
|
|
52
|
+
|
|
53
|
+
def __init__(self, func):
|
|
54
|
+
self.func = func
|
|
55
|
+
|
|
56
|
+
def __get__(self, obj, type=None):
|
|
57
|
+
return _methodwrapper(self.func, obj=obj, type=type)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class _methodwrapper(object):
|
|
61
|
+
|
|
62
|
+
def __init__(self, func, obj, type):
|
|
63
|
+
self.func = func
|
|
64
|
+
self.obj = obj
|
|
65
|
+
self.type = type
|
|
66
|
+
|
|
67
|
+
def __call__(self, *args, **kw):
|
|
68
|
+
assert 'self' not in kw and 'cls' not in kw, (
|
|
69
|
+
"You cannot use 'self' or 'cls' arguments to a "
|
|
70
|
+
"classinstancemethod")
|
|
71
|
+
return self.func(*((self.obj, self.type) + args), **kw)
|
|
72
|
+
|
|
73
|
+
def __repr__(self):
|
|
74
|
+
if self.obj is None:
|
|
75
|
+
return ('<bound class method %s.%s>'
|
|
76
|
+
% (self.type.__name__, self.func.__name__))
|
|
77
|
+
else:
|
|
78
|
+
return ('<bound method %s.%s of %r>'
|
|
79
|
+
% (self.type.__name__, self.func.__name__, self.obj))
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
class DeclarativeMeta(type):
|
|
83
|
+
|
|
84
|
+
def __new__(meta, class_name, bases, new_attrs):
|
|
85
|
+
post_funcs = []
|
|
86
|
+
early_funcs = []
|
|
87
|
+
events.send(events.ClassCreateSignal,
|
|
88
|
+
bases[0], class_name, bases, new_attrs,
|
|
89
|
+
post_funcs, early_funcs)
|
|
90
|
+
cls = type.__new__(meta, class_name, bases, new_attrs)
|
|
91
|
+
for func in early_funcs:
|
|
92
|
+
func(cls)
|
|
93
|
+
if '__classinit__' in new_attrs:
|
|
94
|
+
if hasattr(cls.__classinit__, '__func__'):
|
|
95
|
+
cls.__classinit__ = staticmethod(cls.__classinit__.__func__)
|
|
96
|
+
else:
|
|
97
|
+
cls.__classinit__ = staticmethod(cls.__classinit__)
|
|
98
|
+
cls.__classinit__(cls, new_attrs)
|
|
99
|
+
for func in post_funcs:
|
|
100
|
+
func(cls)
|
|
101
|
+
return cls
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
class Declarative(with_metaclass(DeclarativeMeta, object)):
|
|
105
|
+
|
|
106
|
+
__unpackargs__ = ()
|
|
107
|
+
|
|
108
|
+
__mutableattributes__ = ()
|
|
109
|
+
|
|
110
|
+
__restrict_attributes__ = None
|
|
111
|
+
|
|
112
|
+
def __classinit__(cls, new_attrs):
|
|
113
|
+
cls.declarative_count = next(counter)
|
|
114
|
+
for name in cls.__mutableattributes__:
|
|
115
|
+
if name not in new_attrs:
|
|
116
|
+
setattr(cls, copy.copy(getattr(cls, name)))
|
|
117
|
+
|
|
118
|
+
def __instanceinit__(self, new_attrs):
|
|
119
|
+
if self.__restrict_attributes__ is not None:
|
|
120
|
+
for name in new_attrs:
|
|
121
|
+
if name not in self.__restrict_attributes__:
|
|
122
|
+
raise TypeError(
|
|
123
|
+
'%s() got an unexpected keyword argument %r'
|
|
124
|
+
% (self.__class__.__name__, name))
|
|
125
|
+
for name, value in new_attrs.items():
|
|
126
|
+
setattr(self, name, value)
|
|
127
|
+
if 'declarative_count' not in new_attrs:
|
|
128
|
+
self.declarative_count = next(counter)
|
|
129
|
+
|
|
130
|
+
def __init__(self, *args, **kw):
|
|
131
|
+
if self.__unpackargs__ and self.__unpackargs__[0] == '*':
|
|
132
|
+
assert len(self.__unpackargs__) == 2, \
|
|
133
|
+
"When using __unpackargs__ = ('*', varname), " \
|
|
134
|
+
"you must only provide a single variable name " \
|
|
135
|
+
"(you gave %r)" % self.__unpackargs__
|
|
136
|
+
name = self.__unpackargs__[1]
|
|
137
|
+
if name in kw:
|
|
138
|
+
raise TypeError(
|
|
139
|
+
"keyword parameter '%s' was given by position and name"
|
|
140
|
+
% name)
|
|
141
|
+
kw[name] = args
|
|
142
|
+
else:
|
|
143
|
+
if len(args) > len(self.__unpackargs__):
|
|
144
|
+
raise TypeError(
|
|
145
|
+
'%s() takes at most %i arguments (%i given)'
|
|
146
|
+
% (self.__class__.__name__,
|
|
147
|
+
len(self.__unpackargs__),
|
|
148
|
+
len(args)))
|
|
149
|
+
for name, arg in zip(self.__unpackargs__, args):
|
|
150
|
+
if name in kw:
|
|
151
|
+
raise TypeError(
|
|
152
|
+
"keyword parameter '%s' was given by position and name"
|
|
153
|
+
% name)
|
|
154
|
+
kw[name] = arg
|
|
155
|
+
if '__alsocopy' in kw:
|
|
156
|
+
for name, value in kw['__alsocopy'].items():
|
|
157
|
+
if name not in kw:
|
|
158
|
+
if name in self.__mutableattributes__:
|
|
159
|
+
value = copy.copy(value)
|
|
160
|
+
kw[name] = value
|
|
161
|
+
del kw['__alsocopy']
|
|
162
|
+
self.__instanceinit__(kw)
|
|
163
|
+
|
|
164
|
+
def __call__(self, *args, **kw):
|
|
165
|
+
kw['__alsocopy'] = self.__dict__
|
|
166
|
+
return self.__class__(*args, **kw)
|
|
167
|
+
|
|
168
|
+
@classinstancemethod
|
|
169
|
+
def singleton(self, cls):
|
|
170
|
+
if self:
|
|
171
|
+
return self
|
|
172
|
+
name = '_%s__singleton' % cls.__name__
|
|
173
|
+
if not hasattr(cls, name):
|
|
174
|
+
setattr(cls, name, cls(declarative_count=cls.declarative_count))
|
|
175
|
+
return getattr(cls, name)
|
|
176
|
+
|
|
177
|
+
@classinstancemethod
|
|
178
|
+
def __repr__(self, cls):
|
|
179
|
+
if self:
|
|
180
|
+
name = '%s object' % self.__class__.__name__
|
|
181
|
+
v = self.__dict__.copy()
|
|
182
|
+
else:
|
|
183
|
+
name = '%s class' % cls.__name__
|
|
184
|
+
v = cls.__dict__.copy()
|
|
185
|
+
if 'declarative_count' in v:
|
|
186
|
+
name = '%s %i' % (name, v['declarative_count'])
|
|
187
|
+
del v['declarative_count']
|
|
188
|
+
# @@: simplifying repr:
|
|
189
|
+
# v = {}
|
|
190
|
+
names = v.keys()
|
|
191
|
+
args = []
|
|
192
|
+
for n in self._repr_vars(names):
|
|
193
|
+
args.append('%s=%r' % (n, v[n]))
|
|
194
|
+
if not args:
|
|
195
|
+
return '<%s>' % name
|
|
196
|
+
else:
|
|
197
|
+
return '<%s %s>' % (name, ' '.join(args))
|
|
198
|
+
|
|
199
|
+
@staticmethod
|
|
200
|
+
def _repr_vars(dictNames):
|
|
201
|
+
names = [n for n in dictNames
|
|
202
|
+
if not n.startswith('_')
|
|
203
|
+
and n != 'declarative_count']
|
|
204
|
+
names.sort()
|
|
205
|
+
return names
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
def setup_attributes(cls, new_attrs):
|
|
209
|
+
for name, value in new_attrs.items():
|
|
210
|
+
if hasattr(value, '__addtoclass__'):
|
|
211
|
+
value.__addtoclass__(cls, name)
|
sqlobject/events.py
ADDED
|
@@ -0,0 +1,365 @@
|
|
|
1
|
+
from __future__ import print_function
|
|
2
|
+
import sys
|
|
3
|
+
import types
|
|
4
|
+
from pydispatch import dispatcher
|
|
5
|
+
from weakref import ref
|
|
6
|
+
from .compat import class_types
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
subclassClones = {}
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def listen(receiver, soClass, signal, alsoSubclasses=True, weak=True):
|
|
13
|
+
"""
|
|
14
|
+
Listen for the given ``signal`` on the SQLObject subclass
|
|
15
|
+
``soClass``, calling ``receiver()`` when ``send(soClass, signal,
|
|
16
|
+
...)`` is called.
|
|
17
|
+
|
|
18
|
+
If ``alsoSubclasses`` is true, receiver will also be called when
|
|
19
|
+
an event is fired on any subclass.
|
|
20
|
+
"""
|
|
21
|
+
dispatcher.connect(receiver, signal=signal, sender=soClass, weak=weak)
|
|
22
|
+
weakReceiver = ref(receiver)
|
|
23
|
+
subclassClones.setdefault(soClass, []).append((weakReceiver, signal))
|
|
24
|
+
|
|
25
|
+
# We export this function:
|
|
26
|
+
send = dispatcher.send
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class Signal(object):
|
|
30
|
+
"""
|
|
31
|
+
Base event for all SQLObject events.
|
|
32
|
+
|
|
33
|
+
In general the sender for these methods is the class, not the
|
|
34
|
+
instance.
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class ClassCreateSignal(Signal):
|
|
39
|
+
"""
|
|
40
|
+
Signal raised after class creation. The sender is the superclass
|
|
41
|
+
(in case of multiple superclasses, the first superclass). The
|
|
42
|
+
arguments are ``(new_class_name, bases, new_attrs, post_funcs,
|
|
43
|
+
early_funcs)``. ``new_attrs`` is a dictionary and may be modified
|
|
44
|
+
(but ``new_class_name`` and ``bases`` are immutable).
|
|
45
|
+
``post_funcs`` is an initially-empty list that can have callbacks
|
|
46
|
+
appended to it.
|
|
47
|
+
|
|
48
|
+
Note: at the time this event is called, the new class has not yet
|
|
49
|
+
been created. The functions in ``post_funcs`` will be called
|
|
50
|
+
after the class is created, with the single arguments of
|
|
51
|
+
``(new_class)``. Also, ``early_funcs`` will be called at the
|
|
52
|
+
soonest possible time after class creation (``post_funcs`` is
|
|
53
|
+
called after the class's ``__classinit__``).
|
|
54
|
+
"""
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def _makeSubclassConnections(new_class_name, bases, new_attrs,
|
|
58
|
+
post_funcs, early_funcs):
|
|
59
|
+
early_funcs.insert(0, _makeSubclassConnectionsPost)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def _makeSubclassConnectionsPost(new_class):
|
|
63
|
+
for cls in new_class.__bases__:
|
|
64
|
+
for weakReceiver, signal in subclassClones.get(cls, []):
|
|
65
|
+
receiver = weakReceiver()
|
|
66
|
+
if not receiver:
|
|
67
|
+
continue
|
|
68
|
+
listen(receiver, new_class, signal)
|
|
69
|
+
|
|
70
|
+
dispatcher.connect(_makeSubclassConnections, signal=ClassCreateSignal)
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
# @@: Should there be a class reload event? This would allow modules
|
|
74
|
+
# to be reloaded, possibly. Or it could even be folded into
|
|
75
|
+
# ClassCreateSignal, since anything that listens to that needs to pay
|
|
76
|
+
# attention to reloads (or else it is probably buggy).
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
class RowCreateSignal(Signal):
|
|
80
|
+
"""
|
|
81
|
+
Called before an instance is created, with the class as the
|
|
82
|
+
sender. Called with the arguments ``(instance, kwargs, post_funcs)``.
|
|
83
|
+
There may be a ``connection`` argument. ``kwargs``may be usefully
|
|
84
|
+
modified. ``post_funcs`` is a list of callbacks, intended to have
|
|
85
|
+
functions appended to it, and are called with the arguments
|
|
86
|
+
``(new_instance)``.
|
|
87
|
+
|
|
88
|
+
Note: this is not called when an instance is created from an
|
|
89
|
+
existing database row.
|
|
90
|
+
"""
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
class RowCreatedSignal(Signal):
|
|
94
|
+
"""
|
|
95
|
+
Called after an instance is created, with the class as the
|
|
96
|
+
sender. Called with the arguments ``(instance, kwargs, post_funcs)``.
|
|
97
|
+
There may be a ``connection`` argument. ``kwargs``may be usefully
|
|
98
|
+
modified. ``post_funcs`` is a list of callbacks, intended to have
|
|
99
|
+
functions appended to it, and are called with the arguments
|
|
100
|
+
``(new_instance)``.
|
|
101
|
+
|
|
102
|
+
Note: this is not called when an instance is created from an
|
|
103
|
+
existing database row.
|
|
104
|
+
"""
|
|
105
|
+
# @@: An event for getting a row? But for each row, when doing a
|
|
106
|
+
# select? For .sync, .syncUpdate, .expire?
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
class RowDestroySignal(Signal):
|
|
110
|
+
"""
|
|
111
|
+
Called before an instance is deleted. Sender is the instance's
|
|
112
|
+
class. Arguments are ``(instance, post_funcs)``.
|
|
113
|
+
|
|
114
|
+
``post_funcs`` is a list of callbacks, intended to have
|
|
115
|
+
functions appended to it, and are called with arguments ``(instance)``.
|
|
116
|
+
If any of the post_funcs raises an exception, the deletion is only
|
|
117
|
+
affected if this will prevent a commit.
|
|
118
|
+
|
|
119
|
+
You cannot cancel the delete, but you can raise an exception (which will
|
|
120
|
+
probably cancel the delete, but also cause an uncaught exception if not
|
|
121
|
+
expected).
|
|
122
|
+
|
|
123
|
+
Note: this is not called when an instance is destroyed through
|
|
124
|
+
garbage collection.
|
|
125
|
+
|
|
126
|
+
@@: Should this allow ``instance`` to be a primary key, so that a
|
|
127
|
+
row can be deleted without first fetching it?
|
|
128
|
+
"""
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
class RowDestroyedSignal(Signal):
|
|
132
|
+
"""
|
|
133
|
+
Called after an instance is deleted. Sender is the instance's
|
|
134
|
+
class. Arguments are ``(instance)``.
|
|
135
|
+
|
|
136
|
+
This is called before the post_funcs of RowDestroySignal
|
|
137
|
+
|
|
138
|
+
Note: this is not called when an instance is destroyed through
|
|
139
|
+
garbage collection.
|
|
140
|
+
"""
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
class RowUpdateSignal(Signal):
|
|
144
|
+
"""
|
|
145
|
+
Called when an instance is updated through a call to ``.set()``
|
|
146
|
+
(or a column attribute assignment). The arguments are
|
|
147
|
+
``(instance, kwargs)``. ``kwargs`` can be modified. This is run
|
|
148
|
+
*before* the instance is updated; if you want to look at the
|
|
149
|
+
current values, simply look at ``instance``.
|
|
150
|
+
"""
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
class RowUpdatedSignal(Signal):
|
|
154
|
+
"""
|
|
155
|
+
Called when an instance is updated through a call to ``.set()``
|
|
156
|
+
(or a column attribute assignment). The arguments are
|
|
157
|
+
``(instance, post_funcs)``. ``post_funcs`` is a list of callbacks,
|
|
158
|
+
intended to have functions appended to it, and are called with the
|
|
159
|
+
arguments ``(new_instance)``. This is run *after* the instance is
|
|
160
|
+
updated; Works better with lazyUpdate = True.
|
|
161
|
+
"""
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
class AddColumnSignal(Signal):
|
|
165
|
+
"""
|
|
166
|
+
Called when a column is added to a class, with arguments ``(cls,
|
|
167
|
+
connection, column_name, column_definition, changeSchema,
|
|
168
|
+
post_funcs)``. This is called *after* the column has been added,
|
|
169
|
+
and is called for each column after class creation.
|
|
170
|
+
|
|
171
|
+
post_funcs are called with ``(cls, so_column_obj)``
|
|
172
|
+
"""
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
class DeleteColumnSignal(Signal):
|
|
176
|
+
"""
|
|
177
|
+
Called when a column is removed from a class, with the arguments
|
|
178
|
+
``(cls, connection, column_name, so_column_obj, post_funcs)``.
|
|
179
|
+
Like ``AddColumnSignal`` this is called after the action has been
|
|
180
|
+
performed, and is called for subclassing (when a column is
|
|
181
|
+
implicitly removed by setting it to ``None``).
|
|
182
|
+
|
|
183
|
+
post_funcs are called with ``(cls, so_column_obj)``
|
|
184
|
+
"""
|
|
185
|
+
|
|
186
|
+
# @@: Signals for indexes and joins? These are mostly event consumers,
|
|
187
|
+
# though.
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
class CreateTableSignal(Signal):
|
|
191
|
+
"""
|
|
192
|
+
Called when a table is created. If ``ifNotExists==True`` and the
|
|
193
|
+
table exists, this event is not called.
|
|
194
|
+
|
|
195
|
+
Called with ``(cls, connection, extra_sql, post_funcs)``.
|
|
196
|
+
``extra_sql`` is a list (which can be appended to) of extra SQL
|
|
197
|
+
statements to be run after the table is created. ``post_funcs``
|
|
198
|
+
functions are called with ``(cls, connection)`` after the table
|
|
199
|
+
has been created. Those functions are *not* called simply when
|
|
200
|
+
constructing the SQL.
|
|
201
|
+
"""
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
class DropTableSignal(Signal):
|
|
205
|
+
"""
|
|
206
|
+
Called when a table is dropped. If ``ifExists==True`` and the
|
|
207
|
+
table doesn't exist, this event is not called.
|
|
208
|
+
|
|
209
|
+
Called with ``(cls, connection, extra_sql, post_funcs)``.
|
|
210
|
+
``post_funcs`` functions are called with ``(cls, connection)``
|
|
211
|
+
after the table has been dropped.
|
|
212
|
+
"""
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
class CommitSignal(Signal):
|
|
216
|
+
"""
|
|
217
|
+
Called on transaction commit
|
|
218
|
+
"""
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
class RollbackSignal(Signal):
|
|
222
|
+
"""
|
|
223
|
+
Called on transaction rollback
|
|
224
|
+
"""
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
############################################################
|
|
228
|
+
# Event Debugging
|
|
229
|
+
############################################################
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
def summarize_events_by_sender(sender=None, output=None, indent=0):
|
|
233
|
+
"""
|
|
234
|
+
Prints out a summary of the senders and listeners in the system,
|
|
235
|
+
for debugging purposes.
|
|
236
|
+
"""
|
|
237
|
+
if output is None:
|
|
238
|
+
output = sys.stdout
|
|
239
|
+
leader = ' ' * indent
|
|
240
|
+
if sender is None:
|
|
241
|
+
send_list = [
|
|
242
|
+
(deref(dispatcher.senders.get(sid)), listeners)
|
|
243
|
+
for sid, listeners in dispatcher.connections.items()
|
|
244
|
+
if deref(dispatcher.senders.get(sid))]
|
|
245
|
+
for sender, listeners in sorted_items(send_list):
|
|
246
|
+
real_sender = deref(sender)
|
|
247
|
+
if not real_sender:
|
|
248
|
+
continue
|
|
249
|
+
header = 'Sender: %r' % real_sender
|
|
250
|
+
print(leader + header, file=output)
|
|
251
|
+
print(leader + ('=' * len(header)), file=output)
|
|
252
|
+
summarize_events_by_sender(real_sender, output=output,
|
|
253
|
+
indent=indent + 2)
|
|
254
|
+
else:
|
|
255
|
+
for signal, receivers in \
|
|
256
|
+
sorted_items(dispatcher.connections.get(id(sender), [])):
|
|
257
|
+
receivers = [deref(r) for r in receivers if deref(r)]
|
|
258
|
+
header = 'Signal: %s (%i receivers)' % (sort_name(signal),
|
|
259
|
+
len(receivers))
|
|
260
|
+
print(leader + header, file=output)
|
|
261
|
+
print(leader + ('-' * len(header)), file=output)
|
|
262
|
+
for receiver in sorted(receivers, key=sort_name):
|
|
263
|
+
print(leader + ' ' + nice_repr(receiver), file=output)
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
def deref(value):
|
|
267
|
+
if isinstance(value, dispatcher.WEAKREF_TYPES):
|
|
268
|
+
return value()
|
|
269
|
+
else:
|
|
270
|
+
return value
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
def sorted_items(a_dict):
|
|
274
|
+
if isinstance(a_dict, dict):
|
|
275
|
+
a_dict = a_dict.items()
|
|
276
|
+
return sorted(a_dict, key=lambda t: sort_name(t[0]))
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
def sort_name(value):
|
|
280
|
+
if isinstance(value, type):
|
|
281
|
+
return value.__name__
|
|
282
|
+
elif isinstance(value, types.FunctionType):
|
|
283
|
+
return value.__name__
|
|
284
|
+
else:
|
|
285
|
+
return str(value)
|
|
286
|
+
|
|
287
|
+
_real_dispatcher_send = dispatcher.send
|
|
288
|
+
_real_dispatcher_sendExact = dispatcher.sendExact
|
|
289
|
+
_real_dispatcher_connect = dispatcher.connect
|
|
290
|
+
_real_dispatcher_disconnect = dispatcher.disconnect
|
|
291
|
+
_debug_enabled = False
|
|
292
|
+
|
|
293
|
+
|
|
294
|
+
def debug_events():
|
|
295
|
+
global _debug_enabled, send
|
|
296
|
+
if _debug_enabled:
|
|
297
|
+
return
|
|
298
|
+
_debug_enabled = True
|
|
299
|
+
dispatcher.send = send = _debug_send
|
|
300
|
+
dispatcher.sendExact = _debug_sendExact
|
|
301
|
+
dispatcher.disconnect = _debug_disconnect
|
|
302
|
+
dispatcher.connect = _debug_connect
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
def _debug_send(signal=dispatcher.Any, sender=dispatcher.Anonymous,
|
|
306
|
+
*arguments, **named):
|
|
307
|
+
print("send %s from %s: %s" % (
|
|
308
|
+
nice_repr(signal), nice_repr(sender),
|
|
309
|
+
fmt_args(*arguments, **named)))
|
|
310
|
+
return _real_dispatcher_send(signal, sender, *arguments, **named)
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
def _debug_sendExact(signal=dispatcher.Any, sender=dispatcher.Anonymous,
|
|
314
|
+
*arguments, **named):
|
|
315
|
+
print("sendExact %s from %s: %s" % (
|
|
316
|
+
nice_repr(signal), nice_repr(sender), fmt_args(*arguments, **name)))
|
|
317
|
+
return _real_dispatcher_sendExact(signal, sender, *arguments, **named)
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
def _debug_connect(receiver, signal=dispatcher.Any, sender=dispatcher.Any,
|
|
321
|
+
weak=True):
|
|
322
|
+
print("connect %s to %s signal %s" % (
|
|
323
|
+
nice_repr(receiver), nice_repr(signal), nice_repr(sender)))
|
|
324
|
+
return _real_dispatcher_connect(receiver, signal, sender, weak)
|
|
325
|
+
|
|
326
|
+
|
|
327
|
+
def _debug_disconnect(receiver, signal=dispatcher.Any, sender=dispatcher.Any,
|
|
328
|
+
weak=True):
|
|
329
|
+
print("disconnecting %s from %s signal %s" % (
|
|
330
|
+
nice_repr(receiver), nice_repr(signal), nice_repr(sender)))
|
|
331
|
+
return _real_dispatcher_disconnect(receiver, signal, sender, weak)
|
|
332
|
+
|
|
333
|
+
|
|
334
|
+
def fmt_args(*arguments, **name):
|
|
335
|
+
args = [repr(a) for a in arguments]
|
|
336
|
+
args.extend([
|
|
337
|
+
'%s=%r' % (n, v) for n, v in sorted(name.items())])
|
|
338
|
+
return ', '.join(args)
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
def nice_repr(v):
|
|
342
|
+
"""
|
|
343
|
+
Like repr(), but nicer for debugging here.
|
|
344
|
+
"""
|
|
345
|
+
if isinstance(v, class_types):
|
|
346
|
+
return v.__module__ + '.' + v.__name__
|
|
347
|
+
elif isinstance(v, types.FunctionType):
|
|
348
|
+
if '__name__' in v.__globals__:
|
|
349
|
+
if getattr(sys.modules[v.__globals__['__name__']],
|
|
350
|
+
v.__name__, None) is v:
|
|
351
|
+
return '%s.%s' % (v.__globals__['__name__'], v.__name__)
|
|
352
|
+
return repr(v)
|
|
353
|
+
elif isinstance(v, types.MethodType):
|
|
354
|
+
return '%s.%s of %s' % (
|
|
355
|
+
nice_repr(v.__self__.__class__), v.__func__.__name__,
|
|
356
|
+
nice_repr(v.__self__))
|
|
357
|
+
else:
|
|
358
|
+
return repr(v)
|
|
359
|
+
|
|
360
|
+
|
|
361
|
+
__all__ = ['listen', 'send']
|
|
362
|
+
# Use copy() to avoid 'dictionary changed' issues on python 3
|
|
363
|
+
for name, value in globals().copy().items():
|
|
364
|
+
if isinstance(value, type) and issubclass(value, Signal):
|
|
365
|
+
__all__.append(name)
|