~jbaker/storm/nose_plugin

51 by Gustavo Niemeyer
- Added locals.py.
1
#
129 by Gustavo Niemeyer
Added LGPL version 2.1 in the LICENSE file, and a standard
2
# Copyright (c) 2006, 2007 Canonical
51 by Gustavo Niemeyer
- Added locals.py.
3
#
4
# Written by Gustavo Niemeyer <gustavo@niemeyer.net>
5
#
6
# This file is part of Storm Object Relational Mapper.
7
#
129 by Gustavo Niemeyer
Added LGPL version 2.1 in the LICENSE file, and a standard
8
# Storm is free software; you can redistribute it and/or modify
9
# it under the terms of the GNU Lesser General Public License as
10
# published by the Free Software Foundation; either version 2.1 of
11
# the License, or (at your option) any later version.
12
#
13
# Storm is distributed in the hope that it will be useful,
14
# but WITHOUT ANY WARRANTY; without even the implied warranty of
15
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
# GNU Lesser General Public License for more details.
17
#
18
# You should have received a copy of the GNU Lesser General Public License
19
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
51 by Gustavo Niemeyer
- Added locals.py.
20
#
142.3.20 by Christopher Armstrong
massive doc improvements for store.py
21
22
"""The Store interface to a database.
23
24
This module contains the highest-level ORM interface in Storm.
25
"""
26
27
140.1.1 by Johan Dahlin
Pyflakes
28
from weakref import WeakValueDictionary
10 by Gustavo Niemeyer
- Renamed ClassInfo.primary_key to pk_prop_insts and introduced
29
142.2.3 by Gustavo Niemeyer
- Finished implementation of the C version of ObjectInfo
30
from storm.info import get_cls_info, get_obj_info, set_obj_info
83.1.1 by Gustavo Niemeyer
- Implemented 'autoreload' feature!
31
from storm.variables import Variable, LazyValue
68 by Gustavo Niemeyer
Added meaningful implementations of ResultSet.{any,one,first,last}().
32
from storm.expr import (
140.1.1 by Johan Dahlin
Pyflakes
33
    Expr, Select, Insert, Update, Delete, Column, Count, Max, Min,
95.3.20 by Gustavo Niemeyer
Initial implementation of ResultSet.union(). order_by() doesn't yet
34
    Avg, Sum, Eq, And, Asc, Desc, compile_python, compare_columns, SQLRaw,
95.3.60 by Gustavo Niemeyer
Implemented support for ResultSet.difference()/intersection().
35
    Union, Except, Intersect, Alias)
59 by Gustavo Niemeyer
- Created exception hierarchy, and patched DBAPI2 modules to include
36
from storm.exceptions import (
68 by Gustavo Niemeyer
Added meaningful implementations of ResultSet.{any,one,first,last}().
37
    WrongStoreError, NotFlushedError, OrderLoopError, UnorderedError,
95.3.25 by Gustavo Niemeyer
- In Property, do not access obj.__class__, to avoid problems with
38
    NotOneError, FeatureError, CompileError, LostObjectError, ClassInfoError)
58 by Gustavo Niemeyer
- Major refactoring of the typing system. Kinds are gone. Now Variables
39
from storm import Undef
196.1.4 by Bernd Dorn
implementation of strong reference cache finished
40
from storm.cache import Cache
7 by Gustavo Niemeyer
- Started implementing Store.
41
196.1.6 by Gustavo Niemeyer
These changes address a few issues and perform a few stylistic changes
42
92.1.1 by Jamshed Kakar
- Implemented EmptyResultSet.
43
__all__ = ["Store", "AutoReload", "EmptyResultSet"]
12 by Gustavo Niemeyer
Included some additional checks on Store.add/remove().
44
196.1.6 by Gustavo Niemeyer
These changes address a few issues and perform a few stylistic changes
45
26 by Gustavo Niemeyer
Major cleanup in the store and improved caching behavior.
46
PENDING_ADD = 1
47
PENDING_REMOVE = 2
11 by Gustavo Niemeyer
Store.add() and remove() now only act on flush.
48
7 by Gustavo Niemeyer
- Started implementing Store.
49
50
class Store(object):
142.3.20 by Christopher Armstrong
massive doc improvements for store.py
51
    """The Storm Store.
52
53
    This is the highest-level interface to a database. It manages
54
    transactions with L{commit} and L{rollback}, caching, high-level
55
    querying with L{find}, and more.
56
57
    Note that Store objects are not threadsafe. You should create one
58
    Store per thread in your application, passing them the same
59
    backend L{Database<storm.store.Database>} object.
60
    """
7 by Gustavo Niemeyer
- Started implementing Store.
61
62 by Gustavo Niemeyer
Changed the way that objects are keyed. Now they use the obj_info,
62
    _result_set_factory = None
63
169.1.2 by Jamu Kakar
- Moved the store name retrieval functionality entirely into ZStorm,
64
    def __init__(self, database):
142.3.20 by Christopher Armstrong
massive doc improvements for store.py
65
        """
66
        @param database: The L{storm.database.Database} instance to use.
67
        """
7 by Gustavo Niemeyer
- Started implementing Store.
68
        self._connection = database.connect()
196.1.2 by Bernd Dorn
ups alive is the right name
69
        self._alive = WeakValueDictionary()
95.1.8 by Gustavo Niemeyer
- Event system now has owner as a weak reference, to prevent the
70
        self._dirty = {}
62 by Gustavo Niemeyer
Changed the way that objects are keyed. Now they use the obj_info,
71
        self._order = {} # (info, info) = count
196.1.4 by Bernd Dorn
implementation of strong reference cache finished
72
        self._cache = Cache(100)
7 by Gustavo Niemeyer
- Started implementing Store.
73
74
    @staticmethod
75
    def of(obj):
142.3.20 by Christopher Armstrong
massive doc improvements for store.py
76
        """Get the Store that the object is associated with.
77
78
        If the given object has not yet been associated with a store,
79
        return None.
80
        """
9 by Gustavo Niemeyer
- Merged Connection.execute_expr() into Connection.execute().
81
        try:
25 by Gustavo Niemeyer
- Moved ObjectInfo and ClassInfo to an implementation based on
82
            return get_obj_info(obj).get("store")
95.3.25 by Gustavo Niemeyer
- In Property, do not access obj.__class__, to avoid problems with
83
        except (AttributeError, ClassInfoError):
9 by Gustavo Niemeyer
- Merged Connection.execute_expr() into Connection.execute().
84
            return None
85
19 by Gustavo Niemeyer
- Replaced Store.get_connection() by Store.execute().
86
    def execute(self, statement, params=None, noresult=False):
142.3.20 by Christopher Armstrong
massive doc improvements for store.py
87
        """Execute a basic query.
88
89
        This is just like L{storm.database.Database.execute}, except
90
        that a flush is performed first.
91
        """
19 by Gustavo Niemeyer
- Replaced Store.get_connection() by Store.execute().
92
        self.flush()
93
        return self._connection.execute(statement, params, noresult)
9 by Gustavo Niemeyer
- Merged Connection.execute_expr() into Connection.execute().
94
59.1.3 by Jamshed Kakar
- Introduced Result.close() and Connection.close().
95
    def close(self):
142.3.20 by Christopher Armstrong
massive doc improvements for store.py
96
        """Close the connection."""
59.1.3 by Jamshed Kakar
- Introduced Result.close() and Connection.close().
97
        self._connection.close()
98
9 by Gustavo Niemeyer
- Merged Connection.execute_expr() into Connection.execute().
99
    def commit(self):
142.3.20 by Christopher Armstrong
massive doc improvements for store.py
100
        """Commit all changes to the database.
101
102
        This invalidates the cache, so all live objects will have data
103
        reloaded next time they are touched.
104
        """
10 by Gustavo Niemeyer
- Renamed ClassInfo.primary_key to pk_prop_insts and introduced
105
        self.flush()
95.3.71 by Gustavo Niemeyer
As discussed with James Henstrigde, committing and rolling back
106
        self.invalidate()
9 by Gustavo Niemeyer
- Merged Connection.execute_expr() into Connection.execute().
107
        self._connection.commit()
108
109
    def rollback(self):
142.3.20 by Christopher Armstrong
massive doc improvements for store.py
110
        """Roll back all outstanding changes, reverting to database state."""
95.3.71 by Gustavo Niemeyer
As discussed with James Henstrigde, committing and rolling back
111
        for obj_info in self._dirty:
112
            pending = obj_info.pop("pending", None)
113
            if pending is PENDING_ADD:
114
                # Object never got in the cache, so being "in the store"
115
                # has no actual meaning for it.
116
                del obj_info["store"]
117
            elif pending is PENDING_REMOVE:
118
                # Object never got removed, so it's still in the cache,
119
                # and thus should continue to resolve from now on.
81.1.20 by Gustavo Niemeyer
- Implemented support for expressions as lazy values in the store!
120
                self._enable_lazy_resolving(obj_info)
26 by Gustavo Niemeyer
Major cleanup in the store and improved caching behavior.
121
        self._dirty.clear()
95.3.71 by Gustavo Niemeyer
As discussed with James Henstrigde, committing and rolling back
122
        self.invalidate()
9 by Gustavo Niemeyer
- Merged Connection.execute_expr() into Connection.execute().
123
        self._connection.rollback()
7 by Gustavo Niemeyer
- Started implementing Store.
124
112 by Gustavo Niemeyer
Removed support for adaptation. In practice we were not able
125
    def get(self, cls, key):
106.1.3 by Gustavo Niemeyer
- Refactored common methods of BoundReferenceSet and
126
        """Get object of type cls with the given primary key from the database.
127
196.1.4 by Bernd Dorn
implementation of strong reference cache finished
128
        If the object is alive the database won't be touched.
106.1.3 by Gustavo Niemeyer
- Refactored common methods of BoundReferenceSet and
129
130
        @param cls: Class of the object to be retrieved.
131
        @param key: Primary key of object. May be a tuple for composed keys.
171 by Christopher Armstrong
Mention in the Store.get() docstring that None may be returned
132
133
        @return: The object found with the given primary key, or None
134
            if no object is found.
106.1.3 by Gustavo Niemeyer
- Refactored common methods of BoundReferenceSet and
135
        """
136
10 by Gustavo Niemeyer
- Renamed ClassInfo.primary_key to pk_prop_insts and introduced
137
        self.flush()
138
35 by Gustavo Niemeyer
- Implemented generic hooks in ObjectInfo.
139
        if type(key) != tuple:
7 by Gustavo Niemeyer
- Started implementing Store.
140
            key = (key,)
35 by Gustavo Niemeyer
- Implemented generic hooks in ObjectInfo.
141
142
        cls_info = get_cls_info(cls)
143
144
        assert len(key) == len(cls_info.primary_key)
145
58 by Gustavo Niemeyer
- Major refactoring of the typing system. Kinds are gone. Now Variables
146
        primary_vars = []
147
        for column, variable in zip(cls_info.primary_key, key):
148
            if not isinstance(variable, Variable):
149
                variable = column.variable_factory(value=variable)
150
            primary_vars.append(variable)
151
196.1.2 by Bernd Dorn
ups alive is the right name
152
        obj_info = self._alive.get((cls_info.cls, tuple(primary_vars)))
62 by Gustavo Niemeyer
Changed the way that objects are keyed. Now they use the obj_info,
153
        if obj_info is not None:
95.3.71 by Gustavo Niemeyer
As discussed with James Henstrigde, committing and rolling back
154
            if obj_info.get("invalidated"):
85 by Gustavo Niemeyer
- Greatly improved the autoreload feature support.
155
                try:
196.1.3 by Bernd Dorn
more rename to alive
156
                    self._validate_alive(obj_info)
85 by Gustavo Niemeyer
- Greatly improved the autoreload feature support.
157
                except LostObjectError:
158
                    return None
142.2.2 by Gustavo Niemeyer
- When an object died in memory, ResultSet.cached() wasn't returning
159
            return self._get_object(obj_info)
58 by Gustavo Niemeyer
- Major refactoring of the typing system. Kinds are gone. Now Variables
160
161
        where = compare_columns(cls_info.primary_key, primary_vars)
7 by Gustavo Niemeyer
- Started implementing Store.
162
33 by Gustavo Niemeyer
- Refactored the way properties are stored in the object info. Now
163
        select = Select(cls_info.columns, where,
24 by Gustavo Niemeyer
- Removed AutoTable. It's now implemented internally in statements.
164
                        default_tables=cls_info.table, limit=1)
7 by Gustavo Niemeyer
- Started implementing Store.
165
40 by Gustavo Niemeyer
- Implemented result.to_kind(). It'll be used soon to process
166
        result = self._connection.execute(select)
50 by Gustavo Niemeyer
- Renamed Result.fetch_one/all to get_one/all.
167
        values = result.get_one()
14 by Gustavo Niemeyer
Minor changes for beautification.
168
        if values is None:
7 by Gustavo Niemeyer
- Started implementing Store.
169
            return None
