<commit_before># orm/session.py
# Copyright (C) 2005-2012 the SQLAlchemy authors and contributors <see AUTHORS file>
#
# This module is part of SQLAlchemy and is released under
# the MIT License: http://www.opensource.org/licenses/mit-license.php

"""Provides the Session class and related utilities."""

import weakref
from .. import util, sql, engine, exc as sa_exc, event
from ..sql import util as sql_util, expression
from . import (
    SessionExtension, attributes, exc, query, util as orm_util,
    loading, identity
    )
from .util import (
    object_mapper, class_mapper,
    _class_to_mapper, _state_mapper, object_state,
    _none_set
    )
from .unitofwork import UOWTransaction
from .mapper import Mapper
from .events import SessionEvents
statelib = util.importlater("sqlalchemy.orm", "state")
import sys

__all__ = ['Session', 'SessionTransaction', 'SessionExtension']


def sessionmaker(bind=None, class_=None, autoflush=True, autocommit=False,
                 expire_on_commit=True, **kwargs):
    """Generate a custom-configured :class:`.Session` class.

    The returned object is a subclass of :class:`.Session`, which,
    when instantiated with no arguments, uses the keyword arguments
    configured here as its constructor arguments.

    It is intended that the :func:`.sessionmaker()` function be called
    within the global scope of an application, and the returned class
    be made available to the rest of the application as the single
    class used to instantiate sessions.

    e.g.::

        # global scope
        Session = sessionmaker(autoflush=False)

        # later, in a local scope, create and use a session:
        sess = Session()

    Any keyword arguments sent to the constructor itself will override the
    "configured" keywords::

        Session = sessionmaker()

        # bind an individual session to a connection
        sess = Session(bind=connection)

    The class also includes a special classmethod ``configure()``, which
    allows additional configurational options to take place after the custom
    ``Session`` class has been generated.  This is useful particularly for
    defining the specific ``Engine`` (or engines) to which new instances of
    ``Session`` should be bound::

        Session = sessionmaker()
        Session.configure(bind=create_engine('sqlite:///foo.db'))

        sess = Session()

    For options, see the constructor options for :class:`.Session`.

    """
    kwargs['bind'] = bind
    kwargs['autoflush'] = autoflush
    kwargs['autocommit'] = autocommit
    kwargs['expire_on_commit'] = expire_on_commit

    if class_ is None:
        class_ = Session

    class Sess(object):
        def __init__(self, **local_kwargs):
            for k in kwargs:
                local_kwargs.setdefault(k, kwargs[k])
            super(Sess, self).__init__(**local_kwargs)

        @classmethod
        def configure(self, **new_kwargs):
            """(Re)configure the arguments for this sessionmaker.

            e.g.::

                Session = sessionmaker()

                Session.configure(bind=create_engine('sqlite://'))
            """
            kwargs.update(new_kwargs)


    return type("SessionMaker", (Sess, class_), {})


