]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
- identity map in Session is by default *no longer weak referencing*.
authorMike Bayer <mike_mp@zzzcomputing.com>
Sat, 9 Dec 2006 01:08:25 +0000 (01:08 +0000)
committerMike Bayer <mike_mp@zzzcomputing.com>
Sat, 9 Dec 2006 01:08:25 +0000 (01:08 +0000)
to have it be weak referencing, use create_session(weak_identity_map=True)
- some fixes to OrderedProperties

CHANGES
doc/build/content/unitofwork.txt
lib/sqlalchemy/orm/mapper.py
lib/sqlalchemy/orm/session.py
lib/sqlalchemy/orm/unitofwork.py
lib/sqlalchemy/util.py
test/orm/lazytest1.py

diff --git a/CHANGES b/CHANGES
index 7f01b75467d3851b9cb8a8b414acdc92f6f0320d..8614d8dde8e4191903bdbef82cb2e4f6d3b98804 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -7,6 +7,8 @@ accidentally [ticket:387]
   time-intensive to generate log messages
   - fixed bug in cascade rules whereby the entire object graph
   could be unnecessarily cascaded on the save/update cascade
+- identity map in Session is by default *no longer weak referencing*.
+to have it be weak referencing, use create_session(weak_identity_map=True)
 - MySQL detects errors 2006 (server has gone away) and 2014
 (commands out of sync) and invalidates the connection on which it occured.
 - added keywords for EXCEPT, INTERSECT, EXCEPT ALL, INTERSECT ALL
index 0b482ba136e0aa79d0c9d3f336f1586b63edee3d..4c9bac87067660e09d6bc1e7835ae47eda120ed6 100644 (file)
@@ -83,7 +83,7 @@ For example; below, two separate calls to load an instance with database identit
     >>> obj1 is obj2
     True
     
-The Identity Map is an instance of `weakref.WeakValueDictionary`, so that when an in-memory object falls out of scope, it will be removed automatically.  However, this may not be instant if there are circular references upon the object.  To guarantee that an instance is removed from the identity map before removing references to it, use the `expunge()` method, described later, to remove it.
+The Identity Map is an instance of `dict` by default.  (This is new as of version 0.3.2).  As an option, you can specify the flag `weak_identity_map=True` to the `create_session` function so that it will use a `weakref.WeakValueDictionary`, so that when an in-memory object falls out of scope, it will be removed automatically, thereby providing some automatic management of memory.   However, this may not be instant if there are circular references upon the object.  To guarantee that an instance is removed from the identity map before removing references to it, use the `expunge()` method, described later, to remove it.  Additionally, note that an object that has changes marked on it (i.e. "dirty") can still fall out of scope when using `weak_identity_map`.
 
 The Session supports an iterator interface in order to see all objects in the identity map:
 
@@ -124,15 +124,15 @@ These records are all tracked by collection functions that are also viewable off
     session.new
     
     # persistent objects which currently have changes detected
-    # (this Set is now created on the fly each time the property is called)
+    # (this collection is now created on the fly each time the property is called)
     session.dirty
 
     # persistent objects that have been marked as deleted via session.delete(obj)
     session.deleted
 
-As of the 0.3 series, the `new` and `deleted` lists are actual `Set` objects, and are therefore *not weak referencing*.  Elements that are in the `dirty` collection are, however, and have to be maintained in the current scope else they will be garbage collected(this is a slightly suboptimal behavior introduced in the 0.3 series which may be improved in a future release).  
+Note that if a session is created with the `weak_identity_map` flag, an item which is marked as "dirty" will be silently removed from the session if the item falls out of scope in the user application.  This is because the unit of work does not look for "dirty" changes except for within a flush operation (or any time the session.dirty collection is accessed). 
 
-As for objects inside of `new` and `deleted`, if you abandon all references to new or modified objects within a session, *they are still present* in either of those two lists, and will be saved on the next flush operation, unless they are removed from the Session explicitly (more on that later).  The `new` list may change in a future release to be weak-referencing, however for the `deleted` list, one can see that its quite natural for a an object marked as deleted to have no references in the application, yet a DELETE operation is still required.
+As for objects inside of `new` and `deleted`, if you abandon all references to new or modified objects within a session, *they are still present* in either of those two lists, and will be saved on the next flush operation, unless they are removed from the Session explicitly (more on that later).  
 
 ### The Session API {@name=api}
 
index fd36700e6ff07918d367d7a7cb563a394a2cf86d..d7449c7cab1dc4c6f186dc08bd49f721255fc941 100644 (file)
@@ -178,8 +178,8 @@ class Mapper(object):
             members of the object are accessed."""
             def _get_data(s):
                 self.compile()
-                return s.__dict__['_OrderedProperties__data']
-            _OrderedProperties__data = property(_get_data)
+                return s.__dict__['_data']
+            _data = property(_get_data)
                 
         self.columns = LOrderedProp()
         self.c = self.columns
index 36522379ff04df3a3cc617cd78ddf0dea903e905..1d872c8c4f6af019830d9272fb1d5744ae929acb 100644 (file)
@@ -75,15 +75,16 @@ class Session(object):
     
     The Session object is **not** threadsafe.  For thread-management of Sessions, see the
     sqlalchemy.ext.sessioncontext module."""