196.1.5 by Bernd Dorn
added more tests and changed according to review from niemeyer
170
        return self._load_object(cls_info, result, values)
7 by Gustavo Niemeyer
- Started implementing Store.
171
81.1.5 by Gustavo Niemeyer
Implemented support for using a tuple of classes as the first argument of
172
    def find(self, cls_spec, *args, **kwargs):
142.3.20 by Christopher Armstrong
massive doc improvements for store.py
173
        """Perform a query.
174
175
        Some examples::
176
177
            store.find(Person, Person.name == u"Joe") --> all Persons named Joe
178
            store.find(Person, name=u"Joe") --> same
179
            store.find((Company, Person), Person.company_id == Company.id) -->
180
                iterator of tuples of Company and Person instances which are
181
                associated via the company_id -> Company relation.
182
183
        @param cls_spec: The class or tuple of classes whose
184
            associated tables will be queried.
185
        @param args: Instances of L{Expr}.
186
        @param kwargs: Mapping of simple column names to values or
187
            expressions to query for.
188
189
        @return: A L{ResultSet} of instances C{cls_spec}. If C{cls_spec}
190
            was a tuple, then an iterator of tuples of such instances.
191
        """
10 by Gustavo Niemeyer
- Renamed ClassInfo.primary_key to pk_prop_insts and introduced
192
        self.flush()
81.1.5 by Gustavo Niemeyer
Implemented support for using a tuple of classes as the first argument of
193
        if type(cls_spec) is tuple:
194
            cls_spec_info = tuple(get_cls_info(cls) for cls in cls_spec)
195
            where = get_where_for_args(args, kwargs)
196
        else:
197
            cls_spec_info = get_cls_info(cls_spec)
198
            where = get_where_for_args(args, kwargs, cls_spec)
199
        return self._result_set_factory(self, cls_spec_info, where)
9 by Gustavo Niemeyer
- Merged Connection.execute_expr() into Connection.execute().
200
81.1.3 by Gustavo Niemeyer
- Redesigned join expressions to handle more complex cases.
201
    def using(self, *tables):
142.3.21 by Christopher Armstrong
improvements to store documentation on Gustavo's advice, thanks!
202
        """Specify tables to use explicitly.
203
204
        The L{find} method generally does a good job at figuring out
205
        the tables to query by itself, but in some cases it's useful
206
        to specify them explicitly.
207
208
        This is most often necessary when an explicit SQL join is
209
        required. An example follows::
210
211
            join = LeftJoin(Person, Person.id == Company.person_id)
212
            print list(store.using(Company, join).find((Company, Person)))
213
214
        The previous code snippet will produce an SQL statement
215
        somewhat similar to this, depending on your backend::
216
217
            SELECT company.id, employee.company_id, employee.id
218
            FROM company
219
            LEFT JOIN employee ON employee.company_id = company.id;
220
221
        @return: A L{TableSet}, which has a C{find} method similar to
222
            L{Store.find}.
142.3.20 by Christopher Armstrong
massive doc improvements for store.py
223
        """
81.1.12 by Gustavo Niemeyer
- Reimplemented currval() call in the postgres database backend
224
        return self._table_set(self, tables)
81.1.3 by Gustavo Niemeyer
- Redesigned join expressions to handle more complex cases.
225
9 by Gustavo Niemeyer
- Merged Connection.execute_expr() into Connection.execute().
226
    def add(self, obj):
142.3.20 by Christopher Armstrong
massive doc improvements for store.py
227
        """Add the given object to the store.
228
229
        The object will be inserted into the database if it has not
230
        yet been added.
231
232
        The C{added} event will be fired on the object info's event system.
233
        """
25 by Gustavo Niemeyer
- Moved ObjectInfo and ClassInfo to an implementation based on
234
        obj_info = get_obj_info(obj)
26 by Gustavo Niemeyer
Major cleanup in the store and improved caching behavior.
235
25 by Gustavo Niemeyer
- Moved ObjectInfo and ClassInfo to an implementation based on
236
        store = obj_info.get("store")
26 by Gustavo Niemeyer
Major cleanup in the store and improved caching behavior.
237
        if store is not None and store is not self:
62 by Gustavo Niemeyer
Changed the way that objects are keyed. Now they use the obj_info,
238
            raise WrongStoreError("%s is part of another store" % repr(obj))
26 by Gustavo Niemeyer
Major cleanup in the store and improved caching behavior.
239
240
        pending = obj_info.get("pending")
241
242
        if pending is PENDING_ADD:
60 by Gustavo Niemeyer
- Implemented Store.new()
243
            pass
26 by Gustavo Niemeyer
Major cleanup in the store and improved caching behavior.
244
        elif pending is PENDING_REMOVE:
245
            del obj_info["pending"]
81.1.20 by Gustavo Niemeyer
- Implemented support for expressions as lazy values in the store!
246
            self._enable_lazy_resolving(obj_info)
61 by Gustavo Niemeyer
- Now ReferenceSets will not require the object to be in a store
247
            # obj_info.event.emit("added")
95.3.71 by Gustavo Niemeyer
As discussed with James Henstrigde, committing and rolling back
248
        elif store is None:
249
            obj_info["store"] = self
26 by Gustavo Niemeyer
Major cleanup in the store and improved caching behavior.
250
            obj_info["pending"] = PENDING_ADD
62 by Gustavo Niemeyer
Changed the way that objects are keyed. Now they use the obj_info,
251
            self._set_dirty(obj_info)
81.1.20 by Gustavo Niemeyer
- Implemented support for expressions as lazy values in the store!
252
            self._enable_lazy_resolving(obj_info)
60 by Gustavo Niemeyer
- Implemented Store.new()
253
            obj_info.event.emit("added")
9 by Gustavo Niemeyer
- Merged Connection.execute_expr() into Connection.execute().
254
101.1.1 by Jamu Kakar
- Store.add(obj) returns obj.
255
        return obj
256
9 by Gustavo Niemeyer
- Merged Connection.execute_expr() into Connection.execute().
257
    def remove(self, obj):
142.3.20 by Christopher Armstrong
massive doc improvements for store.py
258
        """Remove the given object from the store.
259
260
        The associated row will be deleted from the database.
261
        """
25 by Gustavo Niemeyer
- Moved ObjectInfo and ClassInfo to an implementation based on
262
        obj_info = get_obj_info(obj)
26 by Gustavo Niemeyer
Major cleanup in the store and improved caching behavior.
263
44 by Gustavo Niemeyer
- Decoupled most of the logic from Reference into Relation, and
264
        if obj_info.get("store") is not self:
62 by Gustavo Niemeyer
Changed the way that objects are keyed. Now they use the obj_info,
265
            raise WrongStoreError("%s is not in this store" % repr(obj))
26 by Gustavo Niemeyer
Major cleanup in the store and improved caching behavior.
266
267
        pending = obj_info.get("pending")
268
269
        if pending is PENDING_REMOVE:
60 by Gustavo Niemeyer
- Implemented Store.new()
270
            pass
26 by Gustavo Niemeyer
Major cleanup in the store and improved caching behavior.
271
        elif pending is PENDING_ADD:
95.3.71 by Gustavo Niemeyer
As discussed with James Henstrigde, committing and rolling back
272
            del obj_info["store"]
26 by Gustavo Niemeyer
Major cleanup in the store and improved caching behavior.
273
            del obj_info["pending"]
62 by Gustavo Niemeyer
Changed the way that objects are keyed. Now they use the obj_info,
274
            self._set_clean(obj_info)
81.1.20 by Gustavo Niemeyer
- Implemented support for expressions as lazy values in the store!
275
            self._disable_lazy_resolving(obj_info)
95.3.71 by Gustavo Niemeyer
As discussed with James Henstrigde, committing and rolling back
276
        else:
26 by Gustavo Niemeyer
Major cleanup in the store and improved caching behavior.
277
            obj_info["pending"] = PENDING_REMOVE
62 by Gustavo Niemeyer
Changed the way that objects are keyed. Now they use the obj_info,
278
            self._set_dirty(obj_info)
81.1.20 by Gustavo Niemeyer
- Implemented support for expressions as lazy values in the store!
279
            self._disable_lazy_resolving(obj_info)
10 by Gustavo Niemeyer
- Renamed ClassInfo.primary_key to pk_prop_insts and introduced
280
44 by Gustavo Niemeyer
- Decoupled most of the logic from Reference into Relation, and
281
    def reload(self, obj):
142.3.20 by Christopher Armstrong
massive doc improvements for store.py
282
        """Reload the given object.
283
284
        The object will immediately have all of its data reset from
285
        the database. Any pending changes will be thrown away.
286
        """
62 by Gustavo Niemeyer
Changed the way that objects are keyed. Now they use the obj_info,
287
        obj_info = get_obj_info(obj)
288
        cls_info = obj_info.cls_info
95.3.71 by Gustavo Niemeyer
As discussed with James Henstrigde, committing and rolling back
289
        if obj_info.get("store") is not self:
62 by Gustavo Niemeyer
Changed the way that objects are keyed. Now they use the obj_info,
290
            raise WrongStoreError("%s is not in this store" % repr(obj))
58 by Gustavo Niemeyer
- Major refactoring of the typing system. Kinds are gone. Now Variables
291
        if "primary_vars" not in obj_info:
59 by Gustavo Niemeyer
- Created exception hierarchy, and patched DBAPI2 modules to include
292
            raise NotFlushedError("Can't reload an object if it was "
293
                                  "never flushed")
62 by Gustavo Niemeyer
Changed the way that objects are keyed. Now they use the obj_info,
294
        where = compare_columns(cls_info.primary_key, obj_info["primary_vars"])
44 by Gustavo Niemeyer
- Decoupled most of the logic from Reference into Relation, and
295
        select = Select(cls_info.columns, where,
296
                        default_tables=cls_info.table, limit=1)
297
        result = self._connection.execute(select)
50 by Gustavo Niemeyer
- Renamed Result.fetch_one/all to get_one/all.
298
        values = result.get_one()
58 by Gustavo Niemeyer
- Major refactoring of the typing system. Kinds are gone. Now Variables
299
        self._set_values(obj_info, cls_info.columns, result, values)
44 by Gustavo Niemeyer
- Decoupled most of the logic from Reference into Relation, and
300
        obj_info.checkpoint()
62 by Gustavo Niemeyer
Changed the way that objects are keyed. Now they use the obj_info,
301
        self._set_clean(obj_info)
44 by Gustavo Niemeyer
- Decoupled most of the logic from Reference into Relation, and
302
83.1.1 by Gustavo Niemeyer
- Implemented 'autoreload' feature!
303
    def autoreload(self, obj=None):
142.3.20 by Christopher Armstrong
massive doc improvements for store.py
304
        """Set an object or all objects to be reloaded automatically on access.
305
142.3.21 by Christopher Armstrong
improvements to store documentation on Gustavo's advice, thanks!
306
        When a database-backed attribute of one of the objects is
307
        accessed, the object will be reloaded entirely from the database.
142.3.20 by Christopher Armstrong
massive doc improvements for store.py
308
309
        @param obj: If passed, only mark the given object for
310
            autoreload. Otherwise, all cached objects will be marked for
311
            autoreload.
312
        """
85 by Gustavo Niemeyer
- Greatly improved the autoreload feature support.
313
        self._mark_autoreload(obj, False)
314
315
    def invalidate(self, obj=None):
142.3.21 by Christopher Armstrong
improvements to store documentation on Gustavo's advice, thanks!
316
        """Set an object or all objects to be invalidated.
317
318
        This prevents Storm from returning the cached object without
319
        first verifying that the object is still available in the
320
        database.
321
322
        This should almost never be called by application code; it is
323
        only necessary if it is possible that an object has
324
        disappeared through some mechanism that Storm was unable to
325
        detect, like direct SQL statements within the current
326
        transaction that bypassed the ORM layer. The Store
327
        automatically invalidates all cached objects on transaction
328
        boundaries.
142.3.20 by Christopher Armstrong
massive doc improvements for store.py
329
        """
196.1.4 by Bernd Dorn
implementation of strong reference cache finished
330
        if obj is None:
331
            self._cache.clear()
332
        else:
333
            self._cache.remove(get_obj_info(obj))
85 by Gustavo Niemeyer
- Greatly improved the autoreload feature support.
334
        self._mark_autoreload(obj, True)
335
336
    def _mark_autoreload(self, obj=None, invalidate=False):
337
        if obj is None:
196.1.3 by Bernd Dorn
more rename to alive
338
            obj_infos = self._iter_alive()
85 by Gustavo Niemeyer
- Greatly improved the autoreload feature support.
339
        else:
340
            obj_infos = (get_obj_info(obj),)
341
        for obj_info in obj_infos:
342
            cls_info = obj_info.cls_info
343
            for column in cls_info.columns:
91 by Gustavo Niemeyer
- Autoreloading objects won't autoreload the primary key, since it's
344
                if id(column) not in cls_info.primary_key_idx:
345
                    obj_info.variables[column].set(AutoReload)
85 by Gustavo Niemeyer
- Greatly improved the autoreload feature support.
346
            if invalidate:
95.3.71 by Gustavo Niemeyer
As discussed with James Henstrigde, committing and rolling back
347
                # Marking an object with 'invalidated' means that we're
348
                # not sure if the object is actually in the database
349
                # anymore, so before the object is returned from the cache
350
                # (e.g. by a get()), the database should be queried to see
351
                # if the object's still there.
352
                obj_info["invalidated"] = True
106.1.5 by Christopher Armstrong
Clean up magic-typing a bit:
353
        # We want to make sure we've marked all objects as invalidated and set
354
        # up their autoreloads before calling the invalidated hook on *any* of
112 by Gustavo Niemeyer
Removed support for adaptation. In practice we were not able
355
        # them, because an invalidated hook might use other objects and we want
356
        # to prevent invalidation ordering issues.
95.6.7 by Gustavo Niemeyer
Renamed invalidate hook to invalidated, confirming to other
357
        if invalidate:
358
            for obj_info in obj_infos:
359
                self._run_hook(obj_info, "__storm_invalidated__")
85 by Gustavo Niemeyer
- Greatly improved the autoreload feature support.
360
36 by Gustavo Niemeyer
- Implemented support for back references.
361
    def add_flush_order(self, before, after):
142.3.20 by Christopher Armstrong
massive doc improvements for store.py
362
        """Explicitly specify the order of flushing two objects.
363
364
        When the next database flush occurs, the order of data
365
        modification statements will be ensured.
366
367
        @param before: The object to flush first.
368
        @param after: The object to flush after C{before}.
369
        """
62 by Gustavo Niemeyer
Changed the way that objects are keyed. Now they use the obj_info,
370
        pair = (get_obj_info(before), get_obj_info(after))
36 by Gustavo Niemeyer
- Implemented support for back references.
371
        try:
372
            self._order[pair] += 1
373
        except KeyError:
374
            self._order[pair] = 1
375
376
    def remove_flush_order(self, before, after):
142.3.20 by Christopher Armstrong
massive doc improvements for store.py
377
        """Cancel an explicit flush order specified with L{add_flush_order}.
378
379
        @param before: The C{before} object previously specified in a
380
            call to L{add_flush_order}.
381
        @param after: The C{after} object previously specified in a
382
            call to L{add_flush_order}.
383
        """
62 by Gustavo Niemeyer
Changed the way that objects are keyed. Now they use the obj_info,
384
        pair = (get_obj_info(before), get_obj_info(after))
36 by Gustavo Niemeyer
- Implemented support for back references.
385
        try:
386
            self._order[pair] -= 1
387
        except KeyError:
388
            pass
389
10 by Gustavo Niemeyer
- Renamed ClassInfo.primary_key to pk_prop_insts and introduced
390
    def flush(self):
144.1.2 by Bjorn Stabell
Added docstring to flush() based on reviews from Christopher Armstrong and Jamu Kakar.
391
        """Flush all dirty objects in cache to database.
392
144.1.3 by Gustavo Niemeyer
- Renamed __storm_flush__ to __storm_pre_flush__, as discussed.
393
        This method will first call the __storm_pre_flush__ hook of all dirty
144.1.2 by Bjorn Stabell
Added docstring to flush() based on reviews from Christopher Armstrong and Jamu Kakar.
394
        objects.  If more objects become dirty as a result of executing code
395
        in the hooks, the hook is also called on them.  The hook is only
396
        called once for each object.
397
144.1.3 by Gustavo Niemeyer
- Renamed __storm_flush__ to __storm_pre_flush__, as discussed.
398
        It will then flush each dirty object to the database, that is,
399
        execute the SQL code to insert/delete/update them.  After each
400
        object is flushed, the hook __storm_flushed__ is called on it,
401
        and if changes are made to the object it will get back to the
402
        dirty list, and be flushed again.
144.1.2 by Bjorn Stabell
Added docstring to flush() based on reviews from Christopher Armstrong and Jamu Kakar.
403
404
        Note that Storm will flush objects for you automatically, so you'll
405
        only need to call this method explicitly in very rare cases where
406
        normal flushing times are insufficient, such as when you want to
407
        make sure a database trigger gets run at a particular time.
142.3.20 by Christopher Armstrong
massive doc improvements for store.py
408
        """
196.1.3 by Bernd Dorn
more rename to alive
409
        for obj_info in self._iter_alive():
95.1.8 by Gustavo Niemeyer
- Event system now has owner as a weak reference, to prevent the
410
            obj_info.event.emit("flush")
411
144.1.1 by Bjorn Stabell
Pair programming: Added __storm_flush__ hook that will be called before commits, as opposed to
412
        # The _dirty list may change under us while we're running
413
        # the flush hooks, so we cannot just simply loop over it
414
        # once.  To prevent infinite looping we keep track of which
415
        # objects we've called the hook for using a `flushing` dict.
416
        flushing = {}
417
        while self._dirty:
418
            (obj_info, obj) = self._dirty.popitem()
419
            if obj_info not in flushing:
420
                flushing[obj_info] = obj
144.1.3 by Gustavo Niemeyer
- Renamed __storm_flush__ to __storm_pre_flush__, as discussed.
421
                self._run_hook(obj_info, "__storm_pre_flush__")
144.1.1 by Bjorn Stabell
Pair programming: Added __storm_flush__ hook that will be called before commits, as opposed to
422
        self._dirty = flushing
423
36 by Gustavo Niemeyer
- Implemented support for back references.
424
        predecessors = {}
62 by Gustavo Niemeyer
Changed the way that objects are keyed. Now they use the obj_info,
425
        for (before_info, after_info), n in self._order.iteritems():
36 by Gustavo Niemeyer
- Implemented support for back references.
426
            if n > 0:
62 by Gustavo Niemeyer
Changed the way that objects are keyed. Now they use the obj_info,
427
                before_set = predecessors.get(after_info)
36 by Gustavo Niemeyer
- Implemented support for back references.
428
                if before_set is None:
62 by Gustavo Niemeyer
Changed the way that objects are keyed. Now they use the obj_info,
429
                    predecessors[after_info] = set((before_info,))
36 by Gustavo Niemeyer
- Implemented support for back references.
430
                else:
62 by Gustavo Niemeyer
Changed the way that objects are keyed. Now they use the obj_info,
431
                    before_set.add(before_info)
36 by Gustavo Niemeyer
- Implemented support for back references.
432
35 by Gustavo Niemeyer
- Implemented generic hooks in ObjectInfo.
433
        while self._dirty:
62 by Gustavo Niemeyer
Changed the way that objects are keyed. Now they use the obj_info,
434
            for obj_info in self._dirty:
435
                for before_info in predecessors.get(obj_info, ()):
436
                    if before_info in self._dirty:
36 by Gustavo Niemeyer
- Implemented support for back references.
437
                        break # A predecessor is still dirty.
438
                else:
439
                    break # Found an item without dirty predecessors.
440
            else:
59 by Gustavo Niemeyer
- Created exception hierarchy, and patched DBAPI2 modules to include
441
                raise OrderLoopError("Can't flush due to ordering loop")
95.1.8 by Gustavo Niemeyer
- Event system now has owner as a weak reference, to prevent the
442
            self._dirty.pop(obj_info, None)
62 by Gustavo Niemeyer
Changed the way that objects are keyed. Now they use the obj_info,
443
            self._flush_one(obj_info)
36 by Gustavo Niemeyer
- Implemented support for back references.
444
445
        self._order.clear()
446
62 by Gustavo Niemeyer
Changed the way that objects are keyed. Now they use the obj_info,
447
    def _flush_one(self, obj_info):
448
        cls_info = obj_info.cls_info
35 by Gustavo Niemeyer
- Implemented generic hooks in ObjectInfo.
449
450
        pending = obj_info.pop("pending", None)
451
452
        if pending is PENDING_REMOVE:
36 by Gustavo Niemeyer
- Implemented support for back references.
453
            expr = Delete(compare_columns(cls_info.primary_key,
58 by Gustavo Niemeyer
- Major refactoring of the typing system. Kinds are gone. Now Variables
454
                                          obj_info["primary_vars"]),
35 by Gustavo Niemeyer
- Implemented generic hooks in ObjectInfo.
455
                          cls_info.table)
456
            self._connection.execute(expr, noresult=True)
457
85 by Gustavo Niemeyer
- Greatly improved the autoreload feature support.
458
            # We're sure the cache is valid at this point.
95.3.71 by Gustavo Niemeyer
As discussed with James Henstrigde, committing and rolling back
459
            obj_info.pop("invalidated", None)
85 by Gustavo Niemeyer
- Greatly improved the autoreload feature support.
460
62 by Gustavo Niemeyer
Changed the way that objects are keyed. Now they use the obj_info,
461
            self._disable_change_notification(obj_info)
196.1.3 by Bernd Dorn
more rename to alive
462
            self._remove_from_alive(obj_info)
95.3.71 by Gustavo Niemeyer
As discussed with James Henstrigde, committing and rolling back
463
            del obj_info["store"]
35 by Gustavo Niemeyer
- Implemented generic hooks in ObjectInfo.
464
465
        elif pending is PENDING_ADD:
156.1.1 by Gustavo Niemeyer
- Implemented support for lazy expressions in primary keys. This opens
466
467
            # Give a chance to the backend to process primary variables.
468
            self._connection.preset_primary_key(cls_info.primary_key,
469
                                                obj_info.primary_vars)
470
471
            changes = self._get_changes_map(obj_info, True)
472
473
            expr = Insert(changes, cls_info.table,
474
                          primary_columns=cls_info.primary_key,
475
                          primary_variables=obj_info.primary_vars)
35 by Gustavo Niemeyer
- Implemented generic hooks in ObjectInfo.
476
477
            result = self._connection.execute(expr)
478
85 by Gustavo Niemeyer
- Greatly improved the autoreload feature support.
479
            # We're sure the cache is valid at this point. We just added
480
            # the object.
95.3.71 by Gustavo Niemeyer
As discussed with James Henstrigde, committing and rolling back
481
            obj_info.pop("invalidated", None)
85 by Gustavo Niemeyer
- Greatly improved the autoreload feature support.
482
190.1.2 by Gustavo Niemeyer
Rather than retrieving all values from the database after an object
483
            self._fill_missing_values(obj_info, obj_info.primary_vars, result)
35 by Gustavo Niemeyer
- Implemented generic hooks in ObjectInfo.
484
62 by Gustavo Niemeyer
Changed the way that objects are keyed. Now they use the obj_info,
485
            self._enable_change_notification(obj_info)
196.1.3 by Bernd Dorn
more rename to alive
486
            self._add_to_alive(obj_info)
35 by Gustavo Niemeyer
- Implemented generic hooks in ObjectInfo.
487
44 by Gustavo Niemeyer
- Decoupled most of the logic from Reference into Relation, and
488
            obj_info.checkpoint()
489
58 by Gustavo Niemeyer
- Major refactoring of the typing system. Kinds are gone. Now Variables
490
        else:
39 by Gustavo Niemeyer
- Implemented new column typing and value filtering system
491
85 by Gustavo Niemeyer
- Greatly improved the autoreload feature support.
492
            cached_primary_vars = obj_info["primary_vars"]
493
            primary_key_idx = cls_info.primary_key_idx
494
156.1.1 by Gustavo Niemeyer
- Implemented support for lazy expressions in primary keys. This opens
495
            changes = self._get_changes_map(obj_info)
35 by Gustavo Niemeyer
- Implemented generic hooks in ObjectInfo.
496
58 by Gustavo Niemeyer
- Major refactoring of the typing system. Kinds are gone. Now Variables
497
            if changes:
498
                expr = Update(changes,
36 by Gustavo Niemeyer
- Implemented support for back references.
499
                              compare_columns(cls_info.primary_key,
85 by Gustavo Niemeyer
- Greatly improved the autoreload feature support.
500
                                              cached_primary_vars),
26 by Gustavo Niemeyer
Major cleanup in the store and improved caching behavior.
501
                              cls_info.table)
502
                self._connection.execute(expr, noresult=True)
503
190.1.2 by Gustavo Niemeyer
Rather than retrieving all values from the database after an object
504
                self._fill_missing_values(obj_info, obj_info.primary_vars)
81.1.20 by Gustavo Niemeyer
- Implemented support for expressions as lazy values in the store!
505
196.1.3 by Bernd Dorn
more rename to alive
506
                self._add_to_alive(obj_info)
26 by Gustavo Niemeyer
Major cleanup in the store and improved caching behavior.
507
85 by Gustavo Niemeyer
- Greatly improved the autoreload feature support.
508
44 by Gustavo Niemeyer
- Decoupled most of the logic from Reference into Relation, and
509
            obj_info.checkpoint()
