Mike Bayer avatar Mike Bayer committed 182eb9a

- identity map in Session is by default *no longer weak referencing*.
to have it be weak referencing, use create_session(weak_identity_map=True)
- some fixes to OrderedProperties

Comments (0)

Files changed (7)

   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

doc/build/content/unitofwork.txt

     >>> 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:
 
     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}
 

lib/sqlalchemy/orm/mapper.py

             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

lib/sqlalchemy/orm/session.py

     
     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)
         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):

lib/sqlalchemy/orm/unitofwork.py

     """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()

lib/sqlalchemy/util.py

     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"""

test/orm/lazytest1.py

         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)
         	}
         	
     	)
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.