root/galaxy-central/eggs/SQLAlchemy-0.5.6_dev_r6498-py2.6.egg/sqlalchemy/orm/state.py @ 3

リビジョン 3, 14.9 KB (コミッタ: kohda, 14 年 前)

Install Unix tools  http://hannonlab.cshl.edu/galaxy_unix_tools/galaxy.html

行番号 
1from sqlalchemy.util import EMPTY_SET
2import weakref
3from sqlalchemy import util
4from sqlalchemy.orm.attributes import PASSIVE_NORESULT, PASSIVE_OFF, NEVER_SET, NO_VALUE, manager_of_class, ATTR_WAS_SET
5from sqlalchemy.orm import attributes
6from sqlalchemy.orm import interfaces
7
8class InstanceState(object):
9    """tracks state information at the instance level."""
10
11    session_id = None
12    key = None
13    runid = None
14    expired_attributes = EMPTY_SET
15    load_options = EMPTY_SET
16    load_path = ()
17    insert_order = None
18    mutable_dict = None
19    _strong_obj = None
20   
21    def __init__(self, obj, manager):
22        self.class_ = obj.__class__
23        self.manager = manager
24        self.obj = weakref.ref(obj, self._cleanup)
25        self.modified = False
26        self.callables = {}
27        self.expired = False
28        self.committed_state = {}
29        self.pending = {}
30        self.parents = {}
31       
32    def detach(self):
33        if self.session_id:
34            del self.session_id
35
36    def dispose(self):
37        if self.session_id:
38            del self.session_id
39        del self.obj
40   
41    def _cleanup(self, ref):
42        instance_dict = self._instance_dict()
43        if instance_dict:
44            instance_dict.remove(self)
45        self.dispose()
46   
47    def obj(self):
48        return None
49   
50    @property
51    def dict(self):
52        o = self.obj()
53        if o is not None:
54            return attributes.instance_dict(o)
55        else:
56            return {}
57       
58    @property
59    def sort_key(self):
60        return self.key and self.key[1] or (self.insert_order, )
61
62    def check_modified(self):
63        # TODO: deprecate
64        return self.modified
65
66    def initialize_instance(*mixed, **kwargs):
67        self, instance, args = mixed[0], mixed[1], mixed[2:]
68        manager = self.manager
69
70        for fn in manager.events.on_init:
71            fn(self, instance, args, kwargs)
72           
73        # LESSTHANIDEAL:
74        # adjust for the case where the InstanceState was created before
75        # mapper compilation, and this actually needs to be a MutableAttrInstanceState
76        if manager.mutable_attributes and self.__class__ is not MutableAttrInstanceState:
77            self.__class__ = MutableAttrInstanceState
78            self.obj = weakref.ref(self.obj(), self._cleanup)
79            self.mutable_dict = {}
80           
81        try:
82            return manager.events.original_init(*mixed[1:], **kwargs)
83        except:
84            for fn in manager.events.on_init_failure:
85                fn(self, instance, args, kwargs)
86            raise
87
88    def get_history(self, key, **kwargs):
89        return self.manager.get_impl(key).get_history(self, self.dict, **kwargs)
90
91    def get_impl(self, key):
92        return self.manager.get_impl(key)
93
94    def get_pending(self, key):
95        if key not in self.pending:
96            self.pending[key] = PendingCollection()
97        return self.pending[key]
98
99    def value_as_iterable(self, key, passive=PASSIVE_OFF):
100        """return an InstanceState attribute as a list,
101        regardless of it being a scalar or collection-based
102        attribute.
103
104        returns None if passive is not PASSIVE_OFF and the getter returns
105        PASSIVE_NORESULT.
106        """
107
108        impl = self.get_impl(key)
109        dict_ = self.dict
110        x = impl.get(self, dict_, passive=passive)
111        if x is PASSIVE_NORESULT:
112            return None
113        elif hasattr(impl, 'get_collection'):
114            return impl.get_collection(self, dict_, x, passive=passive)
115        else:
116            return [x]
117
118    def _run_on_load(self, instance):
119        self.manager.events.run('on_load', instance)
120
121    def __getstate__(self):
122        d = {
123            'instance':self.obj(),
124        }
125
126        d.update(
127            (k, self.__dict__[k]) for k in (
128                'committed_state', 'pending', 'parents', 'modified', 'expired',
129                'callables'
130            ) if self.__dict__[k]
131        )
132       
133        d.update(
134            (k, self.__dict__[k]) for k in (
135                'key', 'load_options', 'expired_attributes', 'mutable_dict'
136            ) if k in self.__dict__
137        )
138        if self.load_path:
139            d['load_path'] = interfaces.serialize_path(self.load_path)
140        return d
141       
142    def __setstate__(self, state):
143        self.obj = weakref.ref(state['instance'], self._cleanup)
144        self.class_ = state['instance'].__class__
145        self.manager = manager_of_class(self.class_)
146
147        self.committed_state = state.get('committed_state', {})
148        self.pending = state.get('pending', {})
149        self.parents = state.get('parents', {})
150        self.modified = state.get('modified', False)
151        self.expired = state.get('expired', False)
152        self.callables = state.get('callables', {})
153       
154        if self.modified:
155            self._strong_obj = state['instance']
156           
157        self.__dict__.update(
158            (k, state[k]) for k in (
159                'key', 'load_options', 'expired_attributes', 'mutable_dict'
160            ) if k in state
161        )
162
163        if 'load_path' in state:
164            self.load_path = interfaces.deserialize_path(state['load_path'])
165
166    def initialize(self, key):
167        self.manager.get_impl(key).initialize(self, self.dict)
168
169    def set_callable(self, key, callable_):
170        self.dict.pop(key, None)
171        self.callables[key] = callable_
172
173    def __call__(self):
174        """__call__ allows the InstanceState to act as a deferred
175        callable for loading expired attributes, which is also
176        serializable (picklable).
177
178        """
179        unmodified = self.unmodified
180        class_manager = self.manager
181        class_manager.deferred_scalar_loader(self, [
182            attr.impl.key for attr in class_manager.attributes if
183                attr.impl.accepts_scalar_loader and
184                attr.impl.key in self.expired_attributes and
185                attr.impl.key in unmodified
186            ])
187        for k in self.expired_attributes:
188            self.callables.pop(k, None)
189        del self.expired_attributes
190        return ATTR_WAS_SET
191
192    @property
193    def unmodified(self):
194        """a set of keys which have no uncommitted changes"""
195       
196        return set(self.manager).difference(self.committed_state)
197
198    @property
199    def unloaded(self):
200        """a set of keys which do not have a loaded value.
201
202        This includes expired attributes and any other attribute that
203        was never populated or modified.
204
205        """
206        return set(
207            key for key in self.manager.iterkeys()
208            if key not in self.committed_state and key not in self.dict)
209
210    def expire_attributes(self, attribute_names, instance_dict=None):
211        self.expired_attributes = set(self.expired_attributes)
212
213        if attribute_names is None:
214            attribute_names = self.manager.keys()
215            self.expired = True
216            if self.modified:
217                if not instance_dict:
218                    instance_dict = self._instance_dict()
219                    if instance_dict:
220                        instance_dict._modified.discard(self)
221                else:
222                    instance_dict._modified.discard(self)
223                   
224            self.modified = False
225            filter_deferred = True
226        else:
227            filter_deferred = False
228        dict_ = self.dict
229       
230        for key in attribute_names:
231            impl = self.manager[key].impl
232            if not filter_deferred or \
233                not impl.dont_expire_missing or \
234                key in dict_:
235                self.expired_attributes.add(key)
236                if impl.accepts_scalar_loader:
237                    self.callables[key] = self
238            dict_.pop(key, None)
239            self.pending.pop(key, None)
240            self.committed_state.pop(key, None)
241            if self.mutable_dict:
242                self.mutable_dict.pop(key, None)
243               
244    def reset(self, key, dict_):
245        """remove the given attribute and any callables associated with it."""
246
247        dict_.pop(key, None)
248        self.callables.pop(key, None)
249
250    def _instance_dict(self):
251        return None
252
253    def _is_really_none(self):
254        return self.obj()
255       
256    def modified_event(self, dict_, attr, should_copy, previous, passive=PASSIVE_OFF):
257        needs_committed = attr.key not in self.committed_state
258
259        if needs_committed:
260            if previous is NEVER_SET:
261                if passive:
262                    if attr.key in dict_:
263                        previous = dict_[attr.key]
264                else:
265                    previous = attr.get(self, dict_)
266
267            if should_copy and previous not in (None, NO_VALUE, NEVER_SET):
268                previous = attr.copy(previous)
269
270            if needs_committed:
271                self.committed_state[attr.key] = previous
272
273        if not self.modified:
274            instance_dict = self._instance_dict()
275            if instance_dict:
276                instance_dict._modified.add(self)
277
278        self.modified = True
279        if self._strong_obj is None:
280            self._strong_obj = self.obj()
281
282    def commit(self, dict_, keys):
283        """Commit attributes.
284
285        This is used by a partial-attribute load operation to mark committed
286        those attributes which were refreshed from the database.
287
288        Attributes marked as "expired" can potentially remain "expired" after
289        this step if a value was not populated in state.dict.
290
291        """
292        class_manager = self.manager
293        for key in keys:
294            if key in dict_ and key in class_manager.mutable_attributes:
295                class_manager[key].impl.commit_to_state(self, dict_, self.committed_state)
296            else:
297                self.committed_state.pop(key, None)
298
299        self.expired = False
300        # unexpire attributes which have loaded
301        for key in self.expired_attributes.intersection(keys):
302            if key in dict_:
303                self.expired_attributes.remove(key)
304                self.callables.pop(key, None)
305
306    def commit_all(self, dict_, instance_dict=None):
307        """commit all attributes unconditionally.
308
309        This is used after a flush() or a full load/refresh
310        to remove all pending state from the instance.
311
312         - all attributes are marked as "committed"
313         - the "strong dirty reference" is removed
314         - the "modified" flag is set to False
315         - any "expired" markers/callables are removed.
316
317        Attributes marked as "expired" can potentially remain "expired" after this step
318        if a value was not populated in state.dict.
319
320        """
321       
322        self.committed_state = {}
323        self.pending = {}
324       
325        # unexpire attributes which have loaded
326        if self.expired_attributes:
327            for key in self.expired_attributes.intersection(dict_):
328                self.callables.pop(key, None)
329            self.expired_attributes.difference_update(dict_)
330
331        for key in self.manager.mutable_attributes:
332            if key in dict_:
333                self.manager[key].impl.commit_to_state(self, dict_, self.committed_state)
334
335        if instance_dict and self.modified:
336            instance_dict._modified.discard(self)
337
338        self.modified = self.expired = False
339        self._strong_obj = None
340
341class MutableAttrInstanceState(InstanceState):
342    def __init__(self, obj, manager):
343        self.mutable_dict = {}
344        InstanceState.__init__(self, obj, manager)
345       
346    def _get_modified(self, dict_=None):
347        if self.__dict__.get('modified', False):
348            return True
349        else:
350            if dict_ is None:
351                dict_ = self.dict
352            for key in self.manager.mutable_attributes:
353                if self.manager[key].impl.check_mutable_modified(self, dict_):
354                    return True
355            else:
356                return False
357   
358    def _set_modified(self, value):
359        self.__dict__['modified'] = value
360       
361    modified = property(_get_modified, _set_modified)
362   
363    @property
364    def unmodified(self):
365        """a set of keys which have no uncommitted changes"""
366
367        dict_ = self.dict
368        return set(
369            key for key in self.manager.iterkeys()
370            if (key not in self.committed_state or
371                (key in self.manager.mutable_attributes and
372                 not self.manager[key].impl.check_mutable_modified(self, dict_))))
373
374    def _is_really_none(self):
375        """do a check modified/resurrect.
376       
377        This would be called in the extremely rare
378        race condition that the weakref returned None but
379        the cleanup handler had not yet established the
380        __resurrect callable as its replacement.
381       
382        """
383        if self.modified:
384            self.obj = self.__resurrect
385            return self.obj()
386        else:
387            return None
388
389    def reset(self, key, dict_):
390        self.mutable_dict.pop(key, None)
391        InstanceState.reset(self, key, dict_)
392   
393    def _cleanup(self, ref):
394        """weakref callback.
395       
396        This method may be called by an asynchronous
397        gc.
398       
399        If the state shows pending changes, the weakref
400        is replaced by the __resurrect callable which will
401        re-establish an object reference on next access,
402        else removes this InstanceState from the owning
403        identity map, if any.
404       
405        """
406        if self._get_modified(self.mutable_dict):
407            self.obj = self.__resurrect
408        else:
409            instance_dict = self._instance_dict()
410            if instance_dict:
411                instance_dict.remove(self)
412            self.dispose()
413           
414    def __resurrect(self):
415        """A substitute for the obj() weakref function which resurrects."""
416       
417        # store strong ref'ed version of the object; will revert
418        # to weakref when changes are persisted
419       
420        obj = self.manager.new_instance(state=self)
421        self.obj = weakref.ref(obj, self._cleanup)
422        self._strong_obj = obj
423        obj.__dict__.update(self.mutable_dict)
424
425        # re-establishes identity attributes from the key
426        self.manager.events.run('on_resurrect', self, obj)
427       
428        # TODO: don't really think we should run this here.
429        # resurrect is only meant to preserve the minimal state needed to
430        # do an UPDATE, not to produce a fully usable object
431        self._run_on_load(obj)
432       
433        return obj
434
435class PendingCollection(object):
436    """A writable placeholder for an unloaded collection.
437
438    Stores items appended to and removed from a collection that has not yet
439    been loaded. When the collection is loaded, the changes stored in
440    PendingCollection are applied to it to produce the final result.
441
442    """
443    def __init__(self):
444        self.deleted_items = util.IdentitySet()
445        self.added_items = util.OrderedIdentitySet()
446
447    def append(self, value):
448        if value in self.deleted_items:
449            self.deleted_items.remove(value)
450        self.added_items.add(value)
451
452    def remove(self, value):
453        if value in self.added_items:
454            self.added_items.remove(value)
455        self.deleted_items.add(value)
456
Note: リポジトリブラウザについてのヘルプは TracBrowser を参照してください。