510
95.6.3 by Gustavo Niemeyer
Renamed all hooks to __storm_<name>__, instead of __<name>__.
511
        self._run_hook(obj_info, "__storm_flushed__")
95.6.2 by Gustavo Niemeyer
Implemented __flushed__ hook.
512
58 by Gustavo Niemeyer
- Major refactoring of the typing system. Kinds are gone. Now Variables
513
        obj_info.event.emit("flushed")
10 by Gustavo Niemeyer
- Renamed ClassInfo.primary_key to pk_prop_insts and introduced
514
156.1.1 by Gustavo Niemeyer
- Implemented support for lazy expressions in primary keys. This opens
515
    def _get_changes_map(self, obj_info, adding=False):
516
        """Return a {column: variable} dictionary suitable for inserts/updates.
517
518
        @param obj_info: ObjectInfo to inspect for changes.
519
        @param adding: If true, any defined variables will be considered
520
                       a change and included in the returned map.
521
        """
522
        cls_info = obj_info.cls_info
523
        changes = {}
524
        select_variables = []
525
        for column in cls_info.columns:
526
            variable = obj_info.variables[column]
527
            if adding or variable.has_changed():
528
                if variable.is_defined():
529
                    changes[column] = variable
530
                else:
531
                    lazy_value = variable.get_lazy()
532
                    if isinstance(lazy_value, Expr):
533
                        if id(column) in cls_info.primary_key_idx:
534
                            select_variables.append(variable) # See below.
535
                            changes[column] = variable
536
                        else:
537
                            changes[column] = lazy_value
538
539
        # If we have any expressions in the primary variables, we
540
        # have to resolve them now so that we have the identity of
541
        # the inserted object available later.
542
        if select_variables:
543
            resolve_expr = Select([variable.get_lazy()
544
                                   for variable in select_variables])
545
            result = self._connection.execute(resolve_expr)
546
            for variable, value in zip(select_variables, result.get_one()):
547
                result.set_variable(variable, value)
548
549
        return changes
550
190.1.2 by Gustavo Niemeyer
Rather than retrieving all values from the database after an object
551
    def _fill_missing_values(self, obj_info, primary_vars, result=None):
552
        """Fill missing values in variables of the given obj_info.
133 by Gustavo Niemeyer
Added big fat comment in Store._fill_missing_values(), as suggested
553
554
        This method will verify which values are unset in obj_info,
190.1.2 by Gustavo Niemeyer
Rather than retrieving all values from the database after an object
555
        and set them to AutoReload, or if it's part of the primary
556
        key, query the database for the actual values.
133 by Gustavo Niemeyer
Added big fat comment in Store._fill_missing_values(), as suggested
557
558
        @param obj_info: ObjectInfo to have its values filled.
559
        @param primary_vars: Variables composing the primary key with
560
            up-to-date values (cached variables may be out-of-date when
561
            this method is called).
562
        @param result: If some value in the set of primary variables
563
            isn't defined, it must be retrieved from the database
564
            using database-dependent logic, which is provided by the
565
            backend in the result of the query which inserted the object.
566
        """
62 by Gustavo Niemeyer
Changed the way that objects are keyed. Now they use the obj_info,
567
        cls_info = obj_info.cls_info
30 by Gustavo Niemeyer
- Added support for retrieving values automatically set by the
568
85 by Gustavo Niemeyer
- Greatly improved the autoreload feature support.
569
        cached_primary_vars = obj_info.get("primary_vars")
570
        primary_key_idx = cls_info.primary_key_idx
33 by Gustavo Niemeyer
- Refactored the way properties are stored in the object info. Now
571
        missing_columns = []
39 by Gustavo Niemeyer
- Implemented new column typing and value filtering system
572
        for column in cls_info.columns:
85 by Gustavo Niemeyer
- Greatly improved the autoreload feature support.
573
            variable = obj_info.variables[column]
574
            if not variable.is_defined():
575
                idx = primary_key_idx.get(id(column))
190.1.2 by Gustavo Niemeyer
Rather than retrieving all values from the database after an object
576
                if idx is not None:
577
                    if (cached_primary_vars is not None
578
                        and variable.get_lazy() is AutoReload):
579
                        # For auto-reloading a primary key, just
580
                        # get the value out of the cache.
581
                        variable.set(cached_primary_vars[idx].get())
582
                    else:
583
                        missing_columns.append(column)
584
                else:
585
                    # Any lazy values are overwritten here.  This value
586
                    # must have just been sent to the database, so this
587
                    # was already set there.
588
                    variable.set(AutoReload)
30 by Gustavo Niemeyer
- Added support for retrieving values automatically set by the
589
33 by Gustavo Niemeyer
- Refactored the way properties are stored in the object info. Now
590
        if missing_columns:
190.1.2 by Gustavo Niemeyer
Rather than retrieving all values from the database after an object
591
            where = result.get_insert_identity(cls_info.primary_key,
592
                                               primary_vars)
40 by Gustavo Niemeyer
- Implemented result.to_kind(). It'll be used soon to process
593
            result = self._connection.execute(Select(missing_columns, where))
594
            self._set_values(obj_info, missing_columns,
50 by Gustavo Niemeyer
- Renamed Result.fetch_one/all to get_one/all.
595
                             result, result.get_one())
40 by Gustavo Niemeyer
- Implemented result.to_kind(). It'll be used soon to process
596
196.1.3 by Bernd Dorn
more rename to alive
597
    def _validate_alive(self, obj_info):
190.1.2 by Gustavo Niemeyer
Rather than retrieving all values from the database after an object
598
        """Perform cache validation for the given obj_info."""
599
        where = compare_columns(obj_info.cls_info.primary_key,
600
                                obj_info["primary_vars"])
601
        result = self._connection.execute(Select(SQLRaw("1"), where))
602
        if not result.get_one():
603
            raise LostObjectError("Object is not in the database anymore")
604
        obj_info.pop("invalidated", None)
85 by Gustavo Niemeyer
- Greatly improved the autoreload feature support.
605
112 by Gustavo Niemeyer
Removed support for adaptation. In practice we were not able
606
    def _load_objects(self, cls_spec_info, result, values):
81.1.5 by Gustavo Niemeyer
Implemented support for using a tuple of classes as the first argument of
607
        if type(cls_spec_info) is not tuple:
112 by Gustavo Niemeyer
Removed support for adaptation. In practice we were not able
608
            return self._load_object(cls_spec_info, result, values)
81.1.5 by Gustavo Niemeyer
Implemented support for using a tuple of classes as the first argument of
609
        else:
610
            objects = []
611
            values_start = values_end = 0
612
            for cls_info in cls_spec_info:
613
                values_end += len(cls_info.columns)
614
                obj = self._load_object(cls_info, result,
112 by Gustavo Niemeyer
Removed support for adaptation. In practice we were not able
615
                                        values[values_start:values_end])
81.1.5 by Gustavo Niemeyer
Implemented support for using a tuple of classes as the first argument of
616
                objects.append(obj)
617
                values_start = values_end
618
            return tuple(objects)
619
112 by Gustavo Niemeyer
Removed support for adaptation. In practice we were not able
620
    def _load_object(self, cls_info, result, values):
81.1.12 by Gustavo Niemeyer
- Reimplemented currval() call in the postgres database backend
621
        # _set_values() need the cls_info columns for the class of the
85 by Gustavo Niemeyer
- Greatly improved the autoreload feature support.
622
        # actual object, not from a possible wrapper (e.g. an alias).
81.1.12 by Gustavo Niemeyer
- Reimplemented currval() call in the postgres database backend
623
        cls = cls_info.cls
624
        cls_info = get_cls_info(cls)
95.1.8 by Gustavo Niemeyer
- Event system now has owner as a weak reference, to prevent the
625
626
        # Prepare cache key.
627
        primary_vars = []
628
        columns = cls_info.columns
629
        is_null = True
630
        for i in cls_info.primary_key_pos:
631
            value = values[i]
632
            if value is not None:
633
                is_null = False
106.1.3 by Gustavo Niemeyer
- Refactored common methods of BoundReferenceSet and
634
            variable = columns[i].variable_factory(value=value, from_db=True)
95.1.8 by Gustavo Niemeyer
- Event system now has owner as a weak reference, to prevent the
635
            primary_vars.append(variable)
636
637
        if is_null:
638
            # We've got a row full of NULLs, so consider that the object
639
            # wasn't found.  This is useful for joins, where unexistent
640
            # rows are reprsented like that.
641
            return None
642
643
        # Lookup cache.
196.1.2 by Bernd Dorn
ups alive is the right name
644
        obj_info = self._alive.get((cls, tuple(primary_vars)))
95.1.8 by Gustavo Niemeyer
- Event system now has owner as a weak reference, to prevent the
645
646
        if obj_info is not None:
647
            # Found object in cache, and it must be valid since the
648
            # primary key was extracted from result values.
95.3.71 by Gustavo Niemeyer
As discussed with James Henstrigde, committing and rolling back
649
            obj_info.pop("invalidated", None)
95.1.8 by Gustavo Niemeyer
- Event system now has owner as a weak reference, to prevent the
650
142.2.2 by Gustavo Niemeyer
- When an object died in memory, ResultSet.cached() wasn't returning
651
            # We're not sure if the obj is still in memory at this
652
            # point.  This will rebuild it if needed.
653
            obj = self._get_object(obj_info)
95.1.8 by Gustavo Niemeyer
- Event system now has owner as a weak reference, to prevent the
654
        else:
655
            # Nothing found in the cache. Build everything from the ground.
81.1.12 by Gustavo Niemeyer
- Reimplemented currval() call in the postgres database backend
656
            obj = cls.__new__(cls)
26 by Gustavo Niemeyer
Major cleanup in the store and improved caching behavior.
657
95.1.8 by Gustavo Niemeyer
- Event system now has owner as a weak reference, to prevent the
658
            obj_info = get_obj_info(obj)
659
            obj_info["store"] = self
660
661
            self._set_values(obj_info, cls_info.columns, result, values)
662
95.3.71 by Gustavo Niemeyer
As discussed with James Henstrigde, committing and rolling back
663
            obj_info.checkpoint()
95.1.8 by Gustavo Niemeyer
- Event system now has owner as a weak reference, to prevent the
664
196.1.3 by Bernd Dorn
more rename to alive
665
            self._add_to_alive(obj_info)
95.1.8 by Gustavo Niemeyer
- Event system now has owner as a weak reference, to prevent the
666
            self._enable_change_notification(obj_info)
667
            self._enable_lazy_resolving(obj_info)
668
95.6.3 by Gustavo Niemeyer
Renamed all hooks to __storm_<name>__, instead of __<name>__.
669
            self._run_hook(obj_info, "__storm_loaded__")
106.1.3 by Gustavo Niemeyer
- Refactored common methods of BoundReferenceSet and
670
671
        return obj
95.1.8 by Gustavo Niemeyer
- Event system now has owner as a weak reference, to prevent the
672
142.2.2 by Gustavo Niemeyer
- When an object died in memory, ResultSet.cached() wasn't returning
673
    def _get_object(self, obj_info):
674
        """Return object for obj_info, rebuilding it if it's dead."""
675
        obj = obj_info.get_obj()
676
        if obj is None:
677
            cls = obj_info.cls_info.cls
678
            obj = cls.__new__(cls)
679
            obj_info.set_obj(obj)
680
            set_obj_info(obj, obj_info)
681
            self._run_hook(obj_info, "__storm_loaded__")
196.1.6 by Gustavo Niemeyer
These changes address a few issues and perform a few stylistic changes
682
        # Renew the cache.
683
        self._cache.add(obj_info)
95.1.8 by Gustavo Niemeyer
- Event system now has owner as a weak reference, to prevent the
684
        return obj
685
686
    @staticmethod
95.6.2 by Gustavo Niemeyer
Implemented __flushed__ hook.
687
    def _run_hook(obj_info, hook_name):
688
        func = getattr(obj_info.get_obj(), hook_name, None)
95.6.1 by Gustavo Niemeyer
Renamed __load__ signal to __loaded__.
689
        if func is not None:
690
            func()
29 by Gustavo Niemeyer
Now changes to object attributes done in __load__() persist if the
691
40 by Gustavo Niemeyer
- Implemented result.to_kind(). It'll be used soon to process
692
    def _set_values(self, obj_info, columns, result, values):
85 by Gustavo Niemeyer
- Greatly improved the autoreload feature support.
693
        if values is None:
694
            raise LostObjectError("Can't obtain values from the database "
695
                                  "(object got removed?)")
190.1.2 by Gustavo Niemeyer
Rather than retrieving all values from the database after an object
696
        obj_info.pop("invalidated", None)
40 by Gustavo Niemeyer
- Implemented result.to_kind(). It'll be used soon to process
697
        for column, value in zip(columns, values):
44 by Gustavo Niemeyer
- Decoupled most of the logic from Reference into Relation, and
698
            if value is None:
58 by Gustavo Niemeyer
- Major refactoring of the typing system. Kinds are gone. Now Variables
699
                obj_info.variables[column].set(value, from_db=True)