class SessionTransaction(object):
    """A :class:`.Session`-level transaction.

    :class:`.SessionTransaction` is a mostly behind-the-scenes object
    not normally referenced directly by application code.   It coordinates
    among multiple :class:`.Connection` objects, maintaining a database
    transaction for each one individually, committing or rolling them
    back all at once.   It also provides optional two-phase commit behavior
    which can augment this coordination operation.

    The :attr:`.Session.transaction` attribute of :class:`.Session` refers to the
    current :class:`.SessionTransaction` object in use, if any.


    A :class:`.SessionTransaction` is associated with a :class:`.Session`
    in its default mode of ``autocommit=False`` immediately, associated
    with no database connections.  As the :class:`.Session` is called upon
    to emit SQL on behalf of various :class:`.Engine` or :class:`.Connection`
    objects, a corresponding :class:`.Connection` and associated :class:`.Transaction`
    is added to a collection within the :class:`.SessionTransaction` object,
    becoming one of the connection/transaction pairs maintained by the
    :class:`.SessionTransaction`.

    The lifespan of the :class:`.SessionTransaction` ends when the
    :meth:`.Session.commit`, :meth:`.Session.rollback` or :meth:`.Session.close`
    methods are called.  At this point, the :class:`.SessionTransaction` removes
    its association with its parent :class:`.Session`.   A :class:`.Session`
    that is in ``autocommit=False`` mode will create a new
    :class:`.SessionTransaction` to replace it immediately, whereas a
    :class:`.Session` that's in ``autocommit=True``
    mode will remain without a :class:`.SessionTransaction` until the
    :meth:`.Session.begin` method is called.

    Another detail of :class:`.SessionTransaction` behavior is that it is
    capable of "nesting".  This means that the :meth:`.begin` method can
    be called while an existing :class:`.SessionTransaction` is already present,
    producing a new :class:`.SessionTransaction` that temporarily replaces
    the parent :class:`.SessionTransaction`.   When a :class:`.SessionTransaction`
    is produced as nested, it assigns itself to the :attr:`.Session.transaction`
    attribute.  When it is ended via :meth:`.Session.commit` or :meth:`.Session.rollback`,
    it restores its parent :class:`.SessionTransaction` back onto the
    :attr:`.Session.transaction` attribute.  The
    behavior is effectively a stack, where :attr:`.Session.transaction` refers
    to the current head of the stack.

    The purpose of this stack is to allow nesting of :meth:`.rollback` or
    :meth:`.commit` calls in context with various flavors of :meth:`.begin`.
    This nesting behavior applies to when :meth:`.Session.begin_nested`
    is used to emit a SAVEPOINT transaction, and is also used to produce
    a so-called "subtransaction" which allows a block of code to use a
    begin/rollback/commit sequence regardless of whether or not its enclosing
    code block has begun a transaction.  The :meth:`.flush` method, whether called
    explicitly or via autoflush, is the primary consumer of the "subtransaction"
    feature, in that it wishes to guarantee that it works within in a transaction block
    regardless of whether or not the :class:`.Session` is in transactional mode
    when the method is called.

    See also:

    :meth:`.Session.rollback`

    :meth:`.Session.commit`

    :meth:`.Session.begin`

    :meth:`.Session.begin_nested`

    :attr:`.Session.is_active`

    :meth:`.SessionEvents.after_commit`

    :meth:`.SessionEvents.after_rollback`

    :meth:`.SessionEvents.after_soft_rollback`

    """

    _rollback_exception = None

    def __init__(self, session, parent=None, nested=False):
        self.session = session
        self._connections = {}
        self._parent = parent
        self.nested = nested
        self._active = True
        self._prepared = False
        if not parent and nested:
            raise sa_exc.InvalidRequestError(
                "Can't start a SAVEPOINT transaction when no existing "
                "transaction is in progress")

        if self.session._enable_transaction_accounting:
            self._take_snapshot()

    @property
    def is_active(self):
        return self.session is not None and self._active

    def _assert_is_active(self):
        self._assert_is_open()
        if not self._active:
            if self._rollback_exception:
                raise sa_exc.InvalidRequestError(
                    "This Session's transaction has been rolled back "
                    "due to a previous exception during flush."
                    " To begin a new transaction with this Session, "
                    "first issue Session.rollback()."
                    " Original exception was: %s"
                    % self._rollback_exception
                )
            else:
                raise sa_exc.InvalidRequestError(
                    "This Session's transaction has been rolled back "
                    "by a nested rollback() call.  To begin a new "
                    "transaction, issue Session.rollback() first."
                    )

    def _assert_is_open(self, error_msg="The transaction is closed"):
        if self.session is None:
            raise sa_exc.ResourceClosedError(error_msg)

    @property
    def _is_transaction_boundary(self):
        return self.nested or not self._parent

    def connection(self, bindkey, **kwargs):
        self._assert_is_active()
        bind = self.session.get_bind(bindkey, **kwargs)
        return self._connection_for_bind(bind)

    def _begin(self, nested=False):
        self._assert_is_active()
        return SessionTransaction(
            self.session, self, nested=nested)

    def _iterate_parents(self, upto=None):
        if self._parent is upto:
            return (self,)
        else:
            if self._parent is None:
                raise sa_exc.InvalidRequestError(
                    "Transaction %s is not on the active transaction list" % (
                    upto))
            return (self,) + self._parent._iterate_parents(upto)

    def _take_snapshot(self):
        if not self._is_transaction_boundary:
            self._new = self._parent._new
            self._deleted = self._parent._deleted
            self._dirty = self._parent._dirty
            return

        if not self.session._flushing:
            self.session.flush()

        self._new = weakref.WeakKeyDictionary()
        self._deleted = weakref.WeakKeyDictionary()
        self._dirty = weakref.WeakKeyDictionary()

    def _restore_snapshot(self, dirty_only=False):
        assert self._is_transaction_boundary

        for s in set(self._new).union(self.session._new):
            self.session._expunge_state(s)
            if s.key:
                del s.key

        for s in set(self._deleted).union(self.session._deleted):
            if s.deleted:
                #assert s in self._deleted
                del s.deleted
            self.session._update_impl(s)

        assert not self.session._deleted

        for s in self.session.identity_map.all_states():
            if not dirty_only or s.modified or s in self._dirty:
                s._expire(s.dict, self.session.identity_map._modified)

    def _remove_snapshot(self):
        assert self._is_transaction_boundary

        if not self.nested and self.session.expire_on_commit:
            for s in self.session.identity_map.all_states():
                s._expire(s.dict, self.session.identity_map._modified)

    def _connection_for_bind(self, bind):
        self._assert_is_active()

        if bind in self._connections:
            return self._connections[bind][0]

        if self._parent:
            conn = self._parent._connection_for_bind(bind)
            if not self.nested:
                return conn
        else:
            if isinstance(bind, engine.Connection):
                conn = bind
                if conn.engine in self._connections:
                    raise sa_exc.InvalidRequestError(
                        "Session already has a Connection associated for the "
                        "given Connection's Engine")
            else:
                conn = bind.contextual_connect()

        if self.session.twophase and self._parent is None:
            transaction = conn.begin_twophase()
        elif self.nested:
            transaction = conn.begin_nested()
        else:
            transaction = conn.begin()

        self._connections[conn] = self._connections[conn.engine] = \
          (conn, transaction, conn is not bind)
        self.session.dispatch.after_begin(self.session, self, conn)
        return conn

    def prepare(self):
        if self._parent is not None or not self.session.twophase:
            raise sa_exc.InvalidRequestError(
                "Only root two phase transactions of can be prepared")
        self._prepare_impl()

    def _prepare_impl(self):
        self._assert_is_active()
        if self._parent is None or self.nested:
            self.session.dispatch.before_commit(self.session)

        stx = self.session.transaction
        if stx is not self:
            for subtransaction in stx._iterate_parents(upto=self):
                subtransaction.commit()

        if not self.session._flushing:
            self.session.flush()

        if self._parent is None and self.session.twophase:
            try:
                for t in set(self._connections.values()):
                    t[1].prepare()
            except:
                self.rollback()
                raise

        self._deactivate()
        self._prepared = True

    def commit(self):
        self._assert_is_open()
        if not self._prepared:
            self._prepare_impl()

        if self._parent is None or self.nested:
            for t in set(self._connections.values()):
                t[1].commit()

            self.session.dispatch.after_commit(self.session)

            if self.session._enable_transaction_accounting:
                self._remove_snapshot()

        self.close()
        return self._parent

    def rollback(self, _capture_exception=False):
        self._assert_is_open()

        stx = self.session.transaction
        if stx is not self:
            for subtransaction in stx._iterate_parents(upto=self):
                subtransaction.close()

        if self.is_active or self._prepared:
            for transaction in self._iterate_parents():
                if transaction._parent is None or transaction.nested:
                    transaction._rollback_impl()
                    transaction._deactivate()
                    break
                else:
                    transaction._deactivate()

        sess = self.session

        if self.session._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.")
            self._restore_snapshot(dirty_only=self.nested)

        self.close()
        if self._parent and _capture_exception:
            self._parent._rollback_exception = sys.exc_info()[1]

        sess.dispatch.after_soft_rollback(sess, self)

        return self._parent

    def _rollback_impl(self):
        for t in set(self._connections.values()):
            t[1].rollback()

        if self.session._enable_transaction_accounting:
            self._restore_snapshot(dirty_only=self.nested)

        self.session.dispatch.after_rollback(self.session)

    def _deactivate(self):
        self._active = False

    def close(self):
        self.session.transaction = self._parent
        if self._parent is None:
            for connection, transaction, autoclose in \
                set(self._connections.values()):
                if autoclose:
                    connection.close()
                else:
                    transaction.close()
            if not self.session.autocommit:
                self.session.begin()
        self._deactivate()
        self.session = None
        self._connections = None

    def __enter__(self):
        return self

    def __exit__(self, type, value, traceback):
        self._assert_is_open("Cannot end transaction context. The transaction "
                                    "was closed from within the context")
        if self.session.transaction is None:
            return
        if type is None:
            try:
                self.commit()
            except:
                self.rollback()
                raise
        else:
            self.rollback()

