diff options
Diffstat (limited to 'lib/sqlalchemy/orm/session.py')
-rw-r--r-- | lib/sqlalchemy/orm/session.py | 570 |
1 files changed, 361 insertions, 209 deletions
diff --git a/lib/sqlalchemy/orm/session.py b/lib/sqlalchemy/orm/session.py index b1993118d..a3edacc19 100644 --- a/lib/sqlalchemy/orm/session.py +++ b/lib/sqlalchemy/orm/session.py @@ -10,15 +10,17 @@ import weakref from .. import util, sql, engine, exc as sa_exc from ..sql import util as sql_util, expression -from . import ( - SessionExtension, attributes, exc, query, - loading, identity -) +from . import SessionExtension, attributes, exc, query, loading, identity from ..inspection import inspect from .base import ( - object_mapper, class_mapper, - _class_to_mapper, _state_mapper, object_state, - _none_set, state_str, instance_str + object_mapper, + class_mapper, + _class_to_mapper, + _state_mapper, + object_state, + _none_set, + state_str, + instance_str, ) import itertools from . import persistence @@ -26,8 +28,7 @@ from .unitofwork import UOWTransaction from . import state as statelib import sys -__all__ = ['Session', 'SessionTransaction', - 'SessionExtension', 'sessionmaker'] +__all__ = ["Session", "SessionTransaction", "SessionExtension", "sessionmaker"] _sessions = weakref.WeakValueDictionary() """Weak-referencing dictionary of :class:`.Session` objects. @@ -77,11 +78,11 @@ class _SessionClassMethods(object): return object_session(instance) -ACTIVE = util.symbol('ACTIVE') -PREPARED = util.symbol('PREPARED') -COMMITTED = util.symbol('COMMITTED') -DEACTIVE = util.symbol('DEACTIVE') -CLOSED = util.symbol('CLOSED') +ACTIVE = util.symbol("ACTIVE") +PREPARED = util.symbol("PREPARED") +COMMITTED = util.symbol("COMMITTED") +DEACTIVE = util.symbol("DEACTIVE") +CLOSED = util.symbol("CLOSED") class SessionTransaction(object): @@ -212,7 +213,8 @@ class SessionTransaction(object): if not parent and nested: raise sa_exc.InvalidRequestError( "Can't start a SAVEPOINT transaction when no existing " - "transaction is in progress") + "transaction is in progress" + ) if self.session._enable_transaction_accounting: self._take_snapshot() @@ -249,10 +251,13 @@ class SessionTransaction(object): def is_active(self): return self.session is not None and self._state is ACTIVE - def _assert_active(self, prepared_ok=False, - rollback_ok=False, - deactive_ok=False, - closed_msg="This transaction is closed"): + def _assert_active( + self, + prepared_ok=False, + rollback_ok=False, + deactive_ok=False, + closed_msg="This transaction is closed", + ): if self._state is COMMITTED: raise sa_exc.InvalidRequestError( "This session is in 'committed' state; no further " @@ -295,21 +300,21 @@ class SessionTransaction(object): def _begin(self, nested=False): self._assert_active() - return SessionTransaction( - self.session, self, nested=nested) + return SessionTransaction(self.session, self, nested=nested) def _iterate_self_and_parents(self, upto=None): current = self result = () while current: - result += (current, ) + result += (current,) if current._parent is upto: break elif current._parent is None: raise sa_exc.InvalidRequestError( - "Transaction %s is not on the active transaction list" % ( - upto)) + "Transaction %s is not on the active transaction list" + % (upto) + ) else: current = current._parent @@ -376,7 +381,8 @@ class SessionTransaction(object): s._expire(s.dict, self.session.identity_map._modified) statelib.InstanceState._detach_states( - list(self._deleted), self.session) + list(self._deleted), self.session + ) self._deleted.clear() elif self.nested: self._parent._new.update(self._new) @@ -391,7 +397,8 @@ class SessionTransaction(object): if execution_options: util.warn( "Connection is already established for the " - "given bind; execution_options ignored") + "given bind; execution_options ignored" + ) return self._connections[bind][0] if self._parent: @@ -404,7 +411,8 @@ class SessionTransaction(object): if conn.engine in self._connections: raise sa_exc.InvalidRequestError( "Session already has a Connection associated for the " - "given Connection's Engine") + "given Connection's Engine" + ) else: conn = bind.contextual_connect() @@ -418,8 +426,11 @@ class SessionTransaction(object): else: transaction = conn.begin() - self._connections[conn] = self._connections[conn.engine] = \ - (conn, transaction, conn is not bind) + self._connections[conn] = self._connections[conn.engine] = ( + conn, + transaction, + conn is not bind, + ) self.session.dispatch.after_begin(self.session, self, conn) return conn @@ -427,7 +438,8 @@ class SessionTransaction(object): if self._parent is not None or not self.session.twophase: raise sa_exc.InvalidRequestError( "'twophase' mode not enabled, or not root transaction; " - "can't prepare.") + "can't prepare." + ) self._prepare_impl() def _prepare_impl(self): @@ -449,7 +461,8 @@ class SessionTransaction(object): raise exc.FlushError( "Over 100 subsequent flushes have occurred within " "session.commit() - is an after_flush() hook " - "creating new objects?") + "creating new objects?" + ) if self._parent is None and self.session.twophase: try: @@ -504,7 +517,8 @@ class SessionTransaction(object): transaction._state = DEACTIVE if self.session._enable_transaction_accounting: transaction._restore_snapshot( - dirty_only=transaction.nested) + dirty_only=transaction.nested + ) boundary = transaction break else: @@ -512,15 +526,19 @@ class SessionTransaction(object): sess = self.session - if not rollback_err and sess._enable_transaction_accounting and \ - not sess._is_clean(): + if ( + not rollback_err + and sess._enable_transaction_accounting + and not sess._is_clean() + ): # if items were added, deleted, or mutated # here, we need to re-restore the snapshot util.warn( "Session's state has been changed on " "a non-active transaction - this state " - "will be discarded.") + "will be discarded." + ) boundary._restore_snapshot(dirty_only=boundary.nested) self.close() @@ -535,12 +553,12 @@ class SessionTransaction(object): return self._parent - def close(self, invalidate=False): self.session.transaction = self._parent if self._parent is None: - for connection, transaction, autoclose in \ - set(self._connections.values()): + for connection, transaction, autoclose in set( + self._connections.values() + ): if invalidate: connection.invalidate() if autoclose: @@ -583,21 +601,49 @@ class Session(_SessionClassMethods): """ public_methods = ( - '__contains__', '__iter__', 'add', 'add_all', 'begin', 'begin_nested', - 'close', 'commit', 'connection', 'delete', 'execute', 'expire', - 'expire_all', 'expunge', 'expunge_all', 'flush', 'get_bind', - 'is_modified', 'bulk_save_objects', 'bulk_insert_mappings', - 'bulk_update_mappings', - 'merge', 'query', 'refresh', 'rollback', - 'scalar') - - def __init__(self, bind=None, autoflush=True, expire_on_commit=True, - _enable_transaction_accounting=True, - autocommit=False, twophase=False, - weak_identity_map=True, binds=None, extension=None, - enable_baked_queries=True, - info=None, - query_cls=query.Query): + "__contains__", + "__iter__", + "add", + "add_all", + "begin", + "begin_nested", + "close", + "commit", + "connection", + "delete", + "execute", + "expire", + "expire_all", + "expunge", + "expunge_all", + "flush", + "get_bind", + "is_modified", + "bulk_save_objects", + "bulk_insert_mappings", + "bulk_update_mappings", + "merge", + "query", + "refresh", + "rollback", + "scalar", + ) + + def __init__( + self, + bind=None, + autoflush=True, + expire_on_commit=True, + _enable_transaction_accounting=True, + autocommit=False, + twophase=False, + weak_identity_map=True, + binds=None, + extension=None, + enable_baked_queries=True, + info=None, + query_cls=query.Query, + ): r"""Construct a new Session. See also the :class:`.sessionmaker` function which is used to @@ -753,12 +799,13 @@ class Session(_SessionClassMethods): "weak_identity_map=False is deprecated. " "See the documentation on 'Session Referencing Behavior' " "for an event-based approach to maintaining strong identity " - "references.") + "references." + ) self._identity_cls = identity.StrongInstanceDict self.identity_map = self._identity_cls() - self._new = {} # InstanceState->object, strong refs object + self._new = {} # InstanceState->object, strong refs object self._deleted = {} # same self.bind = bind self.__binds = {} @@ -861,15 +908,14 @@ class Session(_SessionClassMethods): """ if self.transaction is not None: if subtransactions or nested: - self.transaction = self.transaction._begin( - nested=nested) + self.transaction = self.transaction._begin(nested=nested) else: raise sa_exc.InvalidRequestError( "A transaction is already begun. Use " - "subtransactions=True to allow subtransactions.") + "subtransactions=True to allow subtransactions." + ) else: - self.transaction = SessionTransaction( - self, nested=nested) + self.transaction = SessionTransaction(self, nested=nested) return self.transaction # needed for __enter__/__exit__ hook def begin_nested(self): @@ -972,11 +1018,15 @@ class Session(_SessionClassMethods): self.transaction.prepare() - def connection(self, mapper=None, clause=None, - bind=None, - close_with_result=False, - execution_options=None, - **kw): + def connection( + self, + mapper=None, + clause=None, + bind=None, + close_with_result=False, + execution_options=None, + **kw + ): r"""Return a :class:`.Connection` object corresponding to this :class:`.Session` object's transactional state. @@ -1041,14 +1091,17 @@ class Session(_SessionClassMethods): if bind is None: bind = self.get_bind(mapper, clause=clause, **kw) - return self._connection_for_bind(bind, - close_with_result=close_with_result, - execution_options=execution_options) + return self._connection_for_bind( + bind, + close_with_result=close_with_result, + execution_options=execution_options, + ) def _connection_for_bind(self, engine, execution_options=None, **kw): if self.transaction is not None: return self.transaction._connection_for_bind( - engine, execution_options) + engine, execution_options + ) else: conn = engine.contextual_connect(**kw) if execution_options: @@ -1183,14 +1236,16 @@ class Session(_SessionClassMethods): if bind is None: bind = self.get_bind(mapper, clause=clause, **kw) - return self._connection_for_bind( - bind, close_with_result=True).execute(clause, params or {}) + return self._connection_for_bind(bind, close_with_result=True).execute( + clause, params or {} + ) def scalar(self, clause, params=None, mapper=None, bind=None, **kw): """Like :meth:`~.Session.execute` but return a scalar result.""" return self.execute( - clause, params=params, mapper=mapper, bind=bind, **kw).scalar() + clause, params=params, mapper=mapper, bind=bind, **kw + ).scalar() def close(self): """Close this Session. @@ -1256,9 +1311,7 @@ class Session(_SessionClassMethods): self._new = {} self._deleted = {} - statelib.InstanceState._detach_states( - all_states, self - ) + statelib.InstanceState._detach_states(all_states, self) def _add_bind(self, key, bind): try: @@ -1266,7 +1319,8 @@ class Session(_SessionClassMethods): except sa_exc.NoInspectionAvailable: if not isinstance(key, type): raise sa_exc.ArgumentError( - "Not an acceptable bind target: %s" % key) + "Not an acceptable bind target: %s" % key + ) else: self.__binds[key] = bind else: @@ -1278,7 +1332,8 @@ class Session(_SessionClassMethods): self.__binds[selectable] = bind else: raise sa_exc.ArgumentError( - "Not an acceptable bind target: %s" % key) + "Not an acceptable bind target: %s" % key + ) def bind_mapper(self, mapper, bind): """Associate a :class:`.Mapper` or arbitrary Python class with a @@ -1408,7 +1463,8 @@ class Session(_SessionClassMethods): raise sa_exc.UnboundExecutionError( "This session is not bound to a single Engine or " "Connection, and no context was provided to locate " - "a binding.") + "a binding." + ) if mapper is not None: try: @@ -1443,13 +1499,14 @@ class Session(_SessionClassMethods): context = [] if mapper is not None: - context.append('mapper %s' % mapper) + context.append("mapper %s" % mapper) if clause is not None: - context.append('SQL expression') + context.append("SQL expression") raise sa_exc.UnboundExecutionError( - "Could not locate a bind configured on %s or this Session" % ( - ', '.join(context))) + "Could not locate a bind configured on %s or this Session" + % (", ".join(context)) + ) def query(self, *entities, **kwargs): """Return a new :class:`.Query` object corresponding to this @@ -1499,12 +1556,17 @@ class Session(_SessionClassMethods): e.add_detail( "raised as a result of Query-invoked autoflush; " "consider using a session.no_autoflush block if this " - "flush is occurring prematurely") + "flush is occurring prematurely" + ) util.raise_from_cause(e) def refresh( - self, instance, attribute_names=None, with_for_update=None, - lockmode=None): + self, + instance, + attribute_names=None, + with_for_update=None, + lockmode=None, + ): """Expire and refresh the attributes on the given instance. A query will be issued to the database and all attributes will be @@ -1560,7 +1622,8 @@ class Session(_SessionClassMethods): raise sa_exc.ArgumentError( "with_for_update should be the boolean value " "True, or a dictionary with options. " - "A blank dictionary is ambiguous.") + "A blank dictionary is ambiguous." + ) if lockmode: with_for_update = query.LockmodeArg.parse_legacy_query(lockmode) @@ -1572,14 +1635,19 @@ class Session(_SessionClassMethods): else: with_for_update = None - if loading.load_on_ident( + if ( + loading.load_on_ident( self.query(object_mapper(instance)), - state.key, refresh_state=state, + state.key, + refresh_state=state, with_for_update=with_for_update, - only_load_props=attribute_names) is None: + only_load_props=attribute_names, + ) + is None + ): raise sa_exc.InvalidRequestError( - "Could not refresh instance '%s'" % - instance_str(instance)) + "Could not refresh instance '%s'" % instance_str(instance) + ) def expire_all(self): """Expires all persistent instances within this Session. @@ -1662,8 +1730,9 @@ class Session(_SessionClassMethods): else: # pre-fetch the full cascade since the expire is going to # remove associations - cascaded = list(state.manager.mapper.cascade_iterator( - 'refresh-expire', state)) + cascaded = list( + state.manager.mapper.cascade_iterator("refresh-expire", state) + ) self._conditional_expire(state) for o, m, st_, dct_ in cascaded: self._conditional_expire(st_) @@ -1677,8 +1746,11 @@ class Session(_SessionClassMethods): self._new.pop(state) state._detach(self) - @util.deprecated("0.7", "The non-weak-referencing identity map " - "feature is no longer needed.") + @util.deprecated( + "0.7", + "The non-weak-referencing identity map " + "feature is no longer needed.", + ) def prune(self): """Remove unreferenced instances cached in the identity map. @@ -1705,14 +1777,13 @@ class Session(_SessionClassMethods): raise exc.UnmappedInstanceError(instance) if state.session_id is not self.hash_key: raise sa_exc.InvalidRequestError( - "Instance %s is not present in this Session" % - state_str(state)) + "Instance %s is not present in this Session" % state_str(state) + ) - cascaded = list(state.manager.mapper.cascade_iterator( - 'expunge', state)) - self._expunge_states( - [state] + [st_ for o, m, st_, dct_ in cascaded] + cascaded = list( + state.manager.mapper.cascade_iterator("expunge", state) ) + self._expunge_states([state] + [st_ for o, m, st_, dct_ in cascaded]) def _expunge_states(self, states, to_transient=False): for state in states: @@ -1726,7 +1797,8 @@ class Session(_SessionClassMethods): # in the transaction snapshot self.transaction._deleted.pop(state, None) statelib.InstanceState._detach_states( - states, self, to_transient=to_transient) + states, self, to_transient=to_transient + ) def _register_newly_persistent(self, states): pending_to_persistent = self.dispatch.pending_to_persistent or None @@ -1739,9 +1811,11 @@ class Session(_SessionClassMethods): instance_key = mapper._identity_key_from_state(state) - if _none_set.intersection(instance_key[1]) and \ - not mapper.allow_partial_pks or \ - _none_set.issuperset(instance_key[1]): + if ( + _none_set.intersection(instance_key[1]) + and not mapper.allow_partial_pks + or _none_set.issuperset(instance_key[1]) + ): raise exc.FlushError( "Instance %s has a NULL identity key. If this is an " "auto-generated value, check that the database table " @@ -1765,15 +1839,16 @@ class Session(_SessionClassMethods): else: orig_key = state.key self.transaction._key_switches[state] = ( - orig_key, instance_key) + orig_key, + instance_key, + ) state.key = instance_key self.identity_map.replace(state) state._orphaned_outside_of_session = False statelib.InstanceState._commit_all_states( - ((state, state.dict) for state in states), - self.identity_map + ((state, state.dict) for state in states), self.identity_map ) self._register_altered(states) @@ -1849,9 +1924,8 @@ class Session(_SessionClassMethods): mapper = _state_mapper(state) for o, m, st_, dct_ in mapper.cascade_iterator( - 'save-update', - state, - halt_on=self._contains_state): + "save-update", state, halt_on=self._contains_state + ): self._save_or_update_impl(st_) def delete(self, instance): @@ -1875,8 +1949,8 @@ class Session(_SessionClassMethods): if state.key is None: if head: raise sa_exc.InvalidRequestError( - "Instance '%s' is not persisted" % - state_str(state)) + "Instance '%s' is not persisted" % state_str(state) + ) else: return @@ -1894,8 +1968,9 @@ class Session(_SessionClassMethods): # grab the cascades before adding the item to the deleted list # so that autoflush does not delete the item # the strong reference to the instance itself is significant here - cascade_states = list(state.manager.mapper.cascade_iterator( - 'delete', state)) + cascade_states = list( + state.manager.mapper.cascade_iterator("delete", state) + ) self._deleted[state] = obj @@ -1975,13 +2050,21 @@ class Session(_SessionClassMethods): return self._merge( attributes.instance_state(instance), attributes.instance_dict(instance), - load=load, _recursive=_recursive, - _resolve_conflict_map=_resolve_conflict_map) + load=load, + _recursive=_recursive, + _resolve_conflict_map=_resolve_conflict_map, + ) finally: self.autoflush = autoflush - def _merge(self, state, state_dict, load=True, _recursive=None, - _resolve_conflict_map=None): + def _merge( + self, + state, + state_dict, + load=True, + _recursive=None, + _resolve_conflict_map=None, + ): mapper = _state_mapper(state) if state in _recursive: return _recursive[state] @@ -1995,11 +2078,15 @@ class Session(_SessionClassMethods): "merge() with load=False option does not support " "objects transient (i.e. unpersisted) objects. flush() " "all changes on mapped instances before merging with " - "load=False.") + "load=False." + ) key = mapper._identity_key_from_state(state) key_is_persistent = attributes.NEVER_SET not in key[1] and ( - not _none_set.intersection(key[1]) or - (mapper.allow_partial_pks and not _none_set.issuperset(key[1])) + not _none_set.intersection(key[1]) + or ( + mapper.allow_partial_pks + and not _none_set.issuperset(key[1]) + ) ) else: key_is_persistent = True @@ -2022,7 +2109,8 @@ class Session(_SessionClassMethods): raise sa_exc.InvalidRequestError( "merge() with load=False option does not support " "objects marked as 'dirty'. flush() all changes on " - "mapped instances before merging with load=False.") + "mapped instances before merging with load=False." + ) merged = mapper.class_manager.new_instance() merged_state = attributes.instance_state(merged) merged_state.key = key @@ -2054,17 +2142,21 @@ class Session(_SessionClassMethods): state, state_dict, mapper.version_id_col, - passive=attributes.PASSIVE_NO_INITIALIZE) + passive=attributes.PASSIVE_NO_INITIALIZE, + ) merged_version = mapper._get_state_attr_by_column( merged_state, merged_dict, mapper.version_id_col, - passive=attributes.PASSIVE_NO_INITIALIZE) + passive=attributes.PASSIVE_NO_INITIALIZE, + ) - if existing_version is not attributes.PASSIVE_NO_RESULT and \ - merged_version is not attributes.PASSIVE_NO_RESULT and \ - existing_version != merged_version: + if ( + existing_version is not attributes.PASSIVE_NO_RESULT + and merged_version is not attributes.PASSIVE_NO_RESULT + and existing_version != merged_version + ): raise exc.StaleDataError( "Version id '%s' on merged state %s " "does not match existing version '%s'. " @@ -2073,8 +2165,9 @@ class Session(_SessionClassMethods): % ( existing_version, state_str(merged_state), - merged_version - )) + merged_version, + ) + ) merged_state.load_path = state.load_path merged_state.load_options = state.load_options @@ -2087,9 +2180,16 @@ class Session(_SessionClassMethods): merged_state._copy_callables(state) for prop in mapper.iterate_properties: - prop.merge(self, state, state_dict, - merged_state, merged_dict, - load, _recursive, _resolve_conflict_map) + prop.merge( + self, + state, + state_dict, + merged_state, + merged_dict, + load, + _recursive, + _resolve_conflict_map, + ) if not load: # remove any history @@ -2102,14 +2202,16 @@ class Session(_SessionClassMethods): def _validate_persistent(self, state): if not self.identity_map.contains_state(state): raise sa_exc.InvalidRequestError( - "Instance '%s' is not persistent within this Session" % - state_str(state)) + "Instance '%s' is not persistent within this Session" + % state_str(state) + ) def _save_impl(self, state): if state.key is not None: raise sa_exc.InvalidRequestError( "Object '%s' already has an identity - " - "it can't be registered as pending" % state_str(state)) + "it can't be registered as pending" % state_str(state) + ) obj = state.obj() to_attach = self._before_attach(state, obj) @@ -2122,8 +2224,8 @@ class Session(_SessionClassMethods): def _update_impl(self, state, revert_deletion=False): if state.key is None: raise sa_exc.InvalidRequestError( - "Instance '%s' is not persisted" % - state_str(state)) + "Instance '%s' is not persisted" % state_str(state) + ) if state._deleted: if revert_deletion: @@ -2135,8 +2237,7 @@ class Session(_SessionClassMethods): "Instance '%s' has been deleted. " "Use the make_transient() " "function to send this object back " - "to the transient state." % - state_str(state) + "to the transient state." % state_str(state) ) obj = state.obj() @@ -2234,8 +2335,9 @@ class Session(_SessionClassMethods): if state.session_id and state.session_id in _sessions: raise sa_exc.InvalidRequestError( "Object '%s' is already attached to session '%s' " - "(this is '%s')" % (state_str(state), - state.session_id, self.hash_key)) + "(this is '%s')" + % (state_str(state), state.session_id, self.hash_key) + ) self.dispatch.before_attach(self, obj) @@ -2271,7 +2373,8 @@ class Session(_SessionClassMethods): """ return iter( - list(self._new.values()) + list(self.identity_map.values())) + list(self._new.values()) + list(self.identity_map.values()) + ) def _contains_state(self, state): return state in self._new or self.identity_map.contains_state(state) @@ -2319,13 +2422,15 @@ class Session(_SessionClassMethods): "Usage of the '%s' operation is not currently supported " "within the execution stage of the flush process. " "Results may not be consistent. Consider using alternative " - "event listeners or connection-level operations instead." - % method) + "event listeners or connection-level operations instead." % method + ) def _is_clean(self): - return not self.identity_map.check_modified() and \ - not self._deleted and \ - not self._new + return ( + not self.identity_map.check_modified() + and not self._deleted + and not self._new + ) def _flush(self, objects=None): @@ -2375,12 +2480,16 @@ class Session(_SessionClassMethods): is_persistent_orphan = is_orphan and state.has_identity - if is_orphan and not is_persistent_orphan and \ - state._orphaned_outside_of_session: + if ( + is_orphan + and not is_persistent_orphan + and state._orphaned_outside_of_session + ): self._expunge_states([state]) else: _reg = flush_context.register_object( - state, isdelete=is_persistent_orphan) + state, isdelete=is_persistent_orphan + ) assert _reg, "Failed to add object to the flush context!" processed.add(state) @@ -2397,7 +2506,8 @@ class Session(_SessionClassMethods): return flush_context.transaction = transaction = self.begin( - subtransactions=True) + subtransactions=True + ) try: self._warn_on_events = True try: @@ -2413,16 +2523,20 @@ class Session(_SessionClassMethods): len_ = len(self.identity_map._modified) statelib.InstanceState._commit_all_states( - [(state, state.dict) for state in - self.identity_map._modified], - instance_dict=self.identity_map) - util.warn("Attribute history events accumulated on %d " - "previously clean instances " - "within inner-flush event handlers have been " - "reset, and will not result in database updates. " - "Consider using set_committed_value() within " - "inner-flush event handlers to avoid this warning." - % len_) + [ + (state, state.dict) + for state in self.identity_map._modified + ], + instance_dict=self.identity_map, + ) + util.warn( + "Attribute history events accumulated on %d " + "previously clean instances " + "within inner-flush event handlers have been " + "reset, and will not result in database updates. " + "Consider using set_committed_value() within " + "inner-flush event handlers to avoid this warning." % len_ + ) # useful assertions: # if not objects: @@ -2440,8 +2554,12 @@ class Session(_SessionClassMethods): transaction.rollback(_capture_exception=True) def bulk_save_objects( - self, objects, return_defaults=False, update_changed_only=True, - preserve_order=True): + self, + objects, + return_defaults=False, + update_changed_only=True, + preserve_order=True, + ): """Perform a bulk save of the given list of objects. The bulk save feature allows mapped objects to be used as the @@ -2520,6 +2638,7 @@ class Session(_SessionClassMethods): :meth:`.Session.bulk_update_mappings` """ + def key(state): return (state.mapper, state.key is not None) @@ -2527,15 +2646,20 @@ class Session(_SessionClassMethods): if not preserve_order: obj_states = sorted(obj_states, key=key) - for (mapper, isupdate), states in itertools.groupby( - obj_states, key - ): + for (mapper, isupdate), states in itertools.groupby(obj_states, key): self._bulk_save_mappings( - mapper, states, isupdate, True, - return_defaults, update_changed_only, False) + mapper, + states, + isupdate, + True, + return_defaults, + update_changed_only, + False, + ) def bulk_insert_mappings( - self, mapper, mappings, return_defaults=False, render_nulls=False): + self, mapper, mappings, return_defaults=False, render_nulls=False + ): """Perform a bulk insert of the given list of mapping dictionaries. The bulk insert feature allows plain Python dictionaries to be used as @@ -2622,8 +2746,14 @@ class Session(_SessionClassMethods): """ self._bulk_save_mappings( - mapper, mappings, False, False, - return_defaults, False, render_nulls) + mapper, + mappings, + False, + False, + return_defaults, + False, + render_nulls, + ) def bulk_update_mappings(self, mapper, mappings): """Perform a bulk update of the given list of mapping dictionaries. @@ -2673,25 +2803,41 @@ class Session(_SessionClassMethods): """ self._bulk_save_mappings( - mapper, mappings, True, False, False, False, False) + mapper, mappings, True, False, False, False, False + ) def _bulk_save_mappings( - self, mapper, mappings, isupdate, isstates, - return_defaults, update_changed_only, render_nulls): + self, + mapper, + mappings, + isupdate, + isstates, + return_defaults, + update_changed_only, + render_nulls, + ): mapper = _class_to_mapper(mapper) self._flushing = True - transaction = self.begin( - subtransactions=True) + transaction = self.begin(subtransactions=True) try: if isupdate: persistence._bulk_update( - mapper, mappings, transaction, - isstates, update_changed_only) + mapper, + mappings, + transaction, + isstates, + update_changed_only, + ) else: persistence._bulk_insert( - mapper, mappings, transaction, - isstates, return_defaults, render_nulls) + mapper, + mappings, + transaction, + isstates, + return_defaults, + render_nulls, + ) transaction.commit() except: @@ -2700,8 +2846,7 @@ class Session(_SessionClassMethods): finally: self._flushing = False - def is_modified(self, instance, include_collections=True, - passive=True): + def is_modified(self, instance, include_collections=True, passive=True): r"""Return ``True`` if the given instance has locally modified attributes. @@ -2775,16 +2920,15 @@ class Session(_SessionClassMethods): dict_ = state.dict for attr in state.manager.attributes: - if \ - ( - not include_collections and - hasattr(attr.impl, 'get_collection') - ) or not hasattr(attr.impl, 'get_history'): + if ( + not include_collections + and hasattr(attr.impl, "get_collection") + ) or not hasattr(attr.impl, "get_history"): continue - (added, unchanged, deleted) = \ - attr.impl.get_history(state, dict_, - passive=attributes.NO_CHANGE) + (added, unchanged, deleted) = attr.impl.get_history( + state, dict_, passive=attributes.NO_CHANGE + ) if added or deleted: return True @@ -2898,9 +3042,12 @@ class Session(_SessionClassMethods): """ return util.IdentitySet( - [state.obj() - for state in self._dirty_states - if state not in self._deleted]) + [ + state.obj() + for state in self._dirty_states + if state not in self._deleted + ] + ) @property def deleted(self): @@ -2961,10 +3108,16 @@ class sessionmaker(_SessionClassMethods): """ - def __init__(self, bind=None, class_=Session, autoflush=True, - autocommit=False, - expire_on_commit=True, - info=None, **kw): + def __init__( + self, + bind=None, + class_=Session, + autoflush=True, + autocommit=False, + expire_on_commit=True, + info=None, + **kw + ): r"""Construct a new :class:`.sessionmaker`. All arguments here except for ``class_`` correspond to arguments @@ -2992,12 +3145,12 @@ class sessionmaker(_SessionClassMethods): constructor of newly created :class:`.Session` objects. """ - kw['bind'] = bind - kw['autoflush'] = autoflush - kw['autocommit'] = autocommit - kw['expire_on_commit'] = expire_on_commit + kw["bind"] = bind + kw["autoflush"] = autoflush + kw["autocommit"] = autocommit + kw["expire_on_commit"] = expire_on_commit if info is not None: - kw['info'] = info + kw["info"] = info self.kw = kw # make our own subclass of the given class, so that # events can be associated with it specifically. @@ -3015,10 +3168,10 @@ class sessionmaker(_SessionClassMethods): """ for k, v in self.kw.items(): - if k == 'info' and 'info' in local_kw: + if k == "info" and "info" in local_kw: d = v.copy() - d.update(local_kw['info']) - local_kw['info'] = d + d.update(local_kw["info"]) + local_kw["info"] = d else: local_kw.setdefault(k, v) return self.class_(**local_kw) @@ -3038,7 +3191,7 @@ class sessionmaker(_SessionClassMethods): return "%s(class_=%r, %s)" % ( self.__class__.__name__, self.class_.__name__, - ", ".join("%s=%r" % (k, v) for k, v in self.kw.items()) + ", ".join("%s=%r" % (k, v) for k, v in self.kw.items()), ) @@ -3139,8 +3292,7 @@ def make_transient_to_detached(instance): """ state = attributes.instance_state(instance) if state.session_id or state.key: - raise sa_exc.InvalidRequestError( - "Given object must be transient") + raise sa_exc.InvalidRequestError("Given object must be transient") state.key = state.mapper._identity_key_from_state(state) if state._deleted: del state._deleted |