]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
Added session.prune(), releases unused objects in strong-ref identity maps.
authorJason Kirtland <jek@discorporate.us>
Thu, 16 Aug 2007 19:58:24 +0000 (19:58 +0000)
committerJason Kirtland <jek@discorporate.us>
Thu, 16 Aug 2007 19:58:24 +0000 (19:58 +0000)
CHANGES
lib/sqlalchemy/orm/session.py
lib/sqlalchemy/orm/unitofwork.py
test/orm/session.py

diff --git a/CHANGES b/CHANGES
index 1dcf0c42ba3107d9106c0c8a03a661cc433ddfd0..277d162b7ca90fb85747383f65ae759227ba6390 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -1,6 +1,8 @@
 0.4.0beta4
 - fix to bind param processing such that "False" values (like blank strings)
   still get processed/encoded
+- added session.prune(), trims away instances cached in a session that are
+  no longer referenced elsewhere. (a utility for for strong-ref identity maps)
 
 0.4.0beta3
 - sql types optimization:
index c1e0e5b6519820ba9e9cb1be288a0f8c5dd78d14..4fa9a4067ad59d19f77419381185df124df0b813 100644 (file)
@@ -725,6 +725,16 @@ class Session(object):
         for c in [obj] + list(_object_mapper(obj).cascade_iterator('refresh-expire', obj)):
             self._expire_impl(c)
 
+    def prune(self):
+        """Removes unreferenced instances cached in the identity map.
+
+        Removes any object in this Session'sidentity map that is not
+        referenced in user code, modified, new or scheduled for deletion.
+        Returns the number of objects pruned.
+        """
+        
+        return self.uow.prune_identity_map()
+
     def _expire_impl(self, obj):
         self._validate_persistent(obj)
 
index 9cf3da639c499d8247f1576f07d37bc812f8e1ce..95ed950e19003554cd40b51d52ed3c3d1ea1def1 100644 (file)
@@ -19,12 +19,11 @@ new, dirty, or deleted and provides the capability to flush all those
 changes at once.
 """
 
+import gc, StringIO, weakref
 from sqlalchemy import util, logging, topological, exceptions
 from sqlalchemy.orm import attributes, interfaces
 from sqlalchemy.orm import util as mapperutil
 from sqlalchemy.orm.mapper import object_mapper
-import StringIO
-import weakref
 
 # Load lazily
 object_session = None
@@ -220,6 +219,24 @@ class UnitOfWork(object):
         if session.extension is not None:
             session.extension.after_flush_postexec(session, flush_context)
 
+    def prune_identity_map(self):
+        """Removes unreferenced instances cached in the identity map.
+
+        Removes any object in the identity map that is not referenced
+        in user code or scheduled for a unit of work operation.  Returns
+        the number of objects pruned.
+        """
+
+        if isinstance(self.identity_map, weakref.WeakValueDictionary):
+            return 0
+        ref_count = len(self.identity_map)
+        dirty = self.locate_dirty()
+        keepers = weakref.WeakValueDictionary(self.identity_map)
+        self.identity_map.clear()
+        gc.collect()
+        self.identity_map.update(keepers)
+        return ref_count - len(self.identity_map)
+
 class UOWTransaction(object):
     """Handles the details of organizing and executing transaction
     tasks during a UnitOfWork object's flush() operation.  
index 1ad47dad6c95f82a74f57968efd6502897eb62db..7857bf0647e0ca6ab5552ac25c24bef423a3b8c3 100644 (file)
@@ -385,6 +385,55 @@ class SessionTest(AssertMixin):
         import gc
         gc.collect()
         assert len(s.identity_map) == 1
+
+    def test_prune(self):
+        tables.delete()
+        s = create_session()
+        class User(object):pass
+        mapper(User, users)
+
+        for o in [User() for x in xrange(10)]:
+            s.save(o)
+        # o is still live after this loop...
+
+        self.assert_(len(s.identity_map) == 0)
+        self.assert_(s.prune() == 0)
+        s.flush()
+        self.assert_(s.prune() == 9)
+        self.assert_(len(s.identity_map) == 1)
+
+        user_id = o.user_id
+        del o
+        self.assert_(s.prune() == 1)
+        self.assert_(len(s.identity_map) == 0)
+
+        u = s.query(User).get(user_id)
+        self.assert_(s.prune() == 0)
+        self.assert_(len(s.identity_map) == 1)
+        u.user_name = 'squiznart'
+        del u
+        self.assert_(s.prune() == 0)
+        self.assert_(len(s.identity_map) == 1)
+        s.flush()
+        self.assert_(s.prune() == 1)
+        self.assert_(len(s.identity_map) == 0)
+
+        s.save(User())
+        self.assert_(s.prune() == 0)
+        self.assert_(len(s.identity_map) == 0)
+        s.flush()
+        self.assert_(len(s.identity_map) == 1)
+        self.assert_(s.prune() == 1)
+        self.assert_(len(s.identity_map) == 0)
+
+        u = s.query(User).get(user_id)
+        s.delete(u)
+        del u
+        self.assert_(s.prune() == 0)
+        self.assert_(len(s.identity_map) == 1)
+        s.flush()
+        self.assert_(s.prune() == 0)
+        self.assert_(len(s.identity_map) == 0)
         
     def test_no_save_cascade(self):
         mapper(Address, addresses)