class Session(object):
    """Manages persistence operations for ORM-mapped objects.

    The Session's usage paradigm is described at :ref:`session_toplevel`.


    """

    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',
        '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,
                 query_cls=query.Query):
        """Construct a new Session.

        See also the :func:`.sessionmaker` function which is used to
        generate a :class:`.Session`-producing callable with a given
        set of arguments.

        :param autocommit: Defaults to ``False``. When ``True``, the ``Session``
          does not keep a persistent transaction running, and will acquire
          connections from the engine on an as-needed basis, returning them
          immediately after their use. Flushes will begin and commit (or possibly
          rollback) their own transaction if no transaction is present. When using
          this mode, the `session.begin()` method may be used to begin a
          transaction explicitly.

          Leaving it on its default value of ``False`` means that the ``Session``
          will acquire a connection and begin a transaction the first time it is
          used, which it will maintain persistently until ``rollback()``,
          ``commit()``, or ``close()`` is called. When the transaction is released
          by any of these methods, the ``Session`` is ready for the next usage,
          which will again acquire and maintain a new connection/transaction.

        :param autoflush: When ``True``, all query operations will issue a
           ``flush()`` call to this ``Session`` before proceeding. This is a
           convenience feature so that ``flush()`` need not be called repeatedly
           in order for database queries to retrieve results. It's typical that
           ``autoflush`` is used in conjunction with ``autocommit=False``. In this
           scenario, explicit calls to ``flush()`` are rarely needed; you usually
           only need to call ``commit()`` (which flushes) to finalize changes.

        :param bind: An optional ``Engine`` or ``Connection`` to which this
           ``Session`` should be bound. When specified, all SQL operations
           performed by this session will execute via this connectable.

        :param binds: An optional dictionary which contains more granular "bind"
           information than the ``bind`` parameter provides. This dictionary can
           map individual ``Table`` instances as well as ``Mapper`` instances to
           individual ``Engine`` or ``Connection`` objects. Operations which
           proceed relative to a particular ``Mapper`` will consult this
           dictionary for the direct ``Mapper`` instance as well as the mapper's
           ``mapped_table`` attribute in order to locate an connectable to use.
           The full resolution is described in the ``get_bind()`` method of
           ``Session``. Usage looks like::

            Session = sessionmaker(binds={
                SomeMappedClass: create_engine('postgresql://engine1'),
                somemapper: create_engine('postgresql://engine2'),
                some_table: create_engine('postgresql://engine3'),
                })

          Also see the :meth:`.Session.bind_mapper` and :meth:`.Session.bind_table` methods.

        :param \class_: Specify an alternate class other than
           ``sqlalchemy.orm.session.Session`` which should be used by the returned
           class. This is the only argument that is local to the
           ``sessionmaker()`` function, and is not sent directly to the
           constructor for ``Session``.

        :param _enable_transaction_accounting:  Defaults to ``True``.  A
           legacy-only flag which when ``False`` disables *all* 0.5-style object
           accounting on transaction boundaries, including auto-expiry of
           instances on rollback and commit, maintenance of the "new" and
           "deleted" lists upon rollback, and autoflush of pending changes upon
           begin(), all of which are interdependent.

        :param expire_on_commit:  Defaults to ``True``. When ``True``, all
           instances will be fully expired after each ``commit()``, so that all
           attribute/object access subsequent to a completed transaction will load
           from the most recent database state.

        :param extension: An optional
           :class:`~.SessionExtension` instance, or a list
           of such instances, which will receive pre- and post- commit and flush
           events, as well as a post-rollback event. **Deprecated.**
           Please see :class:`.SessionEvents`.

        :param query_cls:  Class which should be used to create new Query objects,
           as returned by the ``query()`` method. Defaults to
           :class:`~sqlalchemy.orm.query.Query`.

        :param twophase:  When ``True``, all transactions will be started as
            a "two phase" transaction, i.e. using the "two phase" semantics
            of the database in use along with an XID.  During a ``commit()``,
            after ``flush()`` has been issued for all attached databases, the
            ``prepare()`` method on each database's ``TwoPhaseTransaction`` will
            be called. This allows each database to roll back the entire
            transaction, before each transaction is committed.

        :param weak_identity_map:  Defaults to ``True`` - when set to
           ``False``, objects placed in the :class:`.Session` will be
           strongly referenced until explicitly removed or the
           :class:`.Session` is closed.  **Deprecated** - this option
           is obsolete.

        """

        if weak_identity_map:
            self._identity_cls = identity.WeakInstanceDict
        else:
            util.warn_deprecated("weak_identity_map=False is deprecated.  "
                                    "This feature is not needed.")
            self._identity_cls = identity.StrongInstanceDict
        self.identity_map = self._identity_cls()

        self._new = {}   # InstanceState->object, strong refs object
        self._deleted = {}  # same
        self.bind = bind
        self.__binds = {}
        self._flushing = False
        self.transaction = None
        self.hash_key = _new_sessionid()
        self.autoflush = autoflush
        self.autocommit = autocommit
        self.expire_on_commit = expire_on_commit
        self._enable_transaction_accounting = _enable_transaction_accounting
        self.twophase = twophase
        self._query_cls = query_cls

        if extension:
            for ext in util.to_list(extension):
                SessionExtension._adapt_listener(self, ext)

        if binds is not None:
            for mapperortable, bind in binds.iteritems():
                if isinstance(mapperortable, (type, Mapper)):
                    self.bind_mapper(mapperortable, bind)
                else:
                    self.bind_table(mapperortable, bind)

        if not self.autocommit:
            self.begin()
        _sessions[self.hash_key] = self

    dispatch = event.dispatcher(SessionEvents)

    connection_callable = None

    transaction = None
    """The current active or inactive :class:`.SessionTransaction`."""

    def begin(self, subtransactions=False, nested=False):
        """Begin a transaction on this Session.

        If this Session is already within a transaction, either a plain
        transaction or nested transaction, an error is raised, unless
        ``subtransactions=True`` or ``nested=True`` is specified.

        The ``subtransactions=True`` flag indicates that this :meth:`~.Session.begin`
        can create a subtransaction if a transaction is already in progress.
        For documentation on subtransactions, please see :ref:`session_subtransactions`.

        The ``nested`` flag begins a SAVEPOINT transaction and is equivalent
        to calling :meth:`~.Session.begin_nested`. For documentation on SAVEPOINT
        transactions, please see :ref:`session_begin_nested`.

        """
        if self.transaction is not None:
            if subtransactions or nested:
                self.transaction = self.transaction._begin(
                                        nested=nested)
            else:
                raise sa_exc.InvalidRequestError(
                    "A transaction is already begun.  Use subtransactions=True "
                    "to allow subtransactions.")
        else:
            self.transaction = SessionTransaction(
                self, nested=nested)
        return self.transaction  # needed for __enter__/__exit__ hook

    def begin_nested(self):
        """Begin a `nested` transaction on this Session.

        The target database(s) must support SQL SAVEPOINTs or a
        SQLAlchemy-supported vendor implementation of the idea.

        For documentation on SAVEPOINT
        transactions, please see :ref:`session_begin_nested`.

        """
        return self.begin(nested=True)

    def rollback(self):
        """Rollback the current transaction in progress.

        If no transaction is in progress, this method is a pass-through.

        This method rolls back the current transaction or nested transaction
        regardless of subtransactions being in effect.  All subtransactions up
        to the first real transaction are closed.  Subtransactions occur when
        begin() is called multiple times.

        """
        if self.transaction is None:
            pass
        else:
            self.transaction.rollback()

    def commit(self):
        """Flush pending changes and commit the current transaction.

        If no transaction is in progress, this method raises an
        InvalidRequestError.

        By default, the :class:`.Session` also expires all database
        loaded state on all ORM-managed attributes after transaction commit.
        This so that subsequent operations load the most recent
        data from the database.   This behavior can be disabled using
        the ``expire_on_commit=False`` option to :func:`.sessionmaker` or
        the :class:`.Session` constructor.

        If a subtransaction is in effect (which occurs when begin() is called
        multiple times), the subtransaction will be closed, and the next call
        to ``commit()`` will operate on the enclosing transaction.

        For a session configured with autocommit=False, a new transaction will
        be begun immediately after the commit, but note that the newly begun
        transaction does *not* use any connection resources until the first
        SQL is actually emitted.

        """
        if self.transaction is None:
            if not self.autocommit:
                self.begin()
            else:
                raise sa_exc.InvalidRequestError("No transaction is begun.")

        self.transaction.commit()

    def prepare(self):
        """Prepare the current transaction in progress for two phase commit.

        If no transaction is in progress, this method raises an
        InvalidRequestError.

        Only root transactions of two phase sessions can be prepared. If the
        current transaction is not such, an InvalidRequestError is raised.

        """
        if self.transaction is None:
            if not self.autocommit:
                self.begin()
            else:
                raise sa_exc.InvalidRequestError("No transaction is begun.")

        self.transaction.prepare()

    def connection(self, mapper=None, clause=None,
                        bind=None,
                        close_with_result=False,
                        **kw):
        """Return a :class:`.Connection` object corresponding to this
        :class:`.Session` object's transactional state.

        If this :class:`.Session` is configured with ``autocommit=False``,
        either the :class:`.Connection` corresponding to the current transaction
        is returned, or if no transaction is in progress, a new one is begun
        and the :class:`.Connection` returned (note that no transactional state
        is established with the DBAPI until the first SQL statement is emitted).

        Alternatively, if this :class:`.Session` is configured with ``autocommit=True``,
        an ad-hoc :class:`.Connection` is returned using :meth:`.Engine.contextual_connect`
        on the underlying :class:`.Engine`.

        Ambiguity in multi-bind or unbound :class:`.Session` objects can be resolved through
        any of the optional keyword arguments.   This ultimately makes usage of the
        :meth:`.get_bind` method for resolution.

        :param bind:
          Optional :class:`.Engine` to be used as the bind.  If
          this engine is already involved in an ongoing transaction,
          that connection will be used.  This argument takes precedence
          over ``mapper``, ``clause``.

        :param mapper:
          Optional :func:`.mapper` mapped class, used to identify
          the appropriate bind.  This argument takes precedence over
          ``clause``.

        :param clause:
            A :class:`.ClauseElement` (i.e. :func:`~.sql.expression.select`,
            :func:`~.sql.expression.text`,
            etc.) which will be used to locate a bind, if a bind
            cannot otherwise be identified.

        :param close_with_result: Passed to :meth:`Engine.connect`, indicating
          the :class:`.Connection` should be considered "single use", automatically
          closing when the first result set is closed.  This flag only has
          an effect if this :class:`.Session` is configured with ``autocommit=True``
          and does not already have a  transaction in progress.

        :param \**kw:
          Additional keyword arguments are sent to :meth:`get_bind()`,
          allowing additional arguments to be passed to custom
          implementations of :meth:`get_bind`.

        """
        if bind is None:
            bind = self.get_bind(mapper, clause=clause, **kw)

        return self._connection_for_bind(bind,
                                        close_with_result=close_with_result)

    def _connection_for_bind(self, engine, **kwargs):
        if self.transaction is not None:
            return self.transaction._connection_for_bind(engine)
        else:
            return engine.contextual_connect(**kwargs)

    def execute(self, clause, params=None, mapper=None, bind=None, **kw):
        """Execute a clause within the current transaction.

        Returns a :class:`.ResultProxy` representing
        results of the statement execution, in the same manner as that of an
        :class:`.Engine` or
        :class:`.Connection`.

        :meth:`~.Session.execute` accepts any executable clause construct, such
        as :func:`~.sql.expression.select`,
        :func:`~.sql.expression.insert`,
        :func:`~.sql.expression.update`,
        :func:`~.sql.expression.delete`, and
        :func:`~.sql.expression.text`, and additionally accepts
        plain strings that represent SQL statements. If a plain string is
        passed, it is first converted to a
        :func:`~.sql.expression.text` construct, which here means
        that bind parameters should be specified using the format ``:param``.
        If raw DBAPI statement execution is desired, use :meth:`.Session.connection`
        to acquire a :class:`.Connection`, then call its :meth:`~.Connection.execute`
        method.

        The statement is executed within the current transactional context of
        this :class:`.Session`, using the same behavior as that of
        the :meth:`.Session.connection` method to determine the active
        :class:`.Connection`.   The ``close_with_result`` flag is
        set to ``True`` so that an ``autocommit=True`` :class:`.Session`
        with no active transaction will produce a result that auto-closes
        the underlying :class:`.Connection`.

        :param clause:
            A :class:`.ClauseElement` (i.e. :func:`~.sql.expression.select`,
            :func:`~.sql.expression.text`, etc.) or string SQL statement to be executed.  The clause
            will also be used to locate a bind, if this :class:`.Session`
            is not bound to a single engine already, and the ``mapper``
            and ``bind`` arguments are not passed.

        :param params:
            Optional dictionary of bind names mapped to values.

        :param mapper:
          Optional :func:`.mapper` or mapped class, used to identify
          the appropriate bind.  This argument takes precedence over
          ``clause`` when locating a bind.

        :param bind:
          Optional :class:`.Engine` to be used as the bind.  If
          this engine is already involved in an ongoing transaction,
          that connection will be used.  This argument takes
          precedence over ``mapper`` and ``clause`` when locating
          a bind.

        :param \**kw:
          Additional keyword arguments are sent to :meth:`get_bind()`,
          allowing additional arguments to be passed to custom
          implementations of :meth:`get_bind`.

        """
        clause = expression._literal_as_text(clause)<commit_msg>add a note, no autoflush with execute, [ticket:2550]<commit_after># orm/session.py
