Skip to content

Commit 3ee203a

Browse files
committed
fix: generator wrapping with Python 3.10 (#13835)
We fix an issue with generator wrapping with Python 3.10 that could have caused an exception to be raised after the generator had been used and exhausted. The issue has been caught by the exploration tests. - [x] PR author has checked that all the criteria below are met - The PR description includes an overview of the change - The PR description articulates the motivation for the change - The change includes tests OR the PR description describes a testing strategy - The PR description notes risks associated with the change, if any - Newly-added code is easy to change - The change follows the [library release note guidelines](https://ddtrace.readthedocs.io/en/stable/releasenotes.html) - The change includes or references documentation updates if necessary - Backport labels are set (if [applicable](https://ddtrace.readthedocs.io/en/latest/contributing.html#backporting)) - [x] Reviewer has checked that all the criteria below are met - Title is accurate - All changes are related to the pull request's stated goal - Avoids breaking [API](https://ddtrace.readthedocs.io/en/stable/versioning.html#interfaces) changes - Testing strategy adequately addresses listed risks - Newly-added code is easy to change - Release note makes sense to a user of the library - If necessary, author has acknowledged and discussed the performance implications of this PR as reported in the benchmarks PR comment - Backport labels are set in a manner that is consistent with the [release branch maintenance policy](https://ddtrace.readthedocs.io/en/latest/contributing.html#backporting) (cherry picked from commit 5d10180)
1 parent b712298 commit 3ee203a

File tree

11 files changed

+19
-159
lines changed

11 files changed

+19
-159
lines changed

ddtrace/_trace/tracer.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -775,8 +775,7 @@ def func_wrapper(*args, **kwargs):
775775
)
776776

777777
with self.trace(span_name, service=service, resource=resource, span_type=span_type):
778-
return_value = yield from f(*args, **kwargs)
779-
return return_value
778+
yield from f(*args, **kwargs)
780779

781780
return func_wrapper
782781

ddtrace/contrib/internal/pytest/_plugin_v1.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -417,7 +417,7 @@ def _get_test_class_hierarchy(item):
417417

418418

419419
def pytest_load_initial_conftests(early_config, parser, args):
420-
if _is_enabled_early(early_config, args):
420+
if _is_enabled_early(early_config):
421421
# Enables experimental use of ModuleCodeCollector for coverage collection.
422422
from ddtrace.internal.ci_visibility.coverage import USE_DD_COVERAGE
423423
from ddtrace.internal.logger import get_logger

ddtrace/contrib/internal/pytest/_plugin_v2.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,7 @@ def _pytest_load_initial_conftests_pre_yield(early_config, parser, args):
209209
ModuleCodeCollector has a tangible impact on the time it takes to load modules, so it should only be installed if
210210
coverage collection is requested by the backend.
211211
"""
212-
if not _is_enabled_early(early_config, args):
212+
if not _is_enabled_early(early_config):
213213
return
214214

215215
try:

ddtrace/contrib/internal/pytest/_utils.py

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,7 @@ def _extract_span(item):
211211
return getattr(item, "_datadog_span", None)
212212

213213

214-
def _is_enabled_early(early_config, args):
214+
def _is_enabled_early(early_config):
215215
"""Checks if the ddtrace plugin is enabled before the config is fully populated.
216216
217217
This is necessary because the module watchdog for coverage collection needs to be enabled as early as possible.
@@ -222,14 +222,15 @@ def _is_enabled_early(early_config, args):
222222
if not _pytest_version_supports_itr():
223223
return False
224224

225-
if _is_option_true("no-ddtrace", early_config, args):
225+
if (
226+
"--no-ddtrace" in early_config.invocation_params.args
227+
or early_config.getini("no-ddtrace")
228+
or "ddtrace" in early_config.inicfg
229+
and early_config.getini("ddtrace") is False
230+
):
226231
return False
227232

228-
return _is_option_true("ddtrace", early_config, args)
229-
230-
231-
def _is_option_true(option, early_config, args):
232-
return early_config.getoption(option) or early_config.getini(option) or f"--{option}" in args
233+
return "--ddtrace" in early_config.invocation_params.args or early_config.getini("ddtrace")
233234

234235

235236
class _TestOutcome(t.NamedTuple):

ddtrace/internal/wrapping/context.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from abc import ABC
22
from contextvars import ContextVar
33
from inspect import iscoroutinefunction
4+
from inspect import isgeneratorfunction
45
import sys
56
from types import FrameType
67
from types import FunctionType
@@ -671,9 +672,9 @@ def wrap(self) -> None:
671672
pass
672673
i += 1
673674

674-
# Search for the GEN_START instruction
675+
# Search for the GEN_START instruction, which needs to stay on top.
675676
i = 0
676-
if sys.version_info >= (3, 10) and iscoroutinefunction(f):
677+
if sys.version_info >= (3, 10) and (iscoroutinefunction(f) or isgeneratorfunction(f)):
677678
for i, instr in enumerate(bc, 1):
678679
try:
679680
if instr.name == "GEN_START":

releasenotes/notes/ci_visibility-fix-pytest-addopts-early-init-74fb4e207dee18e4.yaml

Lines changed: 0 additions & 5 deletions
This file was deleted.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
fixes:
3+
- |
4+
dynamic instrumentation: fixed an issue with the instrumentation of
5+
generators with Python 3.10.

releasenotes/notes/fix-tracer-wrap-generator-return-value-fbaec09a9670e06b.yaml

Lines changed: 0 additions & 4 deletions
This file was deleted.

tests/contrib/pytest/test_pytest.py

Lines changed: 0 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -218,41 +218,6 @@ def test_ini(ddspan):
218218

219219
assert len(spans) == 4
220220

221-
def test_pytest_addopts_env_var(self):
222-
"""Test enabling ddtrace via the PYTEST_ADDOPTS environment variable."""
223-
py_file = self.testdir.makepyfile(
224-
"""
225-
import pytest
226-
227-
def test_trace(ddspan):
228-
assert ddspan is not None
229-
"""
230-
)
231-
file_name = os.path.basename(py_file.strpath)
232-
rec = self.inline_run(file_name, extra_env={"PYTEST_ADDOPTS": "--ddtrace"})
233-
rec.assertoutcome(passed=1)
234-
spans = self.pop_spans()
235-
236-
assert len(spans) == 4
237-
238-
def test_pytest_addopts_ini(self):
239-
"""Test enabling ddtrace via the `addopts` option in the ini file."""
240-
self.testdir.makefile(".ini", pytest="[pytest]\naddopts = --ddtrace\n")
241-
py_file = self.testdir.makepyfile(
242-
"""
243-
import pytest
244-
245-
def test_ini(ddspan):
246-
assert ddspan is not None
247-
"""
248-
)
249-
file_name = os.path.basename(py_file.strpath)
250-
rec = self.inline_run(file_name)
251-
rec.assertoutcome(passed=1)
252-
spans = self.pop_spans()
253-
254-
assert len(spans) == 4
255-
256221
def test_pytest_command(self):
257222
"""Test that the pytest run command is stored on a test span."""
258223
py_file = self.testdir.makepyfile(

tests/contrib/pytest/test_pytest_early_config.py

Lines changed: 0 additions & 80 deletions
This file was deleted.

0 commit comments

Comments
 (0)