44 by Gustavo Niemeyer
- Decoupled most of the logic from Reference into Relation, and
700
            else:
58 by Gustavo Niemeyer
- Major refactoring of the typing system. Kinds are gone. Now Variables
701
                result.set_variable(obj_info.variables[column], value)
40 by Gustavo Niemeyer
- Implemented result.to_kind(). It'll be used soon to process
702
26 by Gustavo Niemeyer
Major cleanup in the store and improved caching behavior.
703
62 by Gustavo Niemeyer
Changed the way that objects are keyed. Now they use the obj_info,
704
    def _is_dirty(self, obj_info):
705
        return obj_info in self._dirty
706
707
    def _set_dirty(self, obj_info):
95.1.8 by Gustavo Niemeyer
- Event system now has owner as a weak reference, to prevent the
708
        self._dirty[obj_info] = obj_info.get_obj()
62 by Gustavo Niemeyer
Changed the way that objects are keyed. Now they use the obj_info,
709
710
    def _set_clean(self, obj_info):
95.1.8 by Gustavo Niemeyer
- Event system now has owner as a weak reference, to prevent the
711
        self._dirty.pop(obj_info, None)
26 by Gustavo Niemeyer
Major cleanup in the store and improved caching behavior.
712
713
    def _iter_dirty(self):
62 by Gustavo Niemeyer
Changed the way that objects are keyed. Now they use the obj_info,
714
        return self._dirty
715
716
196.1.3 by Bernd Dorn
more rename to alive
717
    def _add_to_alive(self, obj_info):
196.1.7 by Gustavo Niemeyer
Introducing new changes coded with Chris about his review on #172357.
718
        """Add an object to the set of known in-memory objects.
719
720
        When an object is added to the set of known in-memory objects,
721
        the key is built from a copy of the current variables that are
722
        part of the primary key.  This means that, when an object is
723
        retrieved from the database, these values may be used to get
724
        the cached object which is already in memory, even if it
725
        requested the primary key value to be changed.  For that reason,
726
        when changes to the primary key are flushed, the alive object
727
        key should also be updated to reflect these changes.
728
729
        In addition to tracking objects alive in memory, we have a strong
730
        reference cache which keeps a fixed number of last-used objects
731
        in-memory, to prevent further database access for recently fetched
732
        objects.
95.3.71 by Gustavo Niemeyer
As discussed with James Henstrigde, committing and rolling back
733
        """
62 by Gustavo Niemeyer
Changed the way that objects are keyed. Now they use the obj_info,
734
        cls_info = obj_info.cls_info
58 by Gustavo Niemeyer
- Major refactoring of the typing system. Kinds are gone. Now Variables
735
        old_primary_vars = obj_info.get("primary_vars")
736
        if old_primary_vars is not None:
196.1.2 by Bernd Dorn
ups alive is the right name
737
            self._alive.pop((cls_info.cls, old_primary_vars), None)
58 by Gustavo Niemeyer
- Major refactoring of the typing system. Kinds are gone. Now Variables
738
        new_primary_vars = tuple(variable.copy()
739
                                 for variable in obj_info.primary_vars)
196.1.2 by Bernd Dorn
ups alive is the right name
740
        self._alive[cls_info.cls, new_primary_vars] = obj_info
58 by Gustavo Niemeyer
- Major refactoring of the typing system. Kinds are gone. Now Variables
741
        obj_info["primary_vars"] = new_primary_vars
196.1.4 by Bernd Dorn
implementation of strong reference cache finished
742
        self._cache.add(obj_info)
743
196.1.3 by Bernd Dorn
more rename to alive
744
    def _remove_from_alive(self, obj_info):
95.3.71 by Gustavo Niemeyer
As discussed with James Henstrigde, committing and rolling back
745
        """Remove an object from the cache.
746
747
        This method is only called for objects that were explicitly
748
        deleted and flushed.  Objects that are unused will get removed
749
        from the cache dictionary automatically by their weakref callbacks.
750
        """
58 by Gustavo Niemeyer
- Major refactoring of the typing system. Kinds are gone. Now Variables
751
        primary_vars = obj_info.get("primary_vars")
752
        if primary_vars is not None:
196.1.5 by Bernd Dorn
added more tests and changed according to review from niemeyer
753
            self._cache.remove(obj_info)
196.1.2 by Bernd Dorn
ups alive is the right name
754
            del self._alive[obj_info.cls_info.cls, primary_vars]
58 by Gustavo Niemeyer
- Major refactoring of the typing system. Kinds are gone. Now Variables
755
            del obj_info["primary_vars"]
26 by Gustavo Niemeyer
Major cleanup in the store and improved caching behavior.
756
196.1.3 by Bernd Dorn
more rename to alive
757
    def _iter_alive(self):
196.1.2 by Bernd Dorn
ups alive is the right name
758
        return self._alive.values()
26 by Gustavo Niemeyer
Major cleanup in the store and improved caching behavior.
759
760
62 by Gustavo Niemeyer
Changed the way that objects are keyed. Now they use the obj_info,
761
    def _enable_change_notification(self, obj_info):
762
        obj_info.event.hook("changed", self._variable_changed)
26 by Gustavo Niemeyer
Major cleanup in the store and improved caching behavior.
763
62 by Gustavo Niemeyer
Changed the way that objects are keyed. Now they use the obj_info,
764
    def _disable_change_notification(self, obj_info):
765
        obj_info.event.unhook("changed", self._variable_changed)
10 by Gustavo Niemeyer
- Renamed ClassInfo.primary_key to pk_prop_insts and introduced
766
81.1.20 by Gustavo Niemeyer
- Implemented support for expressions as lazy values in the store!
767
    def _variable_changed(self, obj_info, variable,
768
                          old_value, new_value, fromdb):
769
        # The fromdb check makes sure that values coming from the
770
        # database don't mark the object as dirty again.
771
        # XXX The fromdb check is untested. How to test it?
123 by Gustavo Niemeyer
Storm will now raise a LostObjectError if an invalidated and
772
        if not fromdb:
773
            if new_value is not Undef and new_value is not AutoReload:
774
                if obj_info.get("invalidated"):
196.1.3 by Bernd Dorn
more rename to alive
775
                    # This might be a previously alive object being
123 by Gustavo Niemeyer
Storm will now raise a LostObjectError if an invalidated and
776
                    # updated.  Let's validate it now to improve debugging.
777
                    # This will raise LostObjectError if the object is gone.
196.1.3 by Bernd Dorn
more rename to alive
778
                    self._validate_alive(obj_info)
123 by Gustavo Niemeyer
Storm will now raise a LostObjectError if an invalidated and
779
                self._set_dirty(obj_info)
10 by Gustavo Niemeyer
- Renamed ClassInfo.primary_key to pk_prop_insts and introduced
780
7 by Gustavo Niemeyer
- Started implementing Store.
781
81.1.20 by Gustavo Niemeyer
- Implemented support for expressions as lazy values in the store!
782
    def _enable_lazy_resolving(self, obj_info):
783
        obj_info.event.hook("resolve-lazy-value", self._resolve_lazy_value)
784
785
    def _disable_lazy_resolving(self, obj_info):
786
        obj_info.event.unhook("resolve-lazy-value", self._resolve_lazy_value)
787
788
    def _resolve_lazy_value(self, obj_info, variable, lazy_value):
190.1.4 by Gustavo Niemeyer
Applying reviews from Jamu and Chris.
789
        """Resolve a variable set to a lazy value when it's touched.
790
791
        This method is hooked into the obj_info to resolve variables
792
        set to lazy values when they're accessed.  It will first flush
793
        the store, and then set all variables set to AutoReload to
794
        their database values.
795
        """
190.1.2 by Gustavo Niemeyer
Rather than retrieving all values from the database after an object
796
        # XXX This will do it for now, but it should really flush
797
        #     just this single object and ones that it depends on.
798
        #     _flush_one() doesn't consider dependencies, so it may
799
        #     not be used directly.  Maybe allow flush(obj)?
800
        self.flush()
801
802
        autoreload_columns = []
803
        for column in obj_info.cls_info.columns:
804
            if obj_info.variables[column].get_lazy() is AutoReload:
805
                autoreload_columns.append(column)
806
807
        if autoreload_columns:
808
            where = compare_columns(obj_info.cls_info.primary_key,
809
                                    obj_info["primary_vars"])
810
            result = self._connection.execute(Select(autoreload_columns, where))
811
            self._set_values(obj_info, autoreload_columns,
812
                             result, result.get_one())
813
            for column in autoreload_columns:
814
                obj_info.variables[column].checkpoint()
81.1.20 by Gustavo Niemeyer
- Implemented support for expressions as lazy values in the store!
815
816
9 by Gustavo Niemeyer
- Merged Connection.execute_expr() into Connection.execute().
817
class ResultSet(object):
142.3.20 by Christopher Armstrong
massive doc improvements for store.py
818
    """The representation of the results of a query.
819
820
    Note that having an instance of this class does not indicate that
821
    a database query has necessarily been made. Database queries are
822
    put off until absolutely necessary.
823
824
    Generally these should not be constructed directly, but instead
825
    retrieved from calls to L{Store.find}.
826
    """
95.3.20 by Gustavo Niemeyer
Initial implementation of ResultSet.union(). order_by() doesn't yet
827
    def __init__(self, store, cls_spec_info,
828
                 where=Undef, tables=Undef, select=Undef):
44 by Gustavo Niemeyer
- Decoupled most of the logic from Reference into Relation, and
829
        self._store = store
81.1.5 by Gustavo Niemeyer
Implemented support for using a tuple of classes as the first argument of
830
        self._cls_spec_info = cls_spec_info
24 by Gustavo Niemeyer
- Removed AutoTable. It's now implemented internally in statements.
831
        self._where = where
81.1.3 by Gustavo Niemeyer
- Redesigned join expressions to handle more complex cases.
832
        self._tables = tables
95.3.20 by Gustavo Niemeyer
Initial implementation of ResultSet.union(). order_by() doesn't yet
833
        self._select = select
95.3.4 by Gustavo Niemeyer
Forgot to commit changes of Store to support default ordering.
834
        self._order_by = getattr(cls_spec_info, "default_order", Undef)
81.1.6 by Gustavo Niemeyer
- ResultSet is now mutable. Running order_by() will change the instance
835
        self._offset = Undef
836
        self._limit = Undef
837
        self._distinct = False
838
839
    def copy(self):
142.3.20 by Christopher Armstrong
massive doc improvements for store.py
840
        """Return a copy of this ResultSet object, with the same configuration.
106.1.3 by Gustavo Niemeyer
- Refactored common methods of BoundReferenceSet and
841
        """
81.1.6 by Gustavo Niemeyer
- ResultSet is now mutable. Running order_by() will change the instance
842
        result_set = object.__new__(self.__class__)
843
        result_set.__dict__.update(self.__dict__)
844
        return result_set
845
112 by Gustavo Niemeyer
Removed support for adaptation. In practice we were not able
846
    def config(self, distinct=None, offset=None, limit=None):
106.1.3 by Gustavo Niemeyer
- Refactored common methods of BoundReferenceSet and
847
        """Configure this result object in-place. All parameters are optional.
848
849
        @param distinct: Boolean enabling/disabling usage of the DISTINCT
850
            keyword in the query made.
851
        @param offset: Offset where results will start to be retrieved
852
            from the result set.
853
        @param limit: Limit the number of objects retrieved from the
854
            result set.
855
856
        @return: self (not a copy).
857
        """
858
81.1.6 by Gustavo Niemeyer
- ResultSet is now mutable. Running order_by() will change the instance
859
        if distinct is not None:
860
            self._distinct = distinct
861
        if offset is not None:
862
            self._offset = offset
863
        if limit is not None:
864
            self._limit = limit
865
        return self
866
95.3.20 by Gustavo Niemeyer
Initial implementation of ResultSet.union(). order_by() doesn't yet
867
    def _get_select(self):
868
        if self._select is not Undef:
95.3.32 by Gustavo Niemeyer
- Improved a bit support for unions.
869
            if self._order_by is not Undef:
870
                self._select.order_by = self._order_by
871
            if self._limit is not Undef: # XXX UNTESTED!
872
                self._select.limit = self._limit
873
            if self._offset is not Undef: # XXX UNTESTED!
874
                self._select.offset = self._offset
95.3.20 by Gustavo Niemeyer
Initial implementation of ResultSet.union(). order_by() doesn't yet
875
            return self._select
81.1.5 by Gustavo Niemeyer
Implemented support for using a tuple of classes as the first argument of
876
        if type(self._cls_spec_info) is tuple:
877
            columns = []
878
            default_tables = []
879
            for cls_info in self._cls_spec_info:
880
                columns.append(cls_info.columns)
881
                default_tables.append(cls_info.table)
882
        else:
883
            columns = self._cls_spec_info.columns
884
            default_tables = self._cls_spec_info.table