-    def __init__(self, bind_to=None, hash_key=None, import_session=None, echo_uow=False):
+    def __init__(self, bind_to=None, hash_key=None, import_session=None, echo_uow=False, weak_identity_map=False):
         if import_session is not None:
-            self.uow = unitofwork.UnitOfWork(identity_map=import_session.uow.identity_map)
+            self.uow = unitofwork.UnitOfWork(identity_map=import_session.uow.identity_map, weak_identity_map=weak_identity_map)
         else:
-            self.uow = unitofwork.UnitOfWork()
+            self.uow = unitofwork.UnitOfWork(weak_identity_map=weak_identity_map)
         
         self.bind_to = bind_to
         self.binds = {}
         self.echo_uow = echo_uow
+        self.weak_identity_map = weak_identity_map
         self.transaction = None
         if hash_key is None:
             self.hash_key = id(self)
@@ -147,7 +148,7 @@ class Session(object):
         for instance in self:
             self._unattach(instance)
         echo = self.uow.echo
-        self.uow = unitofwork.UnitOfWork()
+        self.uow = unitofwork.UnitOfWork(weak_identity_map=self.weak_identity_map)
         self.uow.echo = echo
             
     def mapper(self, class_, entity_name=None):
index 3c1238287bf1b2a935ab77429964c8e83f0f40b5..3f2583eafc428a7ef9c7193c505faae2060a7134 100644 (file)
@@ -73,11 +73,14 @@ class UnitOfWork(object):
     """main UOW object which stores lists of dirty/new/deleted objects.  
     provides top-level "flush" functionality as well as the transaction 
     boundaries with the SQLEngine(s) involved in a write operation."""
-    def __init__(self, identity_map=None):
+    def __init__(self, identity_map=None, weak_identity_map=False):
         if identity_map is not None:
             self.identity_map = identity_map
         else:
-            self.identity_map = weakref.WeakValueDictionary()
+            if weak_identity_map:
+                self.identity_map = weakref.WeakValueDictionary()
+            else:
+                self.identity_map = {}
             
         self.new = util.Set() #OrderedSet()
         self.deleted = util.Set()
index 470217936841d01723c42cc71b69fae8c36debfd..21f38098034041a76e37f890d6976b8843b8c5e8 100644 (file)
@@ -93,39 +93,40 @@ class OrderedProperties(object):
     no append or extend.)
     """
     def __init__(self):
-        self.__dict__['_OrderedProperties__data'] = OrderedDict()
+        self.__dict__['_data'] = OrderedDict()
     def __len__(self):
-        return len(self.__data)
+        return len(self._data)
     def __iter__(self):
-        return self.__data.itervalues()
+        return self._data.itervalues()
     def __add__(self, other):
         return list(self) + list(other)
     def __setitem__(self, key, object):
-        self.__data[key] = object
+        self._data[key] = object
     def __getitem__(self, key):
-        return self.__data[key]
+        return self._data[key]
     def __delitem__(self, key):
-        del self.__data[key]
+        del self._data[key]
     def __setattr__(self, key, object):
-        self.__data[key] = object
+        self._data[key] = object
+    _data = property(lambda s:s.__dict__['_data'])
     def __getattr__(self, key):
         try:
-            return self.__dict__['_OrderedProperties__data'][key]
+            return self._data[key]
         except KeyError:
             raise AttributeError(key)
     def __contains__(self, key):
-        return key in self.__data
+        return key in self._data
     def get(self, key, default=None):
         if self.has_key(key):
             return self[key]
         else:
             return default
     def keys(self):
-        return self.__data.keys()
+        return self._data.keys()
     def has_key(self, key):
-        return self.__data.has_key(key)
+        return self._data.has_key(key)
     def clear(self):
-        self.__data.clear()
+        self._data.clear()
         
 class OrderedDict(dict):
     """A Dictionary that returns keys/values/items in the order they were added"""
index eb1310d666e90fe5e348a6057c1f37b0c12ccde9..9f2d53e9d0bc1f2b205f3202458cfe8861a29015 100644 (file)
@@ -69,10 +69,10 @@ class LazyTest(AssertMixin):
         mapper(Relation, rel_table, properties={
         
             'datas': relation(Data,
-               primaryjoin=and_(rel_table.c.info_pk==Data.c.info_pk,
-               Data.c.timeval >= rel_table.c.start,
-               Data.c.timeval <= rel_table.c.finish),
-               foreignkey=Data.c.info_pk)
+               primaryjoin=and_(rel_table.c.info_pk==data_table.c.info_pk,
+               data_table.c.timeval >= rel_table.c.start,
+               data_table.c.timeval <= rel_table.c.finish),
+               foreignkey=data_table.c.info_pk)
                }
                
        )