# Copyright (C) 2005-2012 the SQLAlchemy authors and contributors <see AUTHORS file>
#
# This module is part of SQLAlchemy and is released under
# the MIT License: http://www.opensource.org/licenses/mit-license.php

"""Provides the Session class and related utilities."""

import weakref
from .. import util, sql, engine, exc as sa_exc, event
from ..sql import util as sql_util, expression
from . import (
    SessionExtension, attributes, exc, query, util as orm_util,
    loading, identity
    )
from .util import (
    object_mapper, class_mapper,
    _class_to_mapper, _state_mapper, object_state,
    _none_set
    )
from .unitofwork import UOWTransaction
from .mapper import Mapper
from .events import SessionEvents
statelib = util.importlater("sqlalchemy.orm", "state")
import sys

__all__ = ['Session', 'SessionTransaction', 'SessionExtension']


def sessionmaker(bind=None, class_=None, autoflush=True, autocommit=False,
                 expire_on_commit=True, **kwargs):
    """Generate a custom-configured :class:`.Session` class.

    The returned object is a subclass of :class:`.Session`, which,
    when instantiated with no arguments, uses the keyword arguments
    configured here as its constructor arguments.

    It is intended that the :func:`.sessionmaker()` function be called
    within the global scope of an application, and the returned class
    be made available to the rest of the application as the single
    class used to instantiate sessions.

    e.g.::

        # global scope
        Session = sessionmaker(autoflush=False)

        # later, in a local scope, create and use a session:
        sess = Session()

    Any keyword arguments sent to the constructor itself will override the
    "configured" keywords::

        Session = sessionmaker()

        # bind an individual session to a connection
        sess = Session(bind=connection)

    The class also includes a special classmethod ``configure()``, which
    allows additional configurational options to take place after the custom
    ``Session`` class has been generated.  This is useful particularly for
    defining the specific ``Engine`` (or engines) to which new instances of
    ``Session`` should be bound::

        Session = sessionmaker()
        Session.configure(bind=create_engine('sqlite:///foo.db'))

        sess = Session()

    For options, see the constructor options for :class:`.Session`.

    """
    kwargs['bind'] = bind
    kwargs['autoflush'] = autoflush
    kwargs['autocommit'] = autocommit
    kwargs['expire_on_commit'] = expire_on_commit

    if class_ is None:
        class_ = Session

    class Sess(object):
        def __init__(self, **local_kwargs):
            for k in kwargs:
                local_kwargs.setdefault(k, kwargs[k])
            super(Sess, self).__init__(**local_kwargs)

        @classmethod
        def configure(self, **new_kwargs):
            """(Re)configure the arguments for this sessionmaker.

            e.g.::

                Session = sessionmaker()

                Session.configure(bind=create_engine('sqlite://'))
            """
            kwargs.update(new_kwargs)


    return type("SessionMaker", (Sess, class_), {})