885
        return Select(columns, self._where, self._tables, default_tables,
81.1.6 by Gustavo Niemeyer
- ResultSet is now mutable. Running order_by() will change the instance
886
                      self._order_by, offset=self._offset, limit=self._limit,
887
                      distinct=self._distinct)
81.1.5 by Gustavo Niemeyer
Implemented support for using a tuple of classes as the first argument of
888
106.1.3 by Gustavo Niemeyer
- Refactored common methods of BoundReferenceSet and
889
    def _load_objects(self, result, values):
112 by Gustavo Niemeyer
Removed support for adaptation. In practice we were not able
890
        return self._store._load_objects(self._cls_spec_info, result, values)
106.1.3 by Gustavo Niemeyer
- Refactored common methods of BoundReferenceSet and
891
7 by Gustavo Niemeyer
- Started implementing Store.
892
    def __iter__(self):
142.3.20 by Christopher Armstrong
massive doc improvements for store.py
893
        """Iterate the results of the query.
894
        """
81.1.5 by Gustavo Niemeyer
Implemented support for using a tuple of classes as the first argument of
895
        result = self._store._connection.execute(self._get_select())
40 by Gustavo Niemeyer
- Implemented result.to_kind(). It'll be used soon to process
896
        for values in result:
106.1.3 by Gustavo Niemeyer
- Refactored common methods of BoundReferenceSet and
897
            yield self._load_objects(result, values)
7 by Gustavo Niemeyer
- Started implementing Store.
898
71 by Gustavo Niemeyer
Implemented support for slicing sliced result sets.
899
    def __getitem__(self, index):
142.3.20 by Christopher Armstrong
massive doc improvements for store.py
900
        """Get an individual item by offset, or a range of items by slice.
901
902
        If a slice is used, a new L{ResultSet} will be return
903
        appropriately modified with OFFSET and LIMIT clauses.
904
        """
72 by Gustavo Niemeyer
- Implemented support for non-slice ResultSet indexing.
905
        if isinstance(index, (int, long)):
906
            if index == 0:
907
                result_set = self
908
            else:
909
                if self._offset is not Undef:
910
                    index += self._offset
81.1.6 by Gustavo Niemeyer
- ResultSet is now mutable. Running order_by() will change the instance
911
                result_set = self.copy()
912
                result_set.config(offset=index, limit=1)
72 by Gustavo Niemeyer
- Implemented support for non-slice ResultSet indexing.
913
            obj = result_set.any()
914
            if obj is None:
915
                raise IndexError("Index out of range")
916
            return obj
917
71 by Gustavo Niemeyer
Implemented support for slicing sliced result sets.
918
        if not isinstance(index, slice):
81.1.9 by Gustavo Niemeyer
- Renamed UnsupportedDatabaseError to DatabaseModuleError.
919
            raise IndexError("Can't index ResultSets with %r" % (index,))
71 by Gustavo Niemeyer
Implemented support for slicing sliced result sets.
920
        if index.step is not None:
81.1.9 by Gustavo Niemeyer
- Renamed UnsupportedDatabaseError to DatabaseModuleError.
921
            raise IndexError("Stepped slices not yet supported: %r"
71 by Gustavo Niemeyer
Implemented support for slicing sliced result sets.
922
                             % (index.step,))
72 by Gustavo Niemeyer
- Implemented support for non-slice ResultSet indexing.
923
71 by Gustavo Niemeyer
Implemented support for slicing sliced result sets.
924
        offset = self._offset
925
        limit = self._limit
72 by Gustavo Niemeyer
- Implemented support for non-slice ResultSet indexing.
926
71 by Gustavo Niemeyer
Implemented support for slicing sliced result sets.
927
        if index.start is not None:
928
            if offset is Undef:
929
                offset = index.start
930
            else:
931
                offset += index.start
932
            if limit is not Undef:
933
                limit = max(0, limit - index.start)
72 by Gustavo Niemeyer
- Implemented support for non-slice ResultSet indexing.
934
71 by Gustavo Niemeyer
Implemented support for slicing sliced result sets.
935
        if index.stop is not None:
936
            if index.start is None:
937
                new_limit = index.stop
938
            else:
939
                new_limit = index.stop - index.start
940
            if limit is Undef or limit > new_limit:
941
                limit = new_limit
72 by Gustavo Niemeyer
- Implemented support for non-slice ResultSet indexing.
942
81.1.6 by Gustavo Niemeyer
- ResultSet is now mutable. Running order_by() will change the instance
943
        return self.copy().config(offset=offset, limit=limit)
81.1.5 by Gustavo Niemeyer
Implemented support for using a tuple of classes as the first argument of
944
68 by Gustavo Niemeyer
Added meaningful implementations of ResultSet.{any,one,first,last}().
945
    def any(self):
946
        """Return a single item from the result set.
947
948
        See also one(), first(), and last().
949
        """
81.1.5 by Gustavo Niemeyer
Implemented support for using a tuple of classes as the first argument of
950
        select = self._get_select()
951
        select.limit = 1
68 by Gustavo Niemeyer
Added meaningful implementations of ResultSet.{any,one,first,last}().
952
        result = self._store._connection.execute(select)
953
        values = result.get_one()
954
        if values:
106.1.3 by Gustavo Niemeyer
- Refactored common methods of BoundReferenceSet and
955
            return self._load_objects(result, values)
68 by Gustavo Niemeyer
Added meaningful implementations of ResultSet.{any,one,first,last}().
956
        return None
957
958
    def first(self):
959
        """Return the first item from an ordered result set.
960
961
        Will raise UnorderedError if the result set isn't ordered.
962
963
        See also last(), one(), and any().
964
        """
965
        if self._order_by is Undef:
966
            raise UnorderedError("Can't use first() on unordered result set")
967
        return self.any()
968
969
    def last(self):
970
        """Return the last item from an ordered result set.
971
972
        Will raise UnorderedError if the result set isn't ordered.
973
974
        See also first(), one(), and any().
975
        """
976
        if self._order_by is Undef:
977
            raise UnorderedError("Can't use last() on unordered result set")
72 by Gustavo Niemeyer
- Implemented support for non-slice ResultSet indexing.
978
        if self._limit is not Undef:
81.1.9 by Gustavo Niemeyer
- Renamed UnsupportedDatabaseError to DatabaseModuleError.
979
            raise FeatureError("Can't use last() with a slice "
980
                               "of defined stop index")
81.1.5 by Gustavo Niemeyer
Implemented support for using a tuple of classes as the first argument of
981
        select = self._get_select()
982
        select.offset = Undef
983
        select.limit = 1
984
        select.order_by = []
68 by Gustavo Niemeyer
Added meaningful implementations of ResultSet.{any,one,first,last}().
985
        for expr in self._order_by:
986
            if isinstance(expr, Desc):
81.1.5 by Gustavo Niemeyer
Implemented support for using a tuple of classes as the first argument of
987
                select.order_by.append(expr.expr)
68 by Gustavo Niemeyer
Added meaningful implementations of ResultSet.{any,one,first,last}().
988
            elif isinstance(expr, Asc):
81.1.5 by Gustavo Niemeyer
Implemented support for using a tuple of classes as the first argument of
989
                select.order_by.append(Desc(expr.expr))
68 by Gustavo Niemeyer
Added meaningful implementations of ResultSet.{any,one,first,last}().
990
            else:
81.1.5 by Gustavo Niemeyer
Implemented support for using a tuple of classes as the first argument of
991
                select.order_by.append(Desc(expr))
68 by Gustavo Niemeyer
Added meaningful implementations of ResultSet.{any,one,first,last}().
992
        result = self._store._connection.execute(select)
993
        values = result.get_one()
994
        if values:
106.1.3 by Gustavo Niemeyer
- Refactored common methods of BoundReferenceSet and
995
            return self._load_objects(result, values)
68 by Gustavo Niemeyer
Added meaningful implementations of ResultSet.{any,one,first,last}().
996
        return None
997
7 by Gustavo Niemeyer
- Started implementing Store.
998
    def one(self):
68 by Gustavo Niemeyer
Added meaningful implementations of ResultSet.{any,one,first,last}().
999
        """Return one item from a result set containing at most one item.
1000
1001
        Will raise NotOneError if the result set contains more than one item.
1002
1003
        See also first(), one(), and any().
1004
        """
81.1.5 by Gustavo Niemeyer
Implemented support for using a tuple of classes as the first argument of
1005
        select = self._get_select()
1006
        # limit could be 1 due to slicing, for instance.
1007
        if select.limit is not Undef and select.limit > 2:
1008
            select.limit = 2
44 by Gustavo Niemeyer
- Decoupled most of the logic from Reference into Relation, and
1009
        result = self._store._connection.execute(select)
50 by Gustavo Niemeyer
- Renamed Result.fetch_one/all to get_one/all.
1010
        values = result.get_one()
68 by Gustavo Niemeyer
Added meaningful implementations of ResultSet.{any,one,first,last}().
1011
        if result.get_one():
1012
            raise NotOneError("one() used with more than one result available")
10 by Gustavo Niemeyer
- Renamed ClassInfo.primary_key to pk_prop_insts and introduced
1013
        if values:
106.1.3 by Gustavo Niemeyer
- Refactored common methods of BoundReferenceSet and
1014
            return self._load_objects(result, values)
10 by Gustavo Niemeyer
- Renamed ClassInfo.primary_key to pk_prop_insts and introduced
1015
        return None
7 by Gustavo Niemeyer
- Started implementing Store.
1016
1017
    def order_by(self, *args):
142.3.20 by Christopher Armstrong
massive doc improvements for store.py
1018
        """Specify the ordering of the results.
1019
1020
        The query will be modified appropriately with an ORDER BY clause.
1021
142.3.22 by Christopher Armstrong
mention Asc and Desc in the order_by docstring
1022
        Ascending and descending order can be specified by wrapping
1023
        the columns in L{Asc} and L{Desc}.
1024
142.3.20 by Christopher Armstrong
massive doc improvements for store.py
1025
        @param args: One or more L{storm.expr.Column} objects.
1026
        """
72 by Gustavo Niemeyer
- Implemented support for non-slice ResultSet indexing.
1027
        if self._offset is not Undef or self._limit is not Undef:
81.1.9 by Gustavo Niemeyer
- Renamed UnsupportedDatabaseError to DatabaseModuleError.
1028
            raise FeatureError("Can't reorder a sliced result set")
95.3.70 by Gustavo Niemeyer
- Fixed bug in postgres compilation of set expressions, reported by Kiko.
1029
        self._order_by = args or Undef
81.1.6 by Gustavo Niemeyer
- ResultSet is now mutable. Running order_by() will change the instance
1030
        return self
9 by Gustavo Niemeyer
- Merged Connection.execute_expr() into Connection.execute().
1031
1032
    def remove(self):
142.3.20 by Christopher Armstrong
massive doc improvements for store.py
1033
        """Remove all rows represented by this ResultSet from the database.
1034
1035
        This is done efficiently with a DELETE statement, so objects
1036
        are not actually loaded into Python.
1037
        """
72 by Gustavo Niemeyer
- Implemented support for non-slice ResultSet indexing.
1038
        if self._offset is not Undef or self._limit is not Undef:
81.1.9 by Gustavo Niemeyer
- Renamed UnsupportedDatabaseError to DatabaseModuleError.
1039
            raise FeatureError("Can't remove a sliced result set")
81.1.6 by Gustavo Niemeyer
- ResultSet is now mutable. Running order_by() will change the instance
1040
        if type(self._cls_spec_info) is tuple:
81.1.9 by Gustavo Niemeyer
- Renamed UnsupportedDatabaseError to DatabaseModuleError.
1041
            raise FeatureError("Removing not yet supported with tuple finds")
95.3.20 by Gustavo Niemeyer
Initial implementation of ResultSet.union(). order_by() doesn't yet
1042
        if self._select is not Undef:
95.3.75 by Gustavo Niemeyer
Fixing typos, as reported by Jamu.
1043
            raise FeatureError("Removing isn't supported with "
95.3.20 by Gustavo Niemeyer
Initial implementation of ResultSet.union(). order_by() doesn't yet
1044
                               "set expressions (unions, etc)")
44 by Gustavo Niemeyer
- Decoupled most of the logic from Reference into Relation, and
1045
        self._store._connection.execute(Delete(self._where,
81.1.5 by Gustavo Niemeyer
Implemented support for using a tuple of classes as the first argument of
1046
                                               self._cls_spec_info.table),
44 by Gustavo Niemeyer
- Decoupled most of the logic from Reference into Relation, and
1047
                                        noresult=True)
7 by Gustavo Niemeyer
- Started implementing Store.
1048
73 by Gustavo Niemeyer
Now aggregating functions (max(), min(), etc) will pass through the
1049
    def _aggregate(self, expr, column=None):
81.1.6 by Gustavo Niemeyer
- ResultSet is now mutable. Running order_by() will change the instance
1050
        if type(self._cls_spec_info) is tuple:
