Skip to content

Commit 3ece058

Browse files
lrafeeixrmx
andauthored
Add __iter__ method to TracedCursorProxy (#4427)
* Add iter to TracedCursorProxy * Add test to verify cursor is iterable * Disable linter rule for dbapi iter test * Add __iter__ to MockCursor * Add mysql docker tests * Push linter fixes * Update docs-requirements.txt * Update CHANGELOG.md * Update nitpick-exceptions.ini --------- Co-authored-by: Riccardo Magliocchetti <riccardo.magliocchetti@gmail.com>
1 parent 6c8e64e commit 3ece058

File tree

7 files changed

+49
-3
lines changed

7 files changed

+49
-3
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2020

2121
- `opentelemetry-instrumentation-pika` Use `ObjectProxy` instead of `BaseObjectProxy` for `ReadyMessagesDequeProxy` to restore iterability with wrapt 2.x
2222
([#4461](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4461))
23+
- `opentelemetry-instrumentation-dbapi` Use `ObjectProxy` instead of `BaseObjectProxy` for `TracedCursorProxy` to restore iterability with wrapt 2.x
24+
([#4427](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4427))
2325

2426
### Breaking changes
2527

docs-requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ sqlalchemy>=1.0
4949
starlette~=0.50
5050
tornado>=5.1.1
5151
tortoise-orm>=0.17.0
52+
wrapt~=2.1
5253

5354
# required by opamp
5455
uuid_utils

docs/nitpick-exceptions.ini

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ py-class=
4444
psycopg.Connection
4545
psycopg.AsyncConnection
4646
ObjectProxy
47+
wrapt.proxies.ObjectProxy
4748
fastapi.applications.FastAPI
4849
starlette.applications.Starlette
4950
_contextvars.Token

instrumentation/opentelemetry-instrumentation-dbapi/src/opentelemetry/instrumentation/dbapi/__init__.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -177,8 +177,12 @@
177177

178178
try:
179179
# wrapt 2.0.0+
180-
from wrapt import BaseObjectProxy # pylint: disable=no-name-in-module
180+
from wrapt import ( # pylint: disable=no-name-in-module
181+
BaseObjectProxy,
182+
ObjectProxy,
183+
)
181184
except ImportError:
185+
from wrapt import ObjectProxy
182186
from wrapt import ObjectProxy as BaseObjectProxy
183187

184188
from opentelemetry import trace as trace_api
@@ -805,14 +809,14 @@ async def traced_execution_async(
805809

806810

807811
# pylint: disable=abstract-method,no-member
808-
class TracedCursorProxy(BaseObjectProxy, Generic[CursorT]):
812+
class TracedCursorProxy(ObjectProxy, Generic[CursorT]):
809813
# pylint: disable=unused-argument
810814
def __init__(
811815
self,
812816
cursor: CursorT,
813817
db_api_integration: DatabaseApiIntegration,
814818
):
815-
BaseObjectProxy.__init__(self, cursor)
819+
ObjectProxy.__init__(self, cursor)
816820
self._self_cursor_tracer = CursorTracer[CursorT](db_api_integration)
817821

818822
def execute(self, *args: Any, **kwargs: Any):

instrumentation/opentelemetry-instrumentation-dbapi/tests/test_dbapi_integration.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,20 @@ def test_executemany(self):
306306
span = spans_list[0]
307307
self.assertEqual(span.attributes[DB_STATEMENT], "Test query")
308308

309+
# pylint: disable=no-self-use
310+
def test_executemany_iterable_cursor(self):
311+
db_integration = dbapi.DatabaseApiIntegration(
312+
"instrumenting_module_test_name", "testcomponent"
313+
)
314+
mock_connection = db_integration.wrapped_connection(
315+
mock_connect, {}, {}
316+
)
317+
cursor = mock_connection.cursor()
318+
cursor.executemany("Test query")
319+
320+
for _row in cursor:
321+
pass
322+
309323
def test_executemany_comment(self):
310324
connect_module = mock.MagicMock()
311325
connect_module.__name__ = "test"
@@ -1296,13 +1310,17 @@ def __init__(self) -> None:
12961310
self._cnx._cmysql.get_client_info = mock.MagicMock(
12971311
return_value="1.2.3"
12981312
)
1313+
self._items = []
12991314

13001315
# pylint: disable=unused-argument, no-self-use
13011316
def execute(self, query, params=None, throw_exception=False):
13021317
if throw_exception:
13031318
# pylint: disable=broad-exception-raised
13041319
raise Exception("Test Exception")
13051320

1321+
def __iter__(self):
1322+
yield from self._items
1323+
13061324
# pylint: disable=unused-argument, no-self-use
13071325
def executemany(self, query, params=None, throw_exception=False):
13081326
if throw_exception:

tests/opentelemetry-docker-tests/tests/mysql/test_mysql_functional.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,16 @@ def test_executemany(self):
111111
self._cursor.executemany(stmt, data)
112112
self.validate_spans("INSERT")
113113

114+
def test_executemany_with_cursor_iteration(self):
115+
"""Should create a child span for executemany while iterating over the cursor"""
116+
stmt = "SELECT * FROM test"
117+
with self._tracer.start_as_current_span("rootSpan"):
118+
with self._connection.cursor() as cursor:
119+
cursor.execute(stmt)
120+
for _row in cursor:
121+
pass
122+
self.validate_spans("SELECT")
123+
114124
def test_callproc(self):
115125
"""Should create a child span for callproc"""
116126
with (

tests/opentelemetry-docker-tests/tests/pymysql/test_pymysql_functional.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,16 @@ def test_executemany(self):
102102
self._cursor.executemany(stmt, data)
103103
self.validate_spans("INSERT")
104104

105+
def test_executemany_with_cursor_iteration(self):
106+
"""Should create a child span for executemany while iterating over the cursor"""
107+
stmt = "SELECT * FROM test"
108+
with self._tracer.start_as_current_span("rootSpan"):
109+
with self._connection.cursor() as cursor:
110+
cursor.execute(stmt)
111+
for _row in cursor:
112+
pass
113+
self.validate_spans("SELECT")
114+
105115
def test_callproc(self):
106116
"""Should create a child span for callproc"""
107117
with (

0 commit comments

Comments
 (0)