Ошибка: «Невозможно выполнить операцию: другая операция проходит» только при запуске от Pytest/TestClientPython

Программы на Python
Ответить Пред. темаСлед. тема
Anonymous
 Ошибка: «Невозможно выполнить операцию: другая операция проходит» только при запуске от Pytest/TestClient

Сообщение Anonymous »

Это происходит при попытке запустить модульные тесты на простом приложении FASTAPI, используя Asyncpg. Минимальный воспроизводитель: < /p>

Код: Выделить всё

import asyncpg

class Database:
def __init__(self, dsn):
self.dsn = dsn
self.pool = None

# creates connection pool
async def connect(self):
if not self.pool:
self.pool = await asyncpg.create_pool(
min_size=1,
max_size=200,
max_inactive_connection_lifetime=10
command_timeout=60,
dsn=self.dsn
)

async def fetch(self, query: str, *args):
if not self.pool:
await self.connect()

async with self.pool.acquire() as conn:
prep_stmt = await conn.prepare(query)
result = await prep_stmt.fetch(*args)

return result

async def close(self):
if self.pool:
await self.pool.close()

< /code>
Вот приложение: < /p>
from fastapi import FastAPI, HTTPException, status

from app.database import Database

app = FastAPI(title="simple app")
db = Database("postgresql://postgres:postgres@10.201.0.110/postgres")

@app.on_event("startup")
async def startup_event():
await db.connect()

@app.on_event("shutdown")
async def shutdown_event():
await db.close()

@app.get("/test")
async def get_test():

try:
result = await db.fetch("SELECT 1 as foo")
except Exception as e:
raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"Got exception: {e}")

return result
< /code>
Теперь это работает отлично при тестировании из командной строки, например: < /p>
$ curl http://127.0.0.1:8000/test && echo && curl http://127.0.0.1:8000/test && echo
[{"foo":1}]
[{"foo":1}]

< /code>
Но второй запрос не выполняется, когда вызов сделан с использованием TestClient Inside Pytest: < /p>
from fastapi.testclient import TestClient

from app.app import app

client = TestClient(app)

def test_get():

response = client.get("/test")
print(response.content)
assert response.status_code == 200
response = client.get("/test")
print(response.content)
assert response.status_code == 200
< /code>
Вот выход: < /p>
$ pytest -v -s tests/
======================================================= test session starts =======================================================
platform linux -- Python 3.10.11, pytest-7.3.1, pluggy-1.0.0 -- /usr/local/bin/python
cachedir: .pytest_cache
rootdir: /tmp/myapp
plugins: anyio-3.7.0
collected 1 item

tests/test_api.py::test_get b'[{"foo":1}]'
b'{"detail":"Got exception: cannot perform operation: another operation is in progress"}'
FAILED

============================================================ FAILURES =============================================================
____________________________________________________________ test_get _____________________________________________________________

def test_get():

response = client.get("/test")
print(response.content)
assert response.status_code == 200
response = client.get("/test")
print(response.content)
>       assert response.status_code == 200
E       assert 500 == 200
E        +  where 500 = .status_code

tests/test_api.py:15: AssertionError
===================================================== short test summary info =====================================================
FAILED tests/test_api.py::test_get - assert 500 == 200
======================================================== 1 failed in 0.28s ========================================================
Future exception was never retrieved
future: 
asyncpg.exceptions.ConnectionDoesNotExistError: connection was closed in the middle of operation
Я не могу выяснить, почему, только с Pytest/testClient, я получаю, которая не может выполнить операцию: другая операция осуществляется ответ для второго запроса.
Редактировать: Оказывается, это не связано с Pytest, только с использованием TestClient . Следующего кода уже достаточно, чтобы запустить проблему: < /p>

Код: Выделить всё

from fastapi.testclient import TestClient

from app.app import app

client = TestClient(app)