1051
            default_tables = [cls_info.table
1052
                              for cls_info in self._cls_spec_info]
1053
        else:
1054
            default_tables = self._cls_spec_info.table
95.3.59 by Gustavo Niemeyer
Fixed behavior of ResultSet.count() with set expressions,
1055
        if self._select is Undef:
1056
            select = Select(expr, self._where, self._tables, default_tables)
1057
        else:
1058
            select = Select(expr, tables=Alias(self._select))
73 by Gustavo Niemeyer
Now aggregating functions (max(), min(), etc) will pass through the
1059
        result = self._store._connection.execute(select)
1060
        value = result.get_one()[0]
163 by Gustavo Niemeyer
Merged patch from Johan Dahlin [r=jkakar,niemeyer] [f=126001]
1061
        variable_factory = getattr(column, "variable_factory", None)
1062
        if variable_factory:
1063
            variable = variable_factory()
73 by Gustavo Niemeyer
Now aggregating functions (max(), min(), etc) will pass through the
1064
            result.set_variable(variable, value)
1065
            return variable.get()
163 by Gustavo Niemeyer
Merged patch from Johan Dahlin [r=jkakar,niemeyer] [f=126001]
1066
        return value
1067
1068
    def count(self, expr=Undef, distinct=False):
142.3.20 by Christopher Armstrong
massive doc improvements for store.py
1069
        """Get the number of objects represented by this ResultSet."""
163 by Gustavo Niemeyer
Merged patch from Johan Dahlin [r=jkakar,niemeyer] [f=126001]
1070
        return int(self._aggregate(Count(expr, distinct)))
1071
1072
    def max(self, expr):
142.3.20 by Christopher Armstrong
massive doc improvements for store.py
1073
        """Get the highest value from an expression."""
163 by Gustavo Niemeyer
Merged patch from Johan Dahlin [r=jkakar,niemeyer] [f=126001]
1074
        return self._aggregate(Max(expr), expr)
1075
1076
    def min(self, expr):
142.3.20 by Christopher Armstrong
massive doc improvements for store.py
1077
        """Get the lowest value from an expression."""
163 by Gustavo Niemeyer
Merged patch from Johan Dahlin [r=jkakar,niemeyer] [f=126001]
1078
        return self._aggregate(Min(expr), expr)
1079
1080
    def avg(self, expr):
142.3.20 by Christopher Armstrong
massive doc improvements for store.py
1081
        """Get the average value from an expression."""
163 by Gustavo Niemeyer
Merged patch from Johan Dahlin [r=jkakar,niemeyer] [f=126001]
1082
        value = self._aggregate(Avg(expr))
92.1.2 by Jamshed Kakar
- Fixed float conversion bug in ResultSet.avg(). Updated
1083
        if value is None:
1084
            return value
1085
        return float(value)
73 by Gustavo Niemeyer
Now aggregating functions (max(), min(), etc) will pass through the
1086
163 by Gustavo Niemeyer
Merged patch from Johan Dahlin [r=jkakar,niemeyer] [f=126001]
1087
    def sum(self, expr):
142.3.20 by Christopher Armstrong
massive doc improvements for store.py
1088
        """Get the sum of all values in an expression."""
163 by Gustavo Niemeyer
Merged patch from Johan Dahlin [r=jkakar,niemeyer] [f=126001]
1089
        return self._aggregate(Sum(expr), expr)
44 by Gustavo Niemeyer
- Decoupled most of the logic from Reference into Relation, and
1090
75 by Gustavo Niemeyer
Implemented support for multiple arguments in ResultSet.values().
1091
    def values(self, *columns):
142.3.20 by Christopher Armstrong
massive doc improvements for store.py
1092
        """Retrieve only the specified columns.
1093
1094
        This does not load full objects from the database into Python.
1095
1096
        @param columns: One or more L{storm.expr.Column} objects whose
1097
            values will be fetched.
1098
        @return: An iterator of tuples of the values for each column
1099
            from each matching row in the database.
1100
        """
75 by Gustavo Niemeyer
Implemented support for multiple arguments in ResultSet.values().
1101
        if not columns:
81.1.9 by Gustavo Niemeyer
- Renamed UnsupportedDatabaseError to DatabaseModuleError.
1102
            raise FeatureError("values() takes at least one column "
1103
                               "as argument")
81.1.5 by Gustavo Niemeyer
Implemented support for using a tuple of classes as the first argument of
1104
        select = self._get_select()
1105
        select.columns = columns
74 by Gustavo Niemeyer
Implemented ResultSet.values().
1106
        result = self._store._connection.execute(select)
75 by Gustavo Niemeyer
Implemented support for multiple arguments in ResultSet.values().
1107
        if len(columns) == 1:
1108
            variable = columns[0].variable_factory()
1109
            for values in result:
1110
                result.set_variable(variable, values[0])
1111
                yield variable.get()
1112
        else:
1113
            variables = [column.variable_factory() for column in columns]
1114
            for values in result:
1115
                for variable, value in zip(variables, values):
1116
                    result.set_variable(variable, value)
1117
                yield tuple(variable.get() for variable in variables)
74 by Gustavo Niemeyer
Implemented ResultSet.values().
1118
44 by Gustavo Niemeyer
- Decoupled most of the logic from Reference into Relation, and
1119
    def set(self, *args, **kwargs):
99.1.1 by Gustavo Niemeyer
- Now ResultSet.set() will set only the involved attributes to
1120
        """Update objects in the result set with the given arguments.
1121
1122
        This method will update all objects in the current result set
1123
        to match expressions given as equalities or keyword arguments.
1124
        These objects may still be in the database (an UPDATE is issued)
1125
        or may be cached.
1126
1127
        For instance, C{result.set(Class.attr1 == 1, attr2=2)} will set
1128
        C{attr1} to 1 and C{attr2} to 2, on all matching objects.
1129
        """
1130
81.1.6 by Gustavo Niemeyer
- ResultSet is now mutable. Running order_by() will change the instance
1131
        if type(self._cls_spec_info) is tuple:
95.3.75 by Gustavo Niemeyer
Fixing typos, as reported by Jamu.
1132
            raise FeatureError("Setting isn't supported with tuple finds")
95.3.20 by Gustavo Niemeyer
Initial implementation of ResultSet.union(). order_by() doesn't yet
1133
        if self._select is not Undef:
95.3.75 by Gustavo Niemeyer
Fixing typos, as reported by Jamu.
1134
            raise FeatureError("Setting isn't supported with "
95.3.20 by Gustavo Niemeyer
Initial implementation of ResultSet.union(). order_by() doesn't yet
1135
                               "set expressions (unions, etc)")
81.1.6 by Gustavo Niemeyer
- ResultSet is now mutable. Running order_by() will change the instance
1136
44 by Gustavo Niemeyer
- Decoupled most of the logic from Reference into Relation, and
1137
        if not (args or kwargs):
1138
            return
1139
58 by Gustavo Niemeyer
- Major refactoring of the typing system. Kinds are gone. Now Variables
1140
        changes = {}
81.1.5 by Gustavo Niemeyer
Implemented support for using a tuple of classes as the first argument of
1141
        cls = self._cls_spec_info.cls
44 by Gustavo Niemeyer
- Decoupled most of the logic from Reference into Relation, and
1142
99.1.1 by Gustavo Niemeyer
- Now ResultSet.set() will set only the involved attributes to
1143
        # For now only "Class.attr == var" is supported in args.
44 by Gustavo Niemeyer
- Decoupled most of the logic from Reference into Relation, and
1144
        for expr in args:
1145
            if (not isinstance(expr, Eq) or
1146
                not isinstance(expr.expr1, Column) or
58 by Gustavo Niemeyer
- Major refactoring of the typing system. Kinds are gone. Now Variables
1147
                not isinstance(expr.expr2, (Column, Variable))):
81.1.9 by Gustavo Niemeyer
- Renamed UnsupportedDatabaseError to DatabaseModuleError.
1148
                raise FeatureError("Unsupported set expression: %r" %
1149
                                   repr(expr))
58 by Gustavo Niemeyer
- Major refactoring of the typing system. Kinds are gone. Now Variables
1150
            changes[expr.expr1] = expr.expr2
44 by Gustavo Niemeyer
- Decoupled most of the logic from Reference into Relation, and
1151
1152
        for key, value in kwargs.items():
1153
            column = getattr(cls, key)
1154
            if value is None:
58 by Gustavo Niemeyer
- Major refactoring of the typing system. Kinds are gone. Now Variables
1155
                changes[column] = None
44 by Gustavo Niemeyer
- Decoupled most of the logic from Reference into Relation, and
1156
            elif isinstance(value, Expr):
1157
                if not isinstance(value, Column):
81.1.9 by Gustavo Niemeyer
- Renamed UnsupportedDatabaseError to DatabaseModuleError.
1158
                    raise FeatureError("Unsupported set expression: %r" %
1159
                                       repr(value))
58 by Gustavo Niemeyer
- Major refactoring of the typing system. Kinds are gone. Now Variables
1160
                changes[column] = value
44 by Gustavo Niemeyer
- Decoupled most of the logic from Reference into Relation, and
1161
            else:
58 by Gustavo Niemeyer
- Major refactoring of the typing system. Kinds are gone. Now Variables
1162
                changes[column] = column.variable_factory(value=value)
44 by Gustavo Niemeyer
- Decoupled most of the logic from Reference into Relation, and
1163
81.1.5 by Gustavo Niemeyer
Implemented support for using a tuple of classes as the first argument of
1164
        expr = Update(changes, self._where, self._cls_spec_info.table)
142.2.2 by Gustavo Niemeyer
- When an object died in memory, ResultSet.cached() wasn't returning
1165
        self._store.execute(expr, noresult=True)
44 by Gustavo Niemeyer
- Decoupled most of the logic from Reference into Relation, and
1166
1167
        try:
196.1.5 by Bernd Dorn
added more tests and changed according to review from niemeyer
1168
            cached = self.cached()
44 by Gustavo Niemeyer
- Decoupled most of the logic from Reference into Relation, and
1169
        except CompileError:
196.1.3 by Bernd Dorn
more rename to alive
1170
            for obj_info in self._store._iter_alive():
99.1.1 by Gustavo Niemeyer
- Now ResultSet.set() will set only the involved attributes to
1171
                for column in changes:
1172
                    obj_info.variables[column].set(AutoReload)
44 by Gustavo Niemeyer
- Decoupled most of the logic from Reference into Relation, and
1173
        else:
58 by Gustavo Niemeyer
- Major refactoring of the typing system. Kinds are gone. Now Variables
1174
            changes = changes.items()
44 by Gustavo Niemeyer
- Decoupled most of the logic from Reference into Relation, and
1175
            for obj in cached:
58 by Gustavo Niemeyer
- Major refactoring of the typing system. Kinds are gone. Now Variables
1176
                for column, value in changes:
1177
                    variables = get_obj_info(obj).variables
1178
                    if value is None:
1179
                        pass
1180
                    elif isinstance(value, Variable):
1181
                        value = value.get()
44 by Gustavo Niemeyer
- Decoupled most of the logic from Reference into Relation, and
1182
                    else:
58 by Gustavo Niemeyer
- Major refactoring of the typing system. Kinds are gone. Now Variables
1183
                        value = variables[value].get()
1184
                    variables[column].set(value)
1185
                    variables[column].checkpoint()
44 by Gustavo Niemeyer
- Decoupled most of the logic from Reference into Relation, and
1186
196.1.5 by Bernd Dorn
added more tests and changed according to review from niemeyer
1187
    def cached(self):
196.1.6 by Gustavo Niemeyer
These changes address a few issues and perform a few stylistic changes
1188
        """Return matching objects from the cache for the current query."""
81.1.6 by Gustavo Niemeyer
- ResultSet is now mutable. Running order_by() will change the instance
1189
        if type(self._cls_spec_info) is tuple:
196.1.6 by Gustavo Niemeyer
These changes address a few issues and perform a few stylistic changes
1190
            raise FeatureError("Cache finds not supported with tuples")
81.1.8 by Gustavo Niemeyer
Fix check in ResultSet.cached().
1191
        if self._tables is not Undef:
196.1.6 by Gustavo Niemeyer
These changes address a few issues and perform a few stylistic changes
1192
            raise FeatureError("Cache finds not supported with custom tables")
44 by Gustavo Niemeyer
- Decoupled most of the logic from Reference into Relation, and
1193
        if self._where is Undef:
1194
            match = None
1195
        else:
142.1.7 by Gustavo Niemeyer
Sanitized return values from Compile. Now it always return only
1196
            match = compile_python.get_matcher(self._where)
81.1.5 by Gustavo Niemeyer
Implemented support for using a tuple of classes as the first argument of
1197
            name_to_column = dict((column.name, column)
1198
                                  for column in self._cls_spec_info.columns)
44 by Gustavo Niemeyer
- Decoupled most of the logic from Reference into Relation, and
1199
            def get_column(name, name_to_column=name_to_column):