class SessionTransaction(object):
    """A :class:`.Session`-level transaction.

    :class:`.SessionTransaction` is a mostly behind-the-scenes object
    not normally referenced directly by application code.   It coordinates
    among multiple :class:`.Connection` objects, maintaining a database
    transaction for each one individually, committing or rolling them
    back all at once.   It also provides optional two-phase commit behavior
    which can augment this coordination operation.

    The :attr:`.Session.transaction` attribute of :class:`.Session` refers to the
    current :class:`.SessionTransaction` object in use, if any.


    A :class:`.SessionTransaction` is associated with a :class:`.Session`
    in its default mode of ``autocommit=False`` immediately, associated
    with no database connections.  As the :class:`.Session` is called upon
    to emit SQL on behalf of various :class:`.Engine` or :class:`.Connection`
    objects, a corresponding :class:`.Connection` and associated :class:`.Transaction`
    is added to a collection within the :class:`.SessionTransaction` object,
    becoming one of the connection/transaction pairs maintained by the
    :class:`.SessionTransaction`.

    The lifespan of the :class:`.SessionTransaction` ends when the
    :meth:`.Session.commit`, :meth:`.Session.rollback` or :meth:`.Session.close`
    methods are called.  At this point, the :class:`.SessionTransaction` removes
    its association with its parent :class:`.Session`.   A :class:`.Session`
    that is in ``autocommit=False`` mode will create a new
    :class:`.SessionTransaction` to replace it immediately, whereas a
    :class:`.Session` that's in ``autocommit=True``
    mode will remain without a :class:`.SessionTransaction` until the
    :meth:`.Session.begin` method is called.

    Another detail of :class:`.SessionTransaction` behavior is that it is
    capable of "nesting".  This means that the :meth:`.begin` method can
    be called while an existing :class:`.SessionTransaction` is already present,
    producing a new :class:`.SessionTransaction` that temporarily replaces
    the parent :class:`.SessionTransaction`.   When a :class:`.SessionTransaction`
    is produced as nested, it assigns itself to the :attr:`.Session.transaction`
    attribute.  When it is ended via :meth:`.Session.commit` or :meth:`.Session.rollback`,
    it restores its parent :class:`.SessionTransaction` back onto the
    :attr:`.Session.transaction` attribute.  The
    behavior is effectively a stack, where :attr:`.Session.transaction` refers
    to the current head of the stack.

    The purpose of this stack is to allow nesting of :meth:`.rollback` or
    :meth:`.commit` calls in context with various flavors of :meth:`.begin`.
    This nesting behavior applies to when :meth:`.Session.begin_nested`
    is used to emit a SAVEPOINT transaction, and is also used to produce
    a so-called "subtransaction" which allows a block of code to use a
    begin/rollback/commit sequence regardless of whether or not its enclosing
    code block has begun a transaction.  The :meth:`.flush` method, whether called
    explicitly or via autoflush, is the primary consumer of the "subtransaction"
    feature, in that it wishes to guarantee that it works within in a transaction block
    regardless of whether or not the :class:`.Session` is in transactional mode
    when the method is called.

    See also:

    :meth:`.Session.rollback`

    :meth:`.Session.commit`

    :meth:`.Session.begin`

    :meth:`.Session.begin_nested`

    :attr:`.Session.is_active`

    :meth:`.SessionEvents.after_commit`

    :meth:`.SessionEvents.after_rollback`

    :meth:`.SessionEvents.after_soft_rollback`

    """

    _rollback_exception = None

    def __init__(self, session, parent=None, nested=False):
        self.session = session
        self._connections = {}
        self._parent = parent
        self.nested = nested
        self._active = True
        self._prepared = False
        if not parent and nested:
            raise sa_exc.InvalidRequestError(
                "Can't start a SAVEPOINT transaction when no existing "
                "transaction is in progress")

        if self.session._enable_transaction_accounting:
            self._take_snapshot()

    @property
    def is_active(self):
        return self.session is not None and self._active

    def _assert_is_active(self):
        self._assert_is_open()
        if not self._active:
            if self._rollback_exception:
                raise sa_exc.InvalidRequestError(
                    "This Session's transaction has been rolled back "
                    "due to a previous exception during flush."
                    " To begin a new transaction with this Session, "
                    "first issue Session.rollback()."
                    " Original exception was: %s"
                    % self._rollback_exception
                )
            else:
                raise sa_exc.InvalidRequestError(
                    "This Session's transaction has been rolled back "
                    "by a nested rollback() call.  To begin a new "
                    "transaction, issue Session.rollback() first."
                    )

    def _assert_is_open(self, error_msg="The transaction is closed"):
        if self.session is None:
            raise sa_exc.ResourceClosedError(error_msg)

    @property
    def _is_transaction_boundary(self):
        return self.nested or not self._parent

    def connection(self, bindkey, **kwargs):
        self._assert_is_active()
        bind = self.session.get_bind(bindkey, **kwargs)
        return self._connection_for_bind(bind)

    def _begin(self, nested=False):
        self._assert_is_active()
        return SessionTransaction(
            self.session, self, nested=nested)

    def _iterate_parents(self, upto=None):
        if self._parent is upto:
            return (self,)
        else:
            if self._parent is None:
                raise sa_exc.InvalidRequestError(
                    "Transaction %s is not on the active transaction list" % (
                    upto))
            return (self,) + self._parent._iterate_parents(upto)

    def _take_snapshot(self):
        if not self._is_transaction_boundary:
            self._new = self._parent._new
            self._deleted = self._parent._deleted
            self._dirty = self._parent._dirty
            return

        if not self.session._flushing:
            self.session.flush()

        self._new = weakref.WeakKeyDictionary()
        self._deleted = weakref.WeakKeyDictionary()
        self._dirty = weakref.WeakKeyDictionary()

    def _restore_snapshot(self, dirty_only=False):
        assert self._is_transaction_boundary

        for s in set(self._new).union(self.session._new):
            self.session._expunge_state(s)
            if s.key:
                del s.key

        for s in set(self._deleted).union(self.session._deleted):
            if s.deleted:
                #assert s in self._deleted
                del s.deleted
            self.session._update_impl(s)

        assert not self.session._deleted

        for s in self.session.identity_map.all_states():
            if not dirty_only or s.modified or s in self._dirty:
                s._expire(s.dict, self.session.identity_map._modified)

    def _remove_snapshot(self):
        assert self._is_transaction_boundary

        if not self.nested and self.session.expire_on_commit:
            for s in self.session.identity_map.all_states():
                s._expire(s.dict, self.session.identity_map._modified)

    def _connection_for_bind(self, bind):
        self._assert_is_active()

        if bind in self._connections:
            return self._connections[bind][0]

        if self._parent:
            conn = self._parent._connection_for_bind(bind)
            if not self.nested:
                return conn
        else:
            if isinstance(bind, engine.Connection):
                conn = bind
                if conn.engine in self._connections:
                    raise sa_exc.InvalidRequestError(
                        "Session already has a Connection associated for the "
                        "given Connection's Engine")
            else:
                conn = bind.contextual_connect()

        if self.session.twophase and self._parent is None:
            transaction = conn.begin_twophase()
        elif self.nested:
            transaction = conn.begin_nested()
        else:
            transaction = conn.begin()

        self._connections[conn] = self._connections[conn.engine] = \
          (conn, transaction, conn is not bind)
        self.session.dispatch.after_begin(self.session, self, conn)
        return conn

    def prepare(self):
        if self._parent is not None or not self.session.twophase:
            raise sa_exc.InvalidRequestError(
                "Only root two phase transactions of can be prepared")
        self._prepare_impl()

    def _prepare_impl(self):
        self._assert_is_active()
        if self._parent is None or self.nested:
            self.session.dispatch.before_commit(self.session)

        stx = self.session.transaction
        if stx is not self:
            for subtransaction in stx._iterate_parents(upto=self):
                subtransaction.commit()

        if not self.session._flushing:
            self.session.flush()

        if self._parent is None and self.session.twophase:
            try:
                for t in set(self._connections.values()):
                    t[1].prepare()
            except:
                self.rollback()
                raise

        self._deactivate()
        self._prepared = True

    def commit(self):
        self._assert_is_open()
        if not self._prepared:
            self._prepare_impl()

        if self._parent is None or self.nested:
            for t in set(self._connections.values()):
                t[1].commit()

            self.session.dispatch.after_commit(self.session)

            if self.session._enable_transaction_accounting:
                self._remove_snapshot()

        self.close()
        return self._parent

    def rollback(self, _capture_exception=False):
        self._assert_is_open()

        stx = self.session.transaction
        if stx is not self:
            for subtransaction in stx._iterate_parents(upto=self):
                subtransaction.close()

        if self.is_active or self._prepared:
            for transaction in self._iterate_parents():
                if transaction._parent is None or transaction.nested:
                    transaction._rollback_impl()
                    transaction._deactivate()
                    break
                else:
                    transaction._deactivate()

        sess = self.session

        if self.session._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.")
            self._restore_snapshot(dirty_only=self.nested)

        self.close()
        if self._parent and _capture_exception:
            self._parent._rollback_exception = sys.exc_info()[1]

        sess.dispatch.after_soft_rollback(sess, self)

        return self._parent

    def _rollback_impl(self):
        for t in set(self._connections.values()):
            t[1].rollback()

        if self.session._enable_transaction_accounting:
            self._restore_snapshot(dirty_only=self.nested)

        self.session.dispatch.after_rollback(self.session)

    def _deactivate(self):
        self._active = False

    def close(self):
        self.session.transaction = self._parent
        if self._parent is None:
            for connection, transaction, autoclose in \
                set(self._connections.values()):
                if autoclose:
                    connection.close()
                else:
                    transaction.close()
            if not self.session.autocommit:
                self.session.begin()
        self._deactivate()
        self.session = None
        self._connections = None

    def __enter__(self):
        return self

    def __exit__(self, type, value, traceback):
        self._assert_is_open("Cannot end transaction context. The transaction "
                                    "was closed from within the context")
        if self.session.transaction is None:
            return
        if type is None:
            try:
                self.commit()
            except:
                self.rollback()
                raise
        else:
            self.rollback()