response = client.get("/test")
print(response.status_code, response.content)
response = client.get("/test")
print(response.status_code, response.content)
< /code>
Редактировать 2: Чтобы увидеть полный трассерббэк, я только что повторно заработал исключение в приложении, и я получаю это для второго - сбой - запрос: < /p>
Traceback (most recent call last):
File "/tmp/myapp/app/database.py", line 27, in fetch
prep_stmt = await conn.prepare(query)
File "/usr/local/lib/python3.10/site-packages/asyncpg/connection.py", line 565, in prepare
return await self._prepare(
File "/usr/local/lib/python3.10/site-packages/asyncpg/connection.py", line 583, in _prepare
stmt = await self._get_statement(
File "/usr/local/lib/python3.10/site-packages/asyncpg/connection.py", line 397, in _get_statement
statement = await self._protocol.prepare(
File "asyncpg/protocol/protocol.pyx", line 156, in prepare
File "asyncpg/protocol/protocol.pyx", line 741, in asyncpg.protocol.protocol.BaseProtocol._new_waiter
File "/usr/local/lib/python3.10/asyncio/base_events.py", line 721, in call_later
timer = self.call_at(self.time() + delay, callback, *args,
File "/usr/local/lib/python3.10/asyncio/base_events.py", line 732, in call_at
self._check_closed()
File "/usr/local/lib/python3.10/asyncio/base_events.py", line 515, in _check_closed
raise RuntimeError('Event loop is closed')
RuntimeError:  Event loop is closed

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
File "/tmp/myapp/simpletest.py", line 9, in 
response = client.get("/test")
File "/usr/local/lib/python3.10/site-packages/starlette/testclient.py", line 499, in get
return super().get(
File "/usr/local/lib/python3.10/site-packages/httpx/_client.py", line 1041, in get
return self.request(
File "/usr/local/lib/python3.10/site-packages/starlette/testclient.py", line 465, in request
return super().request(
File "/usr/local/lib/python3.10/site-packages/httpx/_client.py", line 814, in request
return self.send(request, auth=auth, follow_redirects=follow_redirects)
File "/usr/local/lib/python3.10/site-packages/httpx/_client.py", line 901, in send
response = self._send_handling_auth(
File "/usr/local/lib/python3.10/site-packages/httpx/_client.py", line 929, in _send_handling_auth
response = self._send_handling_redirects(
File "/usr/local/lib/python3.10/site-packages/httpx/_client.py", line 966, in _send_handling_redirects
response = self._send_single_request(request)
File "/usr/local/lib/python3.10/site-packages/httpx/_client.py", line 1002, in _send_single_request
response = transport.handle_request(request)
File "/usr/local/lib/python3.10/site-packages/starlette/testclient.py", line 342, in handle_request
raise exc
File "/usr/local/lib/python3.10/site-packages/starlette/testclient.py", line 339, in handle_request
portal.call(self.app, scope, receive, send)
File "/usr/local/lib/python3.10/site-packages/anyio/from_thread.py", line 277, in call
return cast(T_Retval, self.start_task_soon(func, *args).result())
File "/usr/local/lib/python3.10/concurrent/futures/_base.py", line 458, in result
return self.__get_result()
File "/usr/local/lib/python3.10/concurrent/futures/_base.py", line 403, in __get_result
raise self._exception
File "/usr/local/lib/python3.10/site-packages/anyio/from_thread.py", line 217, in _call_func
retval = await retval
File "/usr/local/lib/python3.10/site-packages/fastapi/applications.py", line 276, in __call__
await super().__call__(scope, receive, send)
File "/usr/local/lib/python3.10/site-packages/starlette/applications.py", line 122, in __call__
await self.middleware_stack(scope, receive, send)
File "/usr/local/lib/python3.10/site-packages/starlette/middleware/errors.py", line 184, in __call__
raise exc
File "/usr/local/lib/python3.10/site-packages/starlette/middleware/errors.py", line 162, in __call__
await self.app(scope, receive, _send)
File "/usr/local/lib/python3.10/site-packages/starlette/middleware/exceptions.py", line 79, in __call__
raise exc
File "/usr/local/lib/python3.10/site-packages/starlette/middleware/exceptions.py", line 68, in __call__
await self.app(scope, receive, sender)
File "/usr/local/lib/python3.10/site-packages/fastapi/middleware/asyncexitstack.py", line 21, in __call__
raise e
File "/usr/local/lib/python3.10/site-packages/fastapi/middleware/asyncexitstack.py", line 18, in __call__
await self.app(scope, receive, send)
File "/usr/local/lib/python3.10/site-packages/starlette/routing.py", line 718, in __call__
await route.handle(scope, receive, send)
File "/usr/local/lib/python3.10/site-packages/starlette/routing.py", line 276, in handle
await self.app(scope, receive, send)
File "/usr/local/lib/python3.10/site-packages/starlette/routing.py", line 66, in app
response = await func(request)
File "/usr/local/lib/python3.10/site-packages/fastapi/routing.py", line 237, in app
raw_response = await run_endpoint_function(
File "/usr/local/lib/python3.10/site-packages/fastapi/routing.py", line 163, in run_endpoint_function
return await dependant.call(**values)
File "/tmp/myapp/app/app.py", line 27, in get_test
result = await db.fetch("SELECT 1 as foo")
File "/tmp/myapp/app/database.py", line 26, in fetch
async with self.pool.acquire() as conn:
File "/usr/local/lib/python3.10/site-packages/asyncpg/pool.py", line 220, in release
raise ex
File "/usr/local/lib/python3.10/site-packages/asyncpg/pool.py", line 210,  in release
await self._con.reset(timeout=budget)
File "/usr/local/lib/python3.10/site-packages/asyncpg/connection.py", line 1366, in reset
await self.execute(reset_query, timeout=timeout)
File "/usr/local/lib/python3.10/site-packages/asyncpg/connection.py", line 317, in execute
return await self._protocol.query(query, timeout)
File "asyncpg/protocol/protocol.pyx", line 323, in query
File "asyncpg/protocol/protocol.pyx", line 707, in asyncpg.protocol.protocol.BaseProtocol._check_state
asyncpg.exceptions._base.InterfaceError: cannot perform operation: another operation is in progress
Таким образом, похоже, что реальная проблема заключается в том, что цикл события закрыт ошибка.
Изменить 3: реструктуризация кода тестирования следующим образом работает нормально:

Код: Выделить всё

with TestClient(app) as client:
response = client.get("/test")
print(response.status_code, response.content)

response = client.get("/test")
print(response.status_code, response.content)
Все еще не уверен, где находится исходная проблема, но, по крайней мере, теперь у меня есть обходной путь. Кажется, это единственная разница. Мне все еще неясно, почему он не работает в режиме «нормальный» (т.е. не контекст-менеджер), так как функциональная база данных. Очевидно, это не так, иначе я бы не стал увидеть ошибку.

Подробнее здесь: https://stackoverflow.com/questions/763 ... ly-when-ru
Реклама
Ответить Пред. темаСлед. тема

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

  • Похожие темы
    Ответы
    Просмотры
    Последнее сообщение

Вернуться в «Python»