RuntimeError: Event loop is closed Pytest, FastApi, SqlAlchemy, Docker
Без использования pytest все прекрасно работает, да и сам pytest работает, но только один тест, если их больше одного, то вылетает ошибка RuntimeError: Event loop is closed, а именно: AttributeError: 'NoneType' object has no attribute 'send' Вот пример теста:
import pytest
from fastapi.testclient import TestClient
from main import app
client = TestClient(app)
@pytest.mark.parametrize('a, b, code', (
('[email protected]', 'qwerty', 200),
('[email protected]', 'qwerty', 409),
))
def test1(a, b, code):
response = client.post(f"/users/create?&email={a}&password={b}")
assert response.status_code == code
Также вот пример main:
from app.db.importer import *
from app.db.db import *
from fastapi import FastAPI
from app.routes.user import user_router
from app.routes.transaction import transaction_router
from app.routes.auth import auth_router
from app.routes.budget import budget_router
from app.routes.budget_items import budget_items_router
import uvicorn
from base.exception import *
app = FastAPI()
app.include_router(transaction_router)
app.include_router(user_router)
app.include_router(auth_router)
app.include_router(budget_router)
app.include_router(budget_items_router)
app.add_exception_handler(DefaultException, default_exception_handler)
app.add_exception_handler(HTTPException, http_exception_handler)
Ну и собственно полный лог ошибки:
================================================================================ FAILURES =================================================================================
___________________________________________________________________ test1[[email protected]] ____________________________________________________________________
a = '[email protected]', b = 'qwerty', code = 200
@pytest.mark.parametrize('a, b, code', (
('[email protected]', 'qwerty', 200),
))
def test1(a, b, code):
response = client.post(f"/users/create?&email={a}&password={b}")
> assert response.status_code == code
E assert 409 == 200
E + where 409 = <Response [409 Conflict]>.status_code
tests\test_app.py:12: AssertionError
___________________________________________________________________ test2[[email protected]] ____________________________________________________________________
self = <ProactorEventLoop running=False closed=True debug=False>
callback = <bound method BaseProtocol._on_waiter_completed of <asyncpg.protocol.protocol.Protocol object at 0x000001761818B740>>
context = <_contextvars.Context object at 0x000001761906A6C0>, args = (<Future finished exception=AttributeError("'NoneType' object has no attribute 'send'")>,)
def call_soon(self, callback, *args, context=None):
"""Arrange for a callback to be called as soon as possible.
This operates as a FIFO queue: callbacks are called in the
order in which they are registered. Each callback will be
called exactly once.
Any positional arguments after the callback will be passed to
the callback when it is called.
"""
> self._check_closed()
..\..\AppData\Local\Programs\Python\Python310\lib\asyncio\base_events.py:753:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
self = <ProactorEventLoop running=False closed=True debug=False>
def _check_closed(self):
if self._closed:
> raise RuntimeError('Event loop is closed')
E RuntimeError: Event loop is closed
..\..\AppData\Local\Programs\Python\Python310\lib\asyncio\base_events.py:515: RuntimeError
During handling of the above exception, another exception occurred:
a = '[email protected]', b = 'qwerty', code = 409
@pytest.mark.parametrize('a, b, code', (
('[email protected]', 'qwerty', 409),
))
def test2(a, b, code):
> response = client.post(f"/users/create?&email={a}&password={b}")
tests\test_app.py:18:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
..\..\AppData\Local\Programs\Python\Python310\lib\site-packages\starlette\testclient.py:531: in post
return super().post(
..\..\AppData\Local\Programs\Python\Python310\lib\site-packages\httpx\_client.py:1144: in post
return self.request(
..\..\AppData\Local\Programs\Python\Python310\lib\site-packages\starlette\testclient.py:430: in request
return super().request(
..\..\AppData\Local\Programs\Python\Python310\lib\site-packages\httpx\_client.py:825: in request
return self.send(request, auth=auth, follow_redirects=follow_redirects)
..\..\AppData\Local\Programs\Python\Python310\lib\site-packages\httpx\_client.py:914: in send
response = self._send_handling_auth(
..\..\AppData\Local\Programs\Python\Python310\lib\site-packages\httpx\_client.py:942: in _send_handling_auth
response = self._send_handling_redirects(
..\..\AppData\Local\Programs\Python\Python310\lib\site-packages\httpx\_client.py:979: in _send_handling_redirects
response = self._send_single_request(request)
..\..\AppData\Local\Programs\Python\Python310\lib\site-packages\httpx\_client.py:1014: in _send_single_request
response = transport.handle_request(request)
..\..\AppData\Local\Programs\Python\Python310\lib\site-packages\starlette\testclient.py:339: in handle_request
raise exc
..\..\AppData\Local\Programs\Python\Python310\lib\site-packages\starlette\testclient.py:336: in handle_request
portal.call(self.app, scope, receive, send)
..\..\AppData\Local\Programs\Python\Python310\lib\site-packages\anyio\from_thread.py:277: in call
return cast(T_Retval, self.start_task_soon(func, *args).result())
..\..\AppData\Local\Programs\Python\Python310\lib\concurrent\futures\_base.py:458: in result
return self.__get_result()
..\..\AppData\Local\Programs\Python\Python310\lib\concurrent\futures\_base.py:403: in __get_result
raise self._exception
..\..\AppData\Local\Programs\Python\Python310\lib\site-packages\anyio\from_thread.py:217: in _call_func
retval = await retval
..\..\AppData\Local\Programs\Python\Python310\lib\site-packages\fastapi\applications.py:1054: in __call__
await super().__call__(scope, receive, send)
..\..\AppData\Local\Programs\Python\Python310\lib\site-packages\starlette\applications.py:112: in __call__
await self.middleware_stack(scope, receive, send)
..\..\AppData\Local\Programs\Python\Python310\lib\site-packages\starlette\middleware\errors.py:187: in __call__
raise exc
..\..\AppData\Local\Programs\Python\Python310\lib\site-packages\starlette\middleware\errors.py:165: in __call__
await self.app(scope, receive, _send)
..\..\AppData\Local\Programs\Python\Python310\lib\site-packages\starlette\middleware\exceptions.py:62: in __call__
await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send)
..\..\AppData\Local\Programs\Python\Python310\lib\site-packages\starlette\_exception_handler.py:53: in wrapped_app
raise exc
..\..\AppData\Local\Programs\Python\Python310\lib\site-packages\starlette\_exception_handler.py:42: in wrapped_app
await app(scope, receive, sender)
..\..\AppData\Local\Programs\Python\Python310\lib\site-packages\starlette\routing.py:715: in __call__
await self.middleware_stack(scope, receive, send)
..\..\AppData\Local\Programs\Python\Python310\lib\site-packages\starlette\routing.py:735: in app
await route.handle(scope, receive, send)
..\..\AppData\Local\Programs\Python\Python310\lib\site-packages\starlette\routing.py:288: in handle
await self.app(scope, receive, send)
..\..\AppData\Local\Programs\Python\Python310\lib\site-packages\starlette\routing.py:76: in app
await wrap_app_handling_exceptions(app, request)(scope, receive, send)
..\..\AppData\Local\Programs\Python\Python310\lib\site-packages\starlette\_exception_handler.py:53: in wrapped_app
raise exc
..\..\AppData\Local\Programs\Python\Python310\lib\site-packages\starlette\_exception_handler.py:42: in wrapped_app
await app(scope, receive, sender)
..\..\AppData\Local\Programs\Python\Python310\lib\site-packages\starlette\routing.py:73: in app
response = await f(request)
..\..\AppData\Local\Programs\Python\Python310\lib\site-packages\fastapi\routing.py:301: in app
raw_response = await run_endpoint_function(
..\..\AppData\Local\Programs\Python\Python310\lib\site-packages\fastapi\routing.py:212: in run_endpoint_function
return await dependant.call(**values)
app\routes\user.py:88: in create_user
user : UserDTO = await user_r.filter(UserFilterDTO(email=data.email))
app\repositories\user.py:68: in filter
user = await self.conn.execute(query)
..\..\AppData\Local\Programs\Python\Python310\lib\site-packages\sqlalchemy\ext\asyncio\session.py:463: in execute
result = await greenlet_spawn(
..\..\AppData\Local\Programs\Python\Python310\lib\site-packages\sqlalchemy\util\_concurrency_py3k.py:201: in greenlet_spawn
result = context.throw(*sys.exc_info())
..\..\AppData\Local\Programs\Python\Python310\lib\site-packages\sqlalchemy\orm\session.py:2365: in execute
return self._execute_internal(
..\..\AppData\Local\Programs\Python\Python310\lib\site-packages\sqlalchemy\orm\session.py:2251: in _execute_internal
result: Result[Any] = compile_state_cls.orm_execute_statement(
..\..\AppData\Local\Programs\Python\Python310\lib\site-packages\sqlalchemy\orm\context.py:305: in orm_execute_statement
result = conn.execute(
..\..\AppData\Local\Programs\Python\Python310\lib\site-packages\sqlalchemy\engine\base.py:1416: in execute
return meth(
..\..\AppData\Local\Programs\Python\Python310\lib\site-packages\sqlalchemy\sql\elements.py:515: in _execute_on_connection
return connection._execute_clauseelement(
..\..\AppData\Local\Programs\Python\Python310\lib\site-packages\sqlalchemy\engine\base.py:1638: in _execute_clauseelement
ret = self._execute_context(
..\..\AppData\Local\Programs\Python\Python310\lib\site-packages\sqlalchemy\engine\base.py:1843: in _execute_context
return self._exec_single_context(
..\..\AppData\Local\Programs\Python\Python310\lib\site-packages\sqlalchemy\engine\base.py:1983: in _exec_single_context
self._handle_dbapi_exception(
..\..\AppData\Local\Programs\Python\Python310\lib\site-packages\sqlalchemy\engine\base.py:2355: in _handle_dbapi_exception
raise exc_info[1].with_traceback(exc_info[2])
..\..\AppData\Local\Programs\Python\Python310\lib\site-packages\sqlalchemy\engine\base.py:1964: in _exec_single_context
self.dialect.do_execute(
..\..\AppData\Local\Programs\Python\Python310\lib\site-packages\sqlalchemy\engine\default.py:942: in do_execute
cursor.execute(statement, parameters)
..\..\AppData\Local\Programs\Python\Python310\lib\site-packages\sqlalchemy\dialects\postgresql\asyncpg.py:580: in execute
self._adapt_connection.await_(
..\..\AppData\Local\Programs\Python\Python310\lib\site-packages\sqlalchemy\util\_concurrency_py3k.py:132: in await_only
return current.parent.switch(awaitable) # type: ignore[no-any-return,attr-defined] # noqa: E501
..\..\AppData\Local\Programs\Python\Python310\lib\site-packages\sqlalchemy\util\_concurrency_py3k.py:196: in greenlet_spawn
value = await result
..\..\AppData\Local\Programs\Python\Python310\lib\site-packages\sqlalchemy\dialects\postgresql\asyncpg.py:515: in _prepare_and_execute
await adapt_connection._start_transaction()
..\..\AppData\Local\Programs\Python\Python310\lib\site-packages\sqlalchemy\dialects\postgresql\asyncpg.py:845: in _start_transaction
self._handle_exception(error)
..\..\AppData\Local\Programs\Python\Python310\lib\site-packages\sqlalchemy\dialects\postgresql\asyncpg.py:794: in _handle_exception
raise error
..\..\AppData\Local\Programs\Python\Python310\lib\site-packages\sqlalchemy\dialects\postgresql\asyncpg.py:843: in _start_transaction
await self._transaction.start()
..\..\AppData\Local\Programs\Python\Python310\lib\site-packages\asyncpg\transaction.py:146: in start
await self._connection.execute(query)
..\..\AppData\Local\Programs\Python\Python310\lib\site-packages\asyncpg\connection.py:349: in execute
result = await self._protocol.query(query, timeout)
asyncpg\\protocol\\protocol.pyx:375: in query
???
asyncpg\\protocol\\protocol.pyx:368: in asyncpg.protocol.protocol.BaseProtocol.query
???
asyncpg\\protocol\\coreproto.pyx:1174: in asyncpg.protocol.protocol.CoreProtocol._simple_query
???
asyncpg\\protocol\\protocol.pyx:967: in asyncpg.protocol.protocol.BaseProtocol._write
???
..\..\AppData\Local\Programs\Python\Python310\lib\asyncio\proactor_events.py:365: in write
self._loop_writing(data=bytes(data))
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
self = <_ProactorSocketTransport fd=676 read=<_OverlappedFuture cancelled>>, f = None, data = b'Q\x00\x00\x00*BEGIN ISOLATION LEVEL READ COMMITTED;\x00'
def _loop_writing(self, f=None, data=None):
try:
if f is not None and self._write_fut is None and self._closing:
# XXX most likely self._force_close() has been called, and
# it has set self._write_fut to None.
return
assert f is self._write_fut
self._write_fut = None
self._pending_write = 0
if f:
f.result()
if data is None:
data = self._buffer
self._buffer = None
if not data:
if self._closing:
self._loop.call_soon(self._call_connection_lost, None)
if self._eof_written:
self._sock.shutdown(socket.SHUT_WR)
# Now that we've reduced the buffer size, tell the
# protocol to resume writing if it was paused. Note that
# we do this last since the callback is called immediately
# and it may add more data to the buffer (even causing the
# protocol to be paused again).
self._maybe_resume_protocol()
else:
> self._write_fut = self._loop._proactor.send(self._sock, data)
E AttributeError: 'NoneType' object has no attribute 'send'
..\..\AppData\Local\Programs\Python\Python310\lib\asyncio\proactor_events.py:401: AttributeError
============================================================================ warnings summary =============================================================================
tests/test_app.py::test1[[email protected]]
tests/test_app.py::test2[[email protected]]
C:\Users\XCompleX\Desktop\fintrack\base\shemas.py:20: PydanticDeprecatedSince20: The `dict` method is deprecated; use `model_dump` instead. Deprecated in Pydantic V2.0 to be removed in V3.0. See Pydantic V2 Migration Guide at https://errors.pydantic.dev/2.10/migration/
for attr, value in self.dict(exclude_unset=True).items():
-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
========================================================================= short test summary info =========================================================================
FAILED tests/test_app.py::test1[[email protected]] - assert 409 == 200
FAILED tests/test_app.py::test2[[email protected]] - AttributeError: 'NoneType' object has no attribute 'send'
====================================================================== 2 failed, 2 warnings in 6.21s ======================================================================
на 409 можно не обращать внимание, так пока и должно быть, заметил еще что если первый сработает на 422 ошибку, то 2 тест проходит нормально.
Ответы (1 шт):
Автор решения: Максим Брагин
→ Ссылка
Не понимаю каким образом, я пробовал этот вариант, но в итоге он же и заработал, просто добавь фикстуру
import pytest
from fastapi.testclient import TestClient
from app.schemas.user import UserCreateDTO
from main import app
@pytest.fixture(scope="module")
def client():
with TestClient(app) as c:
yield c
@pytest.mark.parametrize('a, b, code', (
('[email protected]', 'qwerty', 201),
('[email protected]', 'qwerty', 409),
))
def test1(client, a, b, code):
response = client.post(f"/users/create?&email={a}&password={b}")
assert response.status_code == code