]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
further work on insuring clear_mappers() really works. assignmapper identified
authorMike Bayer <mike_mp@zzzcomputing.com>
Tue, 30 Jan 2007 01:01:22 +0000 (01:01 +0000)
committerMike Bayer <mike_mp@zzzcomputing.com>
Tue, 30 Jan 2007 01:01:22 +0000 (01:01 +0000)
as a much trickier thing to clean out.  added a unit test so that if any new collections get introduced
we are still testing.

lib/sqlalchemy/ext/assignmapper.py
lib/sqlalchemy/orm/__init__.py
lib/sqlalchemy/orm/mapper.py
lib/sqlalchemy/util.py
test/orm/alltests.py
test/orm/memusage.py [new file with mode: 0644]

index 89cf962e7238ad9aa3da51646baad107b1246b56..b76f299433662c8b58ae00133fa3694240438ecf 100644 (file)
@@ -39,3 +39,4 @@ def assign_mapper(ctx, class_, *args, **kwargs):
     for name in ['flush', 'delete', 'expire', 'refresh', 'expunge', 'merge', 'save', 'update', 'save_or_update']:
         monkeypatch_objectstore_method(ctx, class_, name)
     return m
+
index 96890272d45c76cbad2562d03bd2a8df54ce32b4..1e1a75b63159613530236ab80e6ca155ee3d7c55 100644 (file)
@@ -74,8 +74,10 @@ def clear_mappers():
     when new mappers are created, they will be assigned to their classes as their primary mapper."""
     for mapper in mapper_registry.values():
         attribute_manager.reset_class_managed(mapper.class_)
+        mapper.class_key.dispose()
+        if hasattr(mapper.class_, 'c'):
+            del mapper.class_.c
     mapper_registry.clear()
-    mapperlib.ClassKey.instances.clear()
     
 def clear_mapper(m):
     """remove the given mapper from the storage of mappers.  
@@ -83,7 +85,11 @@ def clear_mapper(m):
     when a new mapper is created for the previous mapper's class, it will be used as that classes' 
     new primary mapper."""
     del mapper_registry[m.class_key]
-
+    attribute_manager.reset_class_managed(m.class_)
+    if hasattr(m.class_, 'c'):
+        del m.class_.c
+    m.class_key.dispose()
+    
 def extension(ext):
     """return a MapperOption that will insert the given MapperExtension to the 
     beginning of the list of extensions that will be called in the context of the Query.
index d6967934cd8b39de1cec514abdbf2eeba0c989a7..1d4ce6bb9c033161374190e8acd9245f882d944b 100644 (file)
@@ -1458,7 +1458,9 @@ class ClassKey(object):
         return self is other
     def __repr__(self):
         return "ClassKey(%s, %s)" % (repr(self.class_), repr(self.entity_name))
-
+    def dispose(self):
+        type(self).dispose_static(self.class_, self.entity_name)
+        
 def has_identity(object):
     return hasattr(object, '_instance_key')
     
index 7066309189435ec7bfbe783b7be4d4fcabfa750d..54b1afa9fbd33e1fff1f96129320b943061241cd 100644 (file)
@@ -49,6 +49,10 @@ def reversed(seq):
 
 class ArgSingleton(type):
     instances = {}
+    def dispose_static(self, *args):
+        hashkey = (self, args)
+        #if hashkey in ArgSingleton.instances:
+        del ArgSingleton.instances[hashkey]
     def __call__(self, *args):
         hashkey = (self, args)
         try:
index ffcdd3d218faaf42dfe37509739f8493a452146e..6d6aba2b269aa7d731796d9ff5c0992826cb4e83 100644 (file)
@@ -17,6 +17,7 @@ def suite():
         'orm.relationships',
         'orm.association',
         'orm.merge',
+        'orm.memusage',
         
         'orm.cycles',
         'orm.poly_linked_list',
diff --git a/test/orm/memusage.py b/test/orm/memusage.py
new file mode 100644 (file)
index 0000000..84d3ebd
--- /dev/null
@@ -0,0 +1,79 @@
+from sqlalchemy import *
+from sqlalchemy.orm import mapperlib, session, unitofwork, attributes
+Mapper = mapperlib.Mapper
+import gc
+import testbase
+import tables
+
+class A(object):pass
+class B(object):pass
+
+class MapperCleanoutTest(testbase.AssertMixin):
+    """test that clear_mappers() removes everything related to the class.
+    
+    does not include classes that use the assignmapper extension."""
+    def setUp(self):
+        global engine
+        engine = testbase.db
+    
+    def test_mapper_cleanup(self):
+        for x in range(0, 5):
+            self.do_test()
+            gc.collect()
+            for o in gc.get_objects():
+                if isinstance(o, Mapper):
+                    # the classes in the 'tables' package have assign_mapper called on them
+                    # which is particularly sticky
+                    # if getattr(tables, o.class_.__name__, None) is o.class_:
+                    #    continue
+                    # well really we are just testing our own classes here
+                    if (o.class_ not in [A,B]):
+                        continue
+                    assert False
+        assert True
+        
+    def do_test(self):
+        metadata = BoundMetaData(engine)
+
+        table1 = Table("mytable", metadata, 
+            Column('col1', Integer, primary_key=True),
+            Column('col2', String(30))
+            )
+
+        table2 = Table("mytable2", metadata, 
+            Column('col1', Integer, primary_key=True),
+            Column('col2', String(30)),
+            Column('col3', String(30), ForeignKey("mytable.col1"))
+            )
+    
+        metadata.create_all()
+
+
+        m1 = mapper(A, table1, properties={
+            "bs":relation(B)
+        })
+        m2 = mapper(B, table2)
+
+        m3 = mapper(A, table1, non_primary=True)
+        
+        sess = create_session()
+        a1 = A()
+        a2 = A()
+        a3 = A()
+        a1.bs.append(B())
+        a1.bs.append(B())
+        a3.bs.append(B())
+        for x in [a1,a2,a3]:
+            sess.save(x)
+        sess.flush()
+        sess.clear()
+
+        alist = sess.query(A).select()
+        for a in alist:
+            print "A", a, "BS", [b for b in a.bs]
+    
+        metadata.drop_all()
+        clear_mappers()
+    
+if __name__ == '__main__':
+    testbase.main()
\ No newline at end of file