dd-trace-py
dd-trace-py copied to clipboard
Python 3.12 / ddtrace-run combined with psycopg3 AsyncConnectionPool leads to `TypeError: 'async for' requires an object with __aiter__ method, got Psycopg3TracedAsyncCursor` when consuming iterator
Summary of problem
When running a fastapi service through uvicorn with ddtrace-run and psycopg3, any async iterator database interface will throw an exception:
TypeError: 'async for' requires an object with __aiter__ method, got Psycopg3TracedAsyncCursor
I have stripped all the fastapi / uvicorn and other related dependencies out for a reproducible example with the same results. I am currently unable to trace the service that needs to run this code, and combined with the known ddtrace profiler issues on python 3.12 (#9205) this is causing a major headache for the team.
Which version of dd-trace-py are you using?
I have tried most versions between 2.1.0 and latest. While 2.1.0 doesn't throw the exception, the iterator is always empty.
Which version of pip are you using?
24.0
Which libraries and their versions are you using?
`pip freeze`
aioitertools==0.11.0 annotated-types==0.7.0 anyio==4.4.0 asyncache==0.3.1 attrs==23.2.0 black==24.4.2 bytecode==0.15.1 cachetools==5.3.3 cattrs==23.2.3 certifi==2024.6.2 cffi==1.16.0 cfgv==3.4.0 click==8.1.7 cryptography==42.0.8 ddsketch==3.0.1 ddtrace==2.9.0 Deprecated==1.2.14 distlib==0.3.8 envier==0.5.1 fastapi==0.110.1 filelock==3.14.0 h11==0.14.0 httpcore==1.0.5 httpx==0.27.0 identify==2.5.36 idna==3.7 importlib_metadata==7.1.0 iniconfig==2.0.0 mypy==1.10.0 mypy-extensions==1.0.0 nodeenv==1.9.1 opentelemetry-api==1.25.0 packaging==24.1 pathspec==0.12.1 platformdirs==4.2.2 pluggy==1.5.0 pre-commit==2.21.0 protobuf==5.27.1 psutil==5.9.8 psycopg==3.1.19 psycopg-binary==3.1.19 psycopg-pool==3.2.2 psycopg2-binary==2.9.9 pycparser==2.22 pydantic==2.7.3 pydantic_core==2.18.4 PyJWT==2.8.0 pytest==7.2.0 pytest-asyncio==0.20.3 python-dateutil==2.9.0.post0 python-json-logger==2.0.4 PyYAML==6.0.1 setuptools==70.0.0 six==1.16.0 sniffio==1.3.1 sqlparse==0.5.0 sse-starlette==2.1.0 starlette==0.37.2 typing_extensions==4.12.2 uvicorn==0.29.0 virtualenv==20.26.2 wrapt==1.16.0 xmltodict==0.13.0 zipp==3.19.2How can we reproduce your problem?
import asyncio
import logging
import os
from psycopg import sql
from psycopg_pool import AsyncConnectionPool
log = logging.getLogger(__name__)
def get_conn_str():
return f"""
dbname={os.environ.get("POSTGRES_DB")}
user={os.environ.get("POSTGRES_USER")}
password={os.environ.get("POSTGRES_PASSWORD")}
host={os.environ.get("POSTGRES_HOST")}
port={os.environ.get("POSTGRES_PORT")}
"""
async def testfunc():
connectstr = get_conn_str()
async with AsyncConnectionPool(
conninfo=connectstr, min_size=4, max_size=30
) as async_pool:
async with async_pool.connection() as conn:
async with conn.cursor() as cur:
await cur.execute(
sql.SQL(
"""
SELECT
*
FROM table1
LIMIT 5
"""
)
)
async for row in cur:
print(row)
if __name__ == "__main__":
asyncio.get_event_loop().run_until_complete(testfunc())
ddtrace-run python connection_test.py
What is the result that you get?
Traceback (most recent call last):
File "/Users/xxxx/connection_test.py", line 68, in <module>
asyncio.get_event_loop().run_until_complete(testfunc())
File "/Users/xxxx/.pyenv/versions/3.12.1/lib/python3.12/asyncio/base_events.py", line 684, in run_until_complete
return future.result()
^^^^^^^^^^^^^^^
File "/Users/xxxx/.venv/lib/python3.12/site-packages/ddtrace/contrib/asyncio/patch.py", line 50, in traced_coro
return await coro
^^^^^^^^^^
File "/Users/xxxx/connection_test.py", line 63, in testfunc
async for row in cur:
TypeError: 'async for' requires an object with __aiter__ method, got Psycopg3TracedAsyncCursor
What is the result that you expected?
I would expect the iterator to return rows. The example code works fine when running python connection_test.py