class Session(object):
    """Manages persistence operations for ORM-mapped objects.

    The Session's usage paradigm is described at :ref:`session_toplevel`.


    """

    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',
        '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,
                 query_cls=query.Query):
        """Construct a new Session.

        See also the :func:`.sessionmaker` function which is used to
        generate a :class:`.Session`-producing callable with a given
        set of arguments.

        :param autocommit: Defaults to ``False``. When ``True``, the ``Session``
          does not keep a persistent transaction running, and will acquire
          connections from the engine on an as-needed basis, returning them
          immediately after their use. Flushes will begin and commit (or possibly
          rollback) their own transaction if no transaction is present. When using
          this mode, the `session.begin()` method may be used to begin a
          transaction explicitly.

          Leaving it on its default value of ``False`` means that the ``Session``
          will acquire a connection and begin a transaction the first time it is
          used, which it will maintain persistently until ``rollback()``,
          ``commit()``, or ``close()`` is called. When the transaction is released
          by any of these methods, the ``Session`` is ready for the next usage,
          which will again acquire and maintain a new connection/transaction.

        :param autoflush: When ``True``, all query operations will issue a
           ``flush()`` call to this ``Session`` before proceeding. This is a
           convenience feature so that ``flush()`` need not be called repeatedly
           in order for database queries to retrieve results. It's typical that
           ``autoflush`` is used in conjunction with ``autocommit=False``. In this
           scenario, explicit calls to ``flush()`` are rarely needed; you usually
           only need to call ``commit()`` (which flushes) to finalize changes.

        :param bind: An optional ``Engine`` or ``Connection`` to which this
           ``Session`` should be bound. When specified, all SQL operations
           performed by this session will execute via this connectable.

        :param binds: An optional dictionary which contains more granular "bind"
           information than the ``bind`` parameter provides. This dictionary can
           map individual ``Table`` instances as well as ``Mapper`` instances to
           individual ``Engine`` or ``Connection`` objects. Operations which
           proceed relative to a particular ``Mapper`` will consult this
           dictionary for the direct ``Mapper`` instance as well as the mapper's
           ``mapped_table`` attribute in order to locate an connectable to use.
           The full resolution is described in the ``get_bind()`` method of
           ``Session``. Usage looks like::

            Session = sessionmaker(binds={
                SomeMappedClass: create_engine('postgresql://engine1'),
                somemapper: create_engine('postgresql://engine2'),
                some_table: create_engine('postgresql://engine3'),
                })

          Also see the :meth:`.Session.bind_mapper` and :meth:`.Session.bind_table` methods.

        :param \class_: Specify an alternate class other than
           ``sqlalchemy.orm.session.Session`` which should be used by the returned
           class. This is the only argument that is local to the
           ``sessionmaker()`` function, and is not sent directly to the
           constructor for ``Session``.

        :param _enable_transaction_accounting:  Defaults to ``True``.  A
           legacy-only flag which when ``False`` disables *all* 0.5-style object
           accounting on transaction boundaries, including auto-expiry of
           instances on rollback and commit, maintenance of the "new" and
           "deleted" lists upon rollback, and autoflush of pending changes upon
           begin(), all of which are interdependent.

        :param expire_on_commit:  Defaults to ``True``. When ``True``, all
           instances will be fully expired after each ``commit()``, so that all
           attribute/object access subsequent to a completed transaction will load
           from the most recent database state.

        :param extension: An optional
           :class:`~.SessionExtension` instance, or a list
           of such instances, which will receive pre- and post- commit and flush
           events, as well as a post-rollback event. **Deprecated.**
           Please see :class:`.SessionEvents`.

        :param query_cls:  Class which should be used to create new Query objects,
           as returned by the ``query()`` method. Defaults to
           :class:`~sqlalchemy.orm.query.Query`.

        :param twophase:  When ``True``, all transactions will be started as
            a "two phase" transaction, i.e. using the "two phase" semantics
            of the database in use along with an XID.  During a ``commit()``,
            after ``flush()`` has been issued for all attached databases, the
            ``prepare()`` method on each database's ``TwoPhaseTransaction`` will
            be called. This allows each database to roll back the entire
            transaction, before each transaction is committed.

        :param weak_identity_map:  Defaults to ``True`` - when set to
           ``False``, objects placed in the :class:`.Session` will be
           strongly referenced until explicitly removed or the
           :class:`.Session` is closed.  **Deprecated** - this option
           is obsolete.

        """

        if weak_identity_map:
            self._identity_cls = identity.WeakInstanceDict
        else:
            util.warn_deprecated("weak_identity_map=False is deprecated.  "
                                    "This feature is not needed.")
            self._identity_cls = identity.StrongInstanceDict
        self.identity_map = self._identity_cls()

        self._new = {}   # InstanceState->object, strong refs object
        self._deleted = {}  # same
        self.bind = bind
        self.__binds = {}
        self._flushing = False
        self.transaction = None
        self.hash_key = _new_sessionid()
        self.autoflush = autoflush
        self.autocommit = autocommit
        self.expire_on_commit = expire_on_commit
        self._enable_transaction_accounting = _enable_transaction_accounting
        self.twophase = twophase
        self._query_cls = query_cls

        if extension:
            for ext in util.to_list(extension):
                SessionExtension._adapt_listener(self, ext)

        if binds is not None:
            for mapperortable, bind in binds.iteritems():
                if isinstance(mapperortable, (type, Mapper)):
                    self.bind_mapper(mapperortable, bind)
                else:
                    self.bind_table(mapperortable, bind)

        if not self.autocommit:
            self.begin()
        _sessions[self.hash_key] = self

    dispatch = event.dispatcher(SessionEvents)

    connection_callable = None

    transaction = None
    """The current active or inactive :class:`.SessionTransaction`."""

    def begin(self, subtransactions=False, nested=False):
        """Begin a transaction on this Session.

        If this Session is already within a transaction, either a plain
        transaction or nested transaction, an error is raised, unless
        ``subtransactions=True`` or ``nested=True`` is specified.

        The ``subtransactions=True`` flag indicates that this :meth:`~.Session.begin`
        can create a subtransaction if a transaction is already in progress.
        For documentation on subtransactions, please see :ref:`session_subtransactions`.

        The ``nested`` flag begins a SAVEPOINT transaction and is equivalent
        to calling :meth:`~.Session.begin_nested`. For documentation on SAVEPOINT
        transactions, please see :ref:`session_begin_nested`.

        """
        if self.transaction is not None:
            if subtransactions or nested:
                self.transaction = self.transaction._begin(
                                        nested=nested)
            else:
                raise sa_exc.InvalidRequestError(
                    "A transaction is already begun.  Use subtransactions=True "
                    "to allow subtransactions.")
        else:
            self.transaction = SessionTransaction(
                self, nested=nested)
        return self.transaction  # needed for __enter__/__exit__ hook

    def begin_nested(self):
        """Begin a `nested` transaction on this Session.

        The target database(s) must support SQL SAVEPOINTs or a
        SQLAlchemy-supported vendor implementation of the idea.

        For documentation on SAVEPOINT
        transactions, please see :ref:`session_begin_nested`.

        """
        return self.begin(nested=True)

    def rollback(self):
        """Rollback the current transaction in progress.

        If no transaction is in progress, this method is a pass-through.

        This method rolls back the current transaction or nested transaction
        regardless of subtransactions being in effect.  All subtransactions up
        to the first real transaction are closed.  Subtransactions occur when
        begin() is called multiple times.

        """
        if self.transaction is None:
            pass
        else:
            self.transaction.rollback()

    def commit(self):
        """Flush pending changes and commit the current transaction.

        If no transaction is in progress, this method raises an
        InvalidRequestError.

        By default, the :class:`.Session` also expires all database
        loaded state on all ORM-managed attributes after transaction commit.
        This so that subsequent operations load the most recent
        data from the database.   This behavior can be disabled using
        the ``expire_on_commit=False`` option to :func:`.sessionmaker` or
        the :class:`.Session` constructor.

        If a subtransaction is in effect (which occurs when begin() is called
        multiple times), the subtransaction will be closed, and the next call
        to ``commit()`` will operate on the enclosing transaction.

        For a session configured with autocommit=False, a new transaction will
        be begun immediately after the commit, but note that the newly begun
        transaction does *not* use any connection resources until the first
        SQL is actually emitted.

        """
        if self.transaction is None:
            if not self.autocommit:
                self.begin()
            else:
                raise sa_exc.InvalidRequestError("No transaction is begun.")

        self.transaction.commit()

    def prepare(self):
        """Prepare the current transaction in progress for two phase commit.

        If no transaction is in progress, this method raises an
        InvalidRequestError.

        Only root transactions of two phase sessions can be prepared. If the
        current transaction is not such, an InvalidRequestError is raised.

        """
        if self.transaction is None:
            if not self.autocommit:
                self.begin()
            else:
                raise sa_exc.InvalidRequestError("No transaction is begun.")

        self.transaction.prepare()

    def connection(self, mapper=None, clause=None,
                        bind=None,
                        close_with_result=False,
                        **kw):
        """Return a :class:`.Connection` object corresponding to this
        :class:`.Session` object's transactional state.

        If this :class:`.Session` is configured with ``autocommit=False``,
        either the :class:`.Connection` corresponding to the current transaction
        is returned, or if no transaction is in progress, a new one is begun
        and the :class:`.Connection` returned (note that no transactional state
        is established with the DBAPI until the first SQL statement is emitted).

        Alternatively, if this :class:`.Session` is configured with ``autocommit=True``,
        an ad-hoc :class:`.Connection` is returned using :meth:`.Engine.contextual_connect`
        on the underlying :class:`.Engine`.

        Ambiguity in multi-bind or unbound :class:`.Session` objects can be resolved through
        any of the optional keyword arguments.   This ultimately makes usage of the
        :meth:`.get_bind` method for resolution.

        :param bind:
          Optional :class:`.Engine` to be used as the bind.  If
          this engine is already involved in an ongoing transaction,
          that connection will be used.  This argument takes precedence
          over ``mapper``, ``clause``.

        :param mapper:
          Optional :func:`.mapper` mapped class, used to identify
          the appropriate bind.  This argument takes precedence over
          ``clause``.

        :param clause:
            A :class:`.ClauseElement` (i.e. :func:`~.sql.expression.select`,
            :func:`~.sql.expression.text`,
            etc.) which will be used to locate a bind, if a bind
            cannot otherwise be identified.

        :param close_with_result: Passed to :meth:`Engine.connect`, indicating
          the :class:`.Connection` should be considered "single use", automatically
          closing when the first result set is closed.  This flag only has
          an effect if this :class:`.Session` is configured with ``autocommit=True``
          and does not already have a  transaction in progress.

        :param \**kw:
          Additional keyword arguments are sent to :meth:`get_bind()`,
          allowing additional arguments to be passed to custom
          implementations of :meth:`get_bind`.

        """
        if bind is None:
            bind = self.get_bind(mapper, clause=clause, **kw)

        return self._connection_for_bind(bind,
                                        close_with_result=close_with_result)

    def _connection_for_bind(self, engine, **kwargs):
        if self.transaction is not None:
            return self.transaction._connection_for_bind(engine)
        else:
            return engine.contextual_connect(**kwargs)

    def execute(self, clause, params=None, mapper=None, bind=None, **kw):
        """Execute a clause within the current transaction.

        Returns a :class:`.ResultProxy` representing
        results of the statement execution, in the same manner as that of an
        :class:`.Engine` or
        :class:`.Connection`.

        :meth:`~.Session.execute` accepts any executable clause construct, such
        as :func:`~.sql.expression.select`,
        :func:`~.sql.expression.insert`,
        :func:`~.sql.expression.update`,
        :func:`~.sql.expression.delete`, and
        :func:`~.sql.expression.text`, and additionally accepts
        plain strings that represent SQL statements. If a plain string is
        passed, it is first converted to a
        :func:`~.sql.expression.text` construct, which here means
        that bind parameters should be specified using the format ``:param``.
        If raw DBAPI statement execution is desired, use :meth:`.Session.connection`
        to acquire a :class:`.Connection`, then call its :meth:`~.Connection.execute`
        method.

        The statement is executed within the current transactional context of
        this :class:`.Session`, using the same behavior as that of
        the :meth:`.Session.connection` method to determine the active
        :class:`.Connection`.   The ``close_with_result`` flag is
        set to ``True`` so that an ``autocommit=True`` :class:`.Session`
        with no active transaction will produce a result that auto-closes
        the underlying :class:`.Connection`.

        The :meth:`.Session.execute` method does *not* invoke autoflush.

        :param clause:
            A :class:`.ClauseElement` (i.e. :func:`~.sql.expression.select`,
            :func:`~.sql.expression.text`, etc.) or string SQL statement to be executed.  The clause
            will also be used to locate a bind, if this :class:`.Session`
            is not bound to a single engine already, and the ``mapper``
            and ``bind`` arguments are not passed.

        :param params:
            Optional dictionary of bind names mapped to values.

        :param mapper:
          Optional :func:`.mapper` or mapped class, used to identify
          the appropriate bind.  This argument takes precedence over
          ``clause`` when locating a bind.

        :param bind:
          Optional :class:`.Engine` to be used as the bind.  If
          this engine is already involved in an ongoing transaction,
          that connection will be used.  This argument takes
          precedence over ``mapper`` and ``clause`` when locating
          a bind.

        :param \**kw:
          Additional keyword arguments are sent to :meth:`get_bind()`,
          allowing additional arguments to be passed to custom
          implementations of :meth:`get_bind`.

        """
        clause = expression._literal_as_text(clause)
{'fraction_non_alphanumeric': 0.08325575209469345, 'fraction_numerical': 0.0005910951515420195, 'mean_word_length': 2.3063956613084478, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 7, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 10, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3076571', 'n_tokens_mistral': 17278, 'n_tokens_neox': 16483, 'n_words': 6103}