62 by Gustavo Niemeyer
Changed the way that objects are keyed. Now they use the obj_info,
1200
                return obj_info.variables[name_to_column[name]].get()
44 by Gustavo Niemeyer
- Decoupled most of the logic from Reference into Relation, and
1201
        objects = []
81.1.5 by Gustavo Niemeyer
Implemented support for using a tuple of classes as the first argument of
1202
        cls = self._cls_spec_info.cls
196.1.3 by Bernd Dorn
more rename to alive
1203
        for obj_info in self._store._iter_alive():
99.1.1 by Gustavo Niemeyer
- Now ResultSet.set() will set only the involved attributes to
1204
            try:
1205
                if (obj_info.cls_info is self._cls_spec_info and
1206
                    (match is None or match(get_column))):
142.2.2 by Gustavo Niemeyer
- When an object died in memory, ResultSet.cached() wasn't returning
1207
                    objects.append(self._store._get_object(obj_info))
99.1.1 by Gustavo Niemeyer
- Now ResultSet.set() will set only the involved attributes to
1208
            except LostObjectError:
142.2.2 by Gustavo Niemeyer
- When an object died in memory, ResultSet.cached() wasn't returning
1209
                pass # This may happen when resolving lazy values
1210
                     # in get_column().
44 by Gustavo Niemeyer
- Decoupled most of the logic from Reference into Relation, and
1211
        return objects
62 by Gustavo Niemeyer
Changed the way that objects are keyed. Now they use the obj_info,
1212
95.3.60 by Gustavo Niemeyer
Implemented support for ResultSet.difference()/intersection().
1213
    def _set_expr(self, expr_cls, other, all=False):
1214
        if self._cls_spec_info != other._cls_spec_info:
1215
            raise FeatureError("Incompatible results for set operation")
1216
1217
        expr = expr_cls(self._get_select(), other._get_select(), all=all)
1218
        return ResultSet(self._store, self._cls_spec_info, select=expr)
1219
95.3.20 by Gustavo Niemeyer
Initial implementation of ResultSet.union(). order_by() doesn't yet
1220
    def union(self, other, all=False):
142.3.20 by Christopher Armstrong
massive doc improvements for store.py
1221
        """Get the L{Union} of this result set and another.
1222
1223
        @param all: If True, include duplicates.
1224
        """
95.3.20 by Gustavo Niemeyer
Initial implementation of ResultSet.union(). order_by() doesn't yet
1225
        if isinstance(other, EmptyResultSet):
1226
            return self
95.3.60 by Gustavo Niemeyer
Implemented support for ResultSet.difference()/intersection().
1227
        return self._set_expr(Union, other, all)
1228
1229
    def difference(self, other, all=False):
142.3.20 by Christopher Armstrong
massive doc improvements for store.py
1230
        """Get the difference, using L{Except}, of this result set and another.
1231
1232
        @param all: If True, include duplicates.
1233
        """
95.3.60 by Gustavo Niemeyer
Implemented support for ResultSet.difference()/intersection().
1234
        if isinstance(other, EmptyResultSet):
1235
            return self
1236
        return self._set_expr(Except, other, all)
1237
1238
    def intersection(self, other, all=False):
142.3.20 by Christopher Armstrong
massive doc improvements for store.py
1239
        """Get the L{Intersection} of this result set and another.
1240
1241
        @param all: If True, include duplicates.
1242
        """
95.3.60 by Gustavo Niemeyer
Implemented support for ResultSet.difference()/intersection().
1243
        if isinstance(other, EmptyResultSet):
1244
            return other
1245
        return self._set_expr(Intersect, other, all)
95.3.20 by Gustavo Niemeyer
Initial implementation of ResultSet.union(). order_by() doesn't yet
1246
59.1.3 by Jamshed Kakar
- Introduced Result.close() and Connection.close().
1247
92.1.1 by Jamshed Kakar
- Implemented EmptyResultSet.
1248
class EmptyResultSet(object):
142.3.20 by Christopher Armstrong
massive doc improvements for store.py
1249
    """An object that looks like a L{ResultSet} but represents no rows.
1250
1251
    This is convenient for application developers who want to provide
1252
    a method which is guaranteed to return a L{ResultSet}-like object
1253
    but which, in certain cases, knows there is no point in querying
1254
    the database. For example::
1255
1256
        def get_people(self, ids):
1257
            if not ids:
1258
                return EmptyResultSet()
1259
            return store.find(People, People.id.is_in(ids))
1260
1261
    The methods on EmptyResultSet (L{one}, L{config}, L{union}, etc)
1262
    are meant to emulate a L{ResultSet} which has matched no rows.
1263
    """
92.1.1 by Jamshed Kakar
- Implemented EmptyResultSet.
1264
92.1.2 by Jamshed Kakar
- Fixed float conversion bug in ResultSet.avg(). Updated
1265
    def __init__(self, ordered=False):
1266
        self._order_by = ordered
92.1.1 by Jamshed Kakar
- Implemented EmptyResultSet.
1267
95.3.20 by Gustavo Niemeyer
Initial implementation of ResultSet.union(). order_by() doesn't yet
1268
    def _get_select(self):
1269
        return Select(SQLRaw("1"), SQLRaw("1 = 2"))
1270
92.1.1 by Jamshed Kakar
- Implemented EmptyResultSet.
1271
    def copy(self):
92.1.2 by Jamshed Kakar
- Fixed float conversion bug in ResultSet.avg(). Updated
1272
        result = EmptyResultSet(self._order_by)
92.1.1 by Jamshed Kakar
- Implemented EmptyResultSet.
1273
        return result
1274
1275
    def config(self, distinct=None, offset=None, limit=None):
1276
        pass
1277
1278
    def __iter__(self):
92.1.2 by Jamshed Kakar
- Fixed float conversion bug in ResultSet.avg(). Updated
1279
        return
1280
        yield None
92.1.1 by Jamshed Kakar
- Implemented EmptyResultSet.
1281
1282
    def __getitem__(self, index):
92.1.2 by Jamshed Kakar
- Fixed float conversion bug in ResultSet.avg(). Updated
1283
        return self.copy()
92.1.1 by Jamshed Kakar
- Implemented EmptyResultSet.
1284
1285
    def any(self):
1286
        return None
1287
1288
    def first(self):
1289
        if self._order_by:
1290
            return None
1291
        raise UnorderedError("Can't use first() on unordered result set")
1292
1293
    def last(self):
1294
        if self._order_by:
1295
            return None
1296
        raise UnorderedError("Can't use last() on unordered result set")
1297
1298
    def one(self):
1299
        return None
1300
1301
    def order_by(self, *args):
1302
        self._order_by = True
1303
        return self
1304
1305
    def remove(self):
1306
        pass
1307
93.1.1 by Christopher Armstrong
make EmptyResultSet.count take the same kind of args as the real ResultSet.count
1308
    def count(self, column=Undef, distinct=False):
92.1.1 by Jamshed Kakar
- Implemented EmptyResultSet.
1309
        return 0
1310
1311
    def max(self, column):
1312
        return None
1313
1314
    def min(self, column):
1315
        return None
1316
1317
    def avg(self, column):
92.1.2 by Jamshed Kakar
- Fixed float conversion bug in ResultSet.avg(). Updated
1318
        return None
92.1.1 by Jamshed Kakar
- Implemented EmptyResultSet.
1319
1320
    def sum(self, column):
1321
        return None
1322
1323
    def values(self, *columns):
1324
        if not columns:
1325
            raise FeatureError("values() takes at least one column "
1326
                               "as argument")
92.1.2 by Jamshed Kakar
- Fixed float conversion bug in ResultSet.avg(). Updated
1327
        return
1328
        yield None
92.1.1 by Jamshed Kakar
- Implemented EmptyResultSet.
1329
1330
    def set(self, *args, **kwargs):
1331
        pass
1332
196.1.5 by Bernd Dorn
added more tests and changed according to review from niemeyer
1333
    def cached(self):
92.1.1 by Jamshed Kakar
- Implemented EmptyResultSet.
1334
        return []
1335
95.3.20 by Gustavo Niemeyer
Initial implementation of ResultSet.union(). order_by() doesn't yet
1336
    def union(self, other):
1337
        if isinstance(other, EmptyResultSet):
1338
            return self
1339
        return other.union(self)
1340
95.3.60 by Gustavo Niemeyer
Implemented support for ResultSet.difference()/intersection().
1341
    def difference(self, other):
1342
        return self
1343
1344
    def intersection(self, other):
1345
        return self
1346
92.1.1 by Jamshed Kakar
- Implemented EmptyResultSet.
1347
81.1.3 by Gustavo Niemeyer
- Redesigned join expressions to handle more complex cases.
1348
class TableSet(object):
142.3.20 by Christopher Armstrong
massive doc improvements for store.py
1349
    """The representation of a set of tables which can be queried at once.
1350
1351
    This will typically be constructed by a call to L{Store.using}.
1352
    """
165 by Gustavo Niemeyer
Applying whitespace normalization patch by Johan Dahlin. [trivial]
1353
81.1.3 by Gustavo Niemeyer
- Redesigned join expressions to handle more complex cases.
1354
    def __init__(self, store, tables):
1355
        self._store = store
1356
        self._tables = tables
1357
81.1.5 by Gustavo Niemeyer
Implemented support for using a tuple of classes as the first argument of
1358
    def find(self, cls_spec, *args, **kwargs):
142.3.20 by Christopher Armstrong
massive doc improvements for store.py
1359
        """Perform a query on the previously specified tables.
1360
142.3.21 by Christopher Armstrong
improvements to store documentation on Gustavo's advice, thanks!
1361
        This is identical to L{Store.find} except that the tables are
1362
        explicitly specified instead of relying on inference.
142.3.20 by Christopher Armstrong
massive doc improvements for store.py
1363
1364
        @return: A L{ResultSet}.
1365
        """
81.1.3 by Gustavo Niemeyer
- Redesigned join expressions to handle more complex cases.
1366
        self._store.flush()
81.1.5 by Gustavo Niemeyer
Implemented support for using a tuple of classes as the first argument of
1367
        if type(cls_spec) is tuple:
1368
            cls_spec_info = tuple(get_cls_info(cls) for cls in cls_spec)
1369
            where = get_where_for_args(args, kwargs)
1370
        else:
1371
            cls_spec_info = get_cls_info(cls_spec)
1372
            where = get_where_for_args(args, kwargs, cls_spec)
1373
        return self._store._result_set_factory(self._store, cls_spec_info,
1374
                                               where, self._tables)
81.1.3 by Gustavo Niemeyer
- Redesigned join expressions to handle more complex cases.
1375
1376
62 by Gustavo Niemeyer
Changed the way that objects are keyed. Now they use the obj_info,
1377
Store._result_set_factory = ResultSet
81.1.3 by Gustavo Niemeyer
- Redesigned join expressions to handle more complex cases.
1378
Store._table_set = TableSet
80 by Gustavo Niemeyer
- Implemented support for "where" expressions being passed to
1379
1380
81.1.5 by Gustavo Niemeyer
Implemented support for using a tuple of classes as the first argument of
1381
def get_where_for_args(args, kwargs, cls=None):
80 by Gustavo Niemeyer
- Implemented support for "where" expressions being passed to
1382
    equals = list(args)
1383
    if kwargs:
81.1.9 by Gustavo Niemeyer
- Renamed UnsupportedDatabaseError to DatabaseModuleError.
1384
        if cls is None:
1385
            raise FeatureError("Can't determine class that keyword "
1386
                               "arguments are associated with")
80 by Gustavo Niemeyer
- Implemented support for "where" expressions being passed to
1387
        for key, value in kwargs.items():
95.3.21 by Gustavo Niemeyer
Implemented reference comparisons (Class.ref == obj, Class.ref == obj.id,
1388
            equals.append(getattr(cls, key) == value)
80 by Gustavo Niemeyer
- Implemented support for "where" expressions being passed to
1389
    if equals:
1390
        return And(*equals)
1391
    return Undef
83.1.1 by Gustavo Niemeyer
- Implemented 'autoreload' feature!
1392
1393
1394
class AutoReload(LazyValue):
142.3.20 by Christopher Armstrong
massive doc improvements for store.py
1395
    """A marker for reloading a single value.
1396
1397
    Often this will be used to specify that a specific attribute
1398
    should be loaded from the database on the next access, like so::
1399
1400
        storm_object.property = AutoReload
1401
1402
    On the next access to C{storm_object.property}, the value will be
1403
    loaded from the database.
1404
1405
    It is also often used as a default value for a property::
1406
1407
        class Person(object):
1408
            __storm_table__ = "person"
1409
            id = Int(allow_none=False, default=AutoReload)
1410
1411
        person = store.add(Person)
1412
        person.id # gets the attribute from the database.
1413
    """
83.1.1 by Gustavo Niemeyer
- Implemented 'autoreload' feature!
1414
    pass
1415
1416
AutoReload = AutoReload()