Exemple #1
0
def test_same_name():
    v1 = ContextVar('v')
    v2 = ContextVar('v')
    v1.set(1)
    v2.set(2)
    assert v1.get() == 1
    assert v2.get() == 2
Exemple #2
0
class BaseDBAsyncClient:
    query_class = Query
    executor_class = BaseExecutor
    schema_generator = BaseSchemaGenerator
    capabilities = Capabilities("")

    def __init__(self,
                 connection_name: str,
                 fetch_inserted: bool = True,
                 **kwargs) -> None:
        self.log = logging.getLogger("db_client")
        self.connection_name = connection_name
        self.fetch_inserted = fetch_inserted

        self._current_transaction = ContextVar(self.connection_name,
                                               default=self)  # Type: dict

    async def create_connection(self, with_db: bool) -> None:
        raise NotImplementedError()  # pragma: nocoverage

    async def close(self) -> None:
        raise NotImplementedError()  # pragma: nocoverage

    async def db_create(self) -> None:
        raise NotImplementedError()  # pragma: nocoverage

    async def db_delete(self) -> None:
        raise NotImplementedError()  # pragma: nocoverage

    def acquire_connection(self):
        raise NotImplementedError()  # pragma: nocoverage

    def _in_transaction(self) -> "BaseTransactionWrapper":
        raise NotImplementedError()  # pragma: nocoverage

    async def execute_insert(self, query: str, values: list) -> Any:
        raise NotImplementedError()  # pragma: nocoverage

    async def execute_query(self, query: str) -> Sequence[dict]:
        raise NotImplementedError()  # pragma: nocoverage

    async def execute_script(self, query: str) -> None:
        raise NotImplementedError()  # pragma: nocoverage

    def get_current_transaction(self) -> "BaseDBAsyncClient":
        return self._current_transaction.get()
