You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					129 lines
				
				3.6 KiB
			
		
		
			
		
	
	
					129 lines
				
				3.6 KiB
			| 
								 
											3 years ago
										 
									 | 
							
								# testing/asyncio.py
							 | 
						||
| 
								 | 
							
								# Copyright (C) 2005-2022 the SQLAlchemy authors and contributors
							 | 
						||
| 
								 | 
							
								# <see AUTHORS file>
							 | 
						||
| 
								 | 
							
								#
							 | 
						||
| 
								 | 
							
								# This module is part of SQLAlchemy and is released under
							 | 
						||
| 
								 | 
							
								# the MIT License: https://www.opensource.org/licenses/mit-license.php
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								# functions and wrappers to run tests, fixtures, provisioning and
							 | 
						||
| 
								 | 
							
								# setup/teardown in an asyncio event loop, conditionally based on the
							 | 
						||
| 
								 | 
							
								# current DB driver being used for a test.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								# note that SQLAlchemy's asyncio integration also supports a method
							 | 
						||
| 
								 | 
							
								# of running individual asyncio functions inside of separate event loops
							 | 
						||
| 
								 | 
							
								# using "async_fallback" mode; however running whole functions in the event
							 | 
						||
| 
								 | 
							
								# loop is a more accurate test for how SQLAlchemy's asyncio features
							 | 
						||
| 
								 | 
							
								# would run in the real world.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								from functools import wraps
							 | 
						||
| 
								 | 
							
								import inspect
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								from . import config
							 | 
						||
| 
								 | 
							
								from ..util.concurrency import _util_async_run
							 | 
						||
| 
								 | 
							
								from ..util.concurrency import _util_async_run_coroutine_function
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								# may be set to False if the
							 | 
						||
| 
								 | 
							
								# --disable-asyncio flag is passed to the test runner.
							 | 
						||
| 
								 | 
							
								ENABLE_ASYNCIO = True
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								def _run_coroutine_function(fn, *args, **kwargs):
							 | 
						||
| 
								 | 
							
								    return _util_async_run_coroutine_function(fn, *args, **kwargs)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								def _assume_async(fn, *args, **kwargs):
							 | 
						||
| 
								 | 
							
								    """Run a function in an asyncio loop unconditionally.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    This function is used for provisioning features like
							 | 
						||
| 
								 | 
							
								    testing a database connection for server info.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    Note that for blocking IO database drivers, this means they block the
							 | 
						||
| 
								 | 
							
								    event loop.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    """
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if not ENABLE_ASYNCIO:
							 | 
						||
| 
								 | 
							
								        return fn(*args, **kwargs)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    return _util_async_run(fn, *args, **kwargs)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								def _maybe_async_provisioning(fn, *args, **kwargs):
							 | 
						||
| 
								 | 
							
								    """Run a function in an asyncio loop if any current drivers might need it.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    This function is used for provisioning features that take
							 | 
						||
| 
								 | 
							
								    place outside of a specific database driver being selected, so if the
							 | 
						||
| 
								 | 
							
								    current driver that happens to be used for the provisioning operation
							 | 
						||
| 
								 | 
							
								    is an async driver, it will run in asyncio and not fail.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    Note that for blocking IO database drivers, this means they block the
							 | 
						||
| 
								 | 
							
								    event loop.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    """
							 | 
						||
| 
								 | 
							
								    if not ENABLE_ASYNCIO:
							 | 
						||
| 
								 | 
							
								        return fn(*args, **kwargs)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if config.any_async:
							 | 
						||
| 
								 | 
							
								        return _util_async_run(fn, *args, **kwargs)
							 | 
						||
| 
								 | 
							
								    else:
							 | 
						||
| 
								 | 
							
								        return fn(*args, **kwargs)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								def _maybe_async(fn, *args, **kwargs):
							 | 
						||
| 
								 | 
							
								    """Run a function in an asyncio loop if the current selected driver is
							 | 
						||
| 
								 | 
							
								    async.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    This function is used for test setup/teardown and tests themselves
							 | 
						||
| 
								 | 
							
								    where the current DB driver is known.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    """
							 | 
						||
| 
								 | 
							
								    if not ENABLE_ASYNCIO:
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        return fn(*args, **kwargs)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    is_async = config._current.is_async
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if is_async:
							 | 
						||
| 
								 | 
							
								        return _util_async_run(fn, *args, **kwargs)
							 | 
						||
| 
								 | 
							
								    else:
							 | 
						||
| 
								 | 
							
								        return fn(*args, **kwargs)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								def _maybe_async_wrapper(fn):
							 | 
						||
| 
								 | 
							
								    """Apply the _maybe_async function to an existing function and return
							 | 
						||
| 
								 | 
							
								    as a wrapped callable, supporting generator functions as well.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    This is currently used for pytest fixtures that support generator use.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    """
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if inspect.isgeneratorfunction(fn):
							 | 
						||
| 
								 | 
							
								        _stop = object()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        def call_next(gen):
							 | 
						||
| 
								 | 
							
								            try:
							 | 
						||
| 
								 | 
							
								                return next(gen)
							 | 
						||
| 
								 | 
							
								                # can't raise StopIteration in an awaitable.
							 | 
						||
| 
								 | 
							
								            except StopIteration:
							 | 
						||
| 
								 | 
							
								                return _stop
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        @wraps(fn)
							 | 
						||
| 
								 | 
							
								        def wrap_fixture(*args, **kwargs):
							 | 
						||
| 
								 | 
							
								            gen = fn(*args, **kwargs)
							 | 
						||
| 
								 | 
							
								            while True:
							 | 
						||
| 
								 | 
							
								                value = _maybe_async(call_next, gen)
							 | 
						||
| 
								 | 
							
								                if value is _stop:
							 | 
						||
| 
								 | 
							
								                    break
							 | 
						||
| 
								 | 
							
								                yield value
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    else:
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        @wraps(fn)
							 | 
						||
| 
								 | 
							
								        def wrap_fixture(*args, **kwargs):
							 | 
						||
| 
								 | 
							
								            return _maybe_async(fn, *args, **kwargs)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    return wrap_fixture
							 |