Exemple #3
0
class GinoEngine:
    """
    Connects a :class:`~.dialects.base.Pool` and
    :class:`~sqlalchemy.engine.interfaces.Dialect` together to provide a source
    of database connectivity and behavior.

    A :class:`.GinoEngine` object is instantiated publicly using the
    :func:`gino.create_engine` function or
    :func:`db.set_bind() <gino.api.Gino.set_bind>` method.

    .. seealso::

        :doc:`/engine`

    """

    connection_cls = GinoConnection
    """Customizes the connection class to use, default is
    :class:`.GinoConnection`."""

    def __init__(self, dialect, pool, loop,
                 logging_name=None, echo=None, execution_options=None):
        self._sa_engine = _SAEngine(
            dialect,
            logging_name=logging_name, echo=echo,
            execution_options=execution_options)
        self._dialect = dialect
        self._pool = pool
        self._loop = loop
        self._ctx = ContextVar('gino', default=None)

    @property
    def dialect(self):
        """
        Read-only property for the
        :class:`~sqlalchemy.engine.interfaces.Dialect` of this engine.

        """
        return self._dialect

    @property
    def raw_pool(self):
        """
        Read-only access to the underlying database connection pool instance.
        This depends on the actual dialect in use, :class:`~asyncpg.pool.Pool`
        of asyncpg for example.

        """
        return self._pool.raw_pool

    def acquire(self, *, timeout=None, reuse=False, lazy=False, reusable=True):
        """
        Acquire a connection from the pool.

        There are two ways using this method - as an asynchronous context
        manager::

            async with engine.acquire() as conn:
                # play with the connection

        which will guarantee the connection is returned to the pool when
        leaving the ``async with`` block; or as a coroutine::

            conn = await engine.acquire()
            try:
                # play with the connection
            finally:
                await conn.release()

        where the connection should be manually returned to the pool with
        :meth:`conn.release() <.GinoConnection.release>`.

        Within the same context (usually the same :class:`~asyncio.Task`, see
        also :doc:`/transaction`), a nesting acquire by default re

        :param timeout: Block up to ``timeout`` seconds until there is one free
          connection in the pool. Default is ``None`` - block forever until
          succeeded. This has no effect when ``lazy=True``, and depends on the
          actual situation when ``reuse=True``.

        :param reuse: Reuse the latest reusable acquired connection (before
          it's returned to the pool) in current context if there is one, or
          borrow a new one if none present. Default is ``False`` for always
          borrow a new one. This is useful when you are in a nested method call
          series, wishing to use the same connection without passing it around
          as parameters. See also: :doc:`/transaction`. A reusing connection is
          not reusable even if ``reusable=True``. If the reused connection
          happened to be a lazy one, then the reusing connection is lazy too.

        :param lazy: Don't acquire the actual underlying connection yet - do it
          only when needed. Default is ``False`` for always do it immediately.
          This is useful before entering a code block which may or may not make
          use of a given connection object. Feeding in a lazy connection will
          save the borrow-return job if the connection is never used. If
          setting ``reuse=True`` at the same time, then the reused connection -
          if any - applies the same laziness. For example, reusing a lazy
          connection with ``lazy=False`` will cause the reused connection to
          acquire an underlying connection immediately.

        :param reusable: Mark this connection as reusable or otherwise. This
          has no effect if it is a reusing connection. All reusable connections
          are placed in a stack, any reusing acquire operation will always
          reuse the top (latest) reusable connection. One reusable connection
          may be reused by several reusing connections - they all share one
          same underlying connection. Acquiring a connection with
          ``reusable=False`` and ``reusing=False`` makes it a cleanly isolated
          connection which is only referenced once here.

        :return: A :class:`.GinoConnection` object.

        """
        return _AcquireContext(functools.partial(
            self._acquire, timeout, reuse, lazy, reusable))

    async def _acquire(self, timeout, reuse, lazy, reusable):
        stack = _ContextualStack(self._ctx)
        if reuse and stack:
            dbapi_conn = _ReusingDBAPIConnection(self._dialect.cursor_cls,
                                                 stack.top)
            reusable = False
        else:
            dbapi_conn = _DBAPIConnection(self._dialect.cursor_cls, self._pool)
        rv = self.connection_cls(self._dialect,
                                 _SAConnection(self._sa_engine, dbapi_conn),
                                 stack if reusable else None)
        dbapi_conn.gino_conn = rv
        if not lazy:
            await dbapi_conn.acquire(timeout=timeout)
        if reusable:
            stack.push(dbapi_conn)
        return rv

    @property
    def current_connection(self):
        """
        Gets the most recently acquired reusable connection in the context.
        ``None`` if there is no such connection.

        :return: :class:`.GinoConnection`

        """
        stack = self._ctx.get()
        if stack:
            return stack[-1].gino_conn

    async def close(self):
        """
        Close the engine, by closing the underlying pool.

        """
        await self._pool.close()

    async def all(self, clause, *multiparams, **params):
        """
        Acquires a connection with ``reuse=True`` and runs
        :meth:`~.GinoConnection.all` on it. ``reuse=True`` means you can safely
        do this without borrowing more than one underlying connection::

            async with engine.acquire():
                await engine.all('SELECT ...')

        The same applies for other query methods.

        """
        async with self.acquire(reuse=True) as conn:
            return await conn.all(clause, *multiparams, **params)

    async def first(self, clause, *multiparams, **params):
        """
        Runs :meth:`~.GinoConnection.first`, See :meth:`.all`.

        """
        async with self.acquire(reuse=True) as conn:
            return await conn.first(clause, *multiparams, **params)

    async def scalar(self, clause, *multiparams, **params):
        """
        Runs :meth:`~.GinoConnection.scalar`, See :meth:`.all`.

        """
        async with self.acquire(reuse=True) as conn:
            return await conn.scalar(clause, *multiparams, **params)

    async def status(self, clause, *multiparams, **params):
        """
        Runs :meth:`~.GinoConnection.status`. See also :meth:`.all`.

        """
        async with self.acquire(reuse=True) as conn:
            return await conn.status(clause, *multiparams, **params)

    def compile(self, clause, *multiparams, **params):
        """
        A shortcut for :meth:`~gino.dialects.base.AsyncDialectMixin.compile` on
        the dialect, returns raw SQL string and parameters according to the
        rules of the dialect.

        """
        return self._dialect.compile(clause, *multiparams, **params)

    def transaction(self, *args, timeout=None, reuse=True, reusable=True,
                    **kwargs):
        """
        Borrows a new connection and starts a transaction with it.

        Different to :meth:`.GinoConnection.transaction`, transaction on engine
        level supports only managed usage::

            async with engine.transaction() as tx:
                # play with transaction here

        Where the implicitly acquired connection is available as
        :attr:`tx.connection <gino.transaction.GinoTransaction.connection>`.

        By default, :meth:`.transaction` acquires connection with
        ``reuse=True`` and ``reusable=True``, that means it by default tries to
        create a nested transaction instead of a new transaction on a new
        connection. You can change the default behavior by setting these two
        arguments.

        The other arguments are the same as
        :meth:`~.GinoConnection.transaction` on connection.

        .. seealso::

            :meth:`.GinoEngine.acquire`

            :meth:`.GinoConnection.transaction`

            :class:`~gino.transaction.GinoTransaction`

        :return: A asynchronous context manager that yields a
          :class:`~gino.transaction.GinoTransaction`

        """
        return _TransactionContext(self.acquire(
            timeout=timeout, reuse=reuse, reusable=reusable), (args, kwargs))

    def iterate(self, clause, *multiparams, **params):
        """
        Creates a server-side cursor in database for large query results.

        This requires that there is a reusable connection in the current
        context, and an active transaction is present. Then its
        :meth:`.GinoConnection.iterate` is executed and returned.

        """
        connection = self.current_connection
        if connection is None:
            raise ValueError(
                'No Connection in context, please provide one')
        return connection.iterate(clause, *multiparams, **params)

    def update_execution_options(self, **opt):
        """Update the default execution_options dictionary
        of this :class:`.GinoEngine`.

        .. seealso::

            :meth:`sqlalchemy.engine.Engine.update_execution_options`

            :meth:`.GinoConnection.execution_options`

        """
        self._sa_engine.update_execution_options(**opt)

    async def _run_visitor(self, *args, **kwargs):
        async with self.acquire(reuse=True) as conn:
            await getattr(conn, '_run_visitor')(*args, **kwargs)
Exemple #4
0
def test_get():
    v = ContextVar('v')
    with pytest.raises(LookupError):
        v.get()
    assert v.get(456) == 456
    return v
Exemple #5
0
def test_get_with_default():
    v = ContextVar('v', default=123)
    assert v.get() == 123
    assert v.get(456) == 456
    return v