Skip to content

Commit 2e4a380

Browse files
authored
fix(internal): no lazy bytecode imports [backport #7151 to 1.20] (#7274)
Backport of #7151 to 1.20 Remove any lazy bytecode imports that can cause incompatibilities with the module cloning mechanism. Fixes #7144. ## Checklist - [x] Change(s) are motivated and described in the PR description. - [x] Testing strategy is described if automated tests are not included in the PR. - [x] Risk is outlined (performance impact, potential for breakage, maintainability, etc). - [x] Change is maintainable (easy to change, telemetry, documentation). - [x] [Library release note guidelines](https://ddtrace.readthedocs.io/en/stable/releasenotes.html) are followed. If no release note is required, add label `changelog/no-changelog`. - [x] Documentation is included (in-code, generated user docs, [public corp docs](https://github.com/DataDog/documentation/)). - [x] Backport labels are set (if [applicable](https://ddtrace.readthedocs.io/en/latest/contributing.html#backporting)) ## Reviewer Checklist - [x] Title is accurate. - [x] No unnecessary changes are introduced. - [x] Description motivates each change. - [x] Avoids breaking [API](https://ddtrace.readthedocs.io/en/stable/versioning.html#interfaces) changes unless absolutely necessary. - [x] Testing strategy adequately addresses listed risk(s). - [x] Change is maintainable (easy to change, telemetry, documentation). - [x] Release note makes sense to a user of the library. - [x] Reviewer has explicitly acknowledged and discussed the performance implications of this PR as reported in the benchmarks PR comment. - [x] 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) - [x] If this PR touches code that signs or publishes builds or packages, or handles credentials of any kind, I've requested a review from `@DataDog/security-design-and-guidance`. - [x] This PR doesn't touch any of that.
1 parent 01912f5 commit 2e4a380

File tree

4 files changed

+61
-69
lines changed

4 files changed

+61
-69
lines changed

ddtrace/internal/wrapping/__init__.py

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@
1414
except ImportError:
1515
from typing_extensions import Protocol # type: ignore[assignment]
1616

17+
import bytecode as bc
1718
from bytecode import Bytecode
18-
from bytecode import CompilerFlags
1919
from bytecode import Instr
2020

2121
from ddtrace.internal.compat import PYTHON_VERSION_INFO as PY
@@ -38,9 +38,7 @@ def __call__(self, *args, **kwargs):
3838

3939
def _add(lineno):
4040
if PY >= (3, 11):
41-
from bytecode import BinaryOp
42-
43-
return Instr("BINARY_OP", BinaryOp.ADD, lineno=lineno)
41+
return Instr("BINARY_OP", bc.BinaryOp.ADD, lineno=lineno)
4442

4543
return Instr("INPLACE_ADD", lineno=lineno)
4644

@@ -96,8 +94,8 @@ def wrap_bytecode(wrapper, wrapped):
9694

9795
code = wrapped.__code__
9896
lineno = code.co_firstlineno + FIRSTLINENO_OFFSET
99-
varargs = bool(code.co_flags & CompilerFlags.VARARGS)
100-
varkwargs = bool(code.co_flags & CompilerFlags.VARKEYWORDS)
97+
varargs = bool(code.co_flags & bc.CompilerFlags.VARARGS)
98+
varkwargs = bool(code.co_flags & bc.CompilerFlags.VARKEYWORDS)
10199
nargs = code.co_argcount
102100
argnames = code.co_varnames[:nargs]
103101
try:
@@ -122,9 +120,7 @@ def wrap_bytecode(wrapper, wrapped):
122120
]
123121

124122
if code.co_cellvars:
125-
from bytecode import CellVar
126-
127-
instrs[0:0] = [Instr("MAKE_CELL", CellVar(_), lineno=lineno) for _ in code.co_cellvars]
123+
instrs[0:0] = [Instr("MAKE_CELL", bc.CellVar(_), lineno=lineno) for _ in code.co_cellvars]
128124

129125
if code.co_freevars:
130126
instrs.insert(0, Instr("COPY_FREE_VARS", len(code.co_freevars), lineno=lineno))
@@ -133,7 +129,7 @@ def wrap_bytecode(wrapper, wrapped):
133129
if nargs:
134130
instrs.extend(
135131
[
136-
Instr("LOAD_DEREF", CellVar(argname), lineno=lineno)
132+
Instr("LOAD_DEREF", bc.CellVar(argname), lineno=lineno)
137133
if PY >= (3, 11) and argname in code.co_cellvars
138134
else Instr("LOAD_FAST", argname, lineno=lineno)
139135
for argname in argnames
@@ -177,7 +173,7 @@ def wrap_bytecode(wrapper, wrapped):
177173

178174
# If the function has special flags set, like the generator, async generator
179175
# or coroutine, inject unraveling code before the return opcode.
180-
if CompilerFlags.GENERATOR & code.co_flags and not (CompilerFlags.COROUTINE & code.co_flags):
176+
if bc.CompilerFlags.GENERATOR & code.co_flags and not (bc.CompilerFlags.COROUTINE & code.co_flags):
181177
wrap_generator(instrs, code, lineno)
182178
elif PY3:
183179
wrap_async(instrs, code, lineno)
@@ -230,7 +226,7 @@ def wrap(f, wrapper):
230226
nargs += code.kwonlyargcount
231227
except AttributeError:
232228
pass
233-
nargs += bool(code.flags & CompilerFlags.VARARGS) + bool(code.flags & CompilerFlags.VARKEYWORDS)
229+
nargs += bool(code.flags & bc.CompilerFlags.VARARGS) + bool(code.flags & bc.CompilerFlags.VARKEYWORDS)
234230
code.argnames = f.__code__.co_varnames[:nargs]
235231

236232
f.__code__ = code.to_code()

ddtrace/internal/wrapping/asyncs.py

Lines changed: 28 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
import sys
22

3-
from bytecode import Compare
4-
from bytecode import CompilerFlags
3+
import bytecode as bc
54
from bytecode import Instr
6-
from bytecode import Label
75

86
from ddtrace.internal.compat import PYTHON_VERSION_INFO as PY
97

@@ -33,16 +31,16 @@
3331
if PY >= (3, 11):
3432

3533
def wrap_async(instrs, code, lineno):
36-
if CompilerFlags.COROUTINE & code.co_flags:
34+
if bc.CompilerFlags.COROUTINE & code.co_flags:
3735
# DEV: This is just
3836
# >>> return await wrapper(wrapped, args, kwargs)
3937
instrs[0:0] = [
4038
Instr("RETURN_GENERATOR", lineno=lineno),
4139
Instr("POP_TOP", lineno=lineno),
4240
]
4341

44-
send = Label()
45-
send_jb = Label()
42+
send = bc.Label()
43+
send_jb = bc.Label()
4644

4745
instrs[-1:-1] = [
4846
Instr("GET_AWAITABLE", 0, lineno=lineno),
@@ -55,28 +53,25 @@ def wrap_async(instrs, code, lineno):
5553
send,
5654
]
5755

58-
elif CompilerFlags.ASYNC_GENERATOR & code.co_flags:
59-
from bytecode import TryBegin
60-
from bytecode import TryEnd
61-
56+
elif bc.CompilerFlags.ASYNC_GENERATOR & code.co_flags:
6257
instrs[0:0] = [
6358
Instr("RETURN_GENERATOR", lineno=lineno),
6459
Instr("POP_TOP", lineno=lineno),
6560
]
6661

67-
stopiter = Label()
68-
loop = Label()
69-
genexit = Label()
70-
exc = Label()
71-
propagate = Label()
72-
_yield = Label()
62+
stopiter = bc.Label()
63+
loop = bc.Label()
64+
genexit = bc.Label()
65+
exc = bc.Label()
66+
propagate = bc.Label()
67+
_yield = bc.Label()
7368

74-
try_stopiter = TryBegin(stopiter, push_lasti=False)
75-
try_stopiter_2 = TryBegin(stopiter, push_lasti=False)
76-
try_except = TryBegin(genexit, push_lasti=True)
69+
try_stopiter = bc.TryBegin(stopiter, push_lasti=False)
70+
try_stopiter_2 = bc.TryBegin(stopiter, push_lasti=False)
71+
try_except = bc.TryBegin(genexit, push_lasti=True)
7772

78-
send = [Label() for _ in range(3)]
79-
send_jb = [Label() for _ in range(3)]
73+
send = [bc.Label() for _ in range(3)]
74+
send_jb = [bc.Label() for _ in range(3)]
8075

8176
instrs[-1:] = [
8277
try_stopiter,
@@ -93,7 +88,7 @@ def wrap_async(instrs, code, lineno):
9388
Instr("LOAD_CONST", None, lineno=lineno),
9489
send_jb[0],
9590
Instr("SEND", send[0], lineno=lineno),
96-
TryEnd(try_stopiter),
91+
bc.TryEnd(try_stopiter),
9792
try_except,
9893
Instr("YIELD_VALUE", lineno=lineno),
9994
Instr("RESUME", 3, lineno=lineno),
@@ -110,7 +105,7 @@ def wrap_async(instrs, code, lineno):
110105
Instr("PRECALL", 1, lineno=lineno),
111106
Instr("CALL", 1, lineno=lineno),
112107
Instr("JUMP_BACKWARD", loop, lineno=lineno),
113-
TryEnd(try_except),
108+
bc.TryEnd(try_except),
114109
try_stopiter_2,
115110
genexit, # except GeneratorExit:
116111
Instr("PUSH_EXC_INFO", lineno=lineno),
@@ -155,7 +150,7 @@ def wrap_async(instrs, code, lineno):
155150
Instr("SWAP", 2, lineno=lineno),
156151
Instr("POP_EXCEPT", lineno=lineno),
157152
Instr("JUMP_BACKWARD", _yield, lineno=lineno),
158-
TryEnd(try_stopiter_2),
153+
bc.TryEnd(try_stopiter_2),
159154
stopiter, # except StopAsyncIteration:
160155
Instr("PUSH_EXC_INFO", lineno=lineno),
161156
Instr("LOAD_CONST", StopAsyncIteration, lineno=lineno),
@@ -175,7 +170,7 @@ def wrap_async(instrs, code, lineno):
175170
def _compare_exc(label, lineno):
176171
"""Compat helper for comparing exceptions."""
177172
if PY < (3, 9):
178-
return Instr("COMPARE_OP", Compare.EXC_MATCH, lineno=lineno)
173+
return Instr("COMPARE_OP", bc.Compare.EXC_MATCH, lineno=lineno)
179174
return Instr("JUMP_IF_NOT_EXC_MATCH", label, lineno=lineno)
180175

181176
def _jump_if_false(label, lineno):
@@ -198,21 +193,21 @@ def _setup_block(label, lineno):
198193
return Instr("SETUP_FINALLY", label, lineno=lineno)
199194

200195
def wrap_async(instrs, code, lineno):
201-
if CompilerFlags.COROUTINE & code.co_flags:
196+
if bc.CompilerFlags.COROUTINE & code.co_flags:
202197
# DEV: This is just
203198
# >>> return await wrapper(wrapped, args, kwargs)
204199
instrs[-1:-1] = [
205200
Instr("GET_AWAITABLE", lineno=lineno),
206201
Instr("LOAD_CONST", None, lineno=lineno),
207202
Instr("YIELD_FROM", lineno=lineno),
208203
]
209-
elif CompilerFlags.ASYNC_GENERATOR & code.co_flags:
210-
stopiter = Label()
211-
loop = Label()
212-
genexit = Label()
213-
exc = Label()
214-
propagate = Label()
215-
_yield = Label()
204+
elif bc.CompilerFlags.ASYNC_GENERATOR & code.co_flags:
205+
stopiter = bc.Label()
206+
loop = bc.Label()
207+
genexit = bc.Label()
208+
exc = bc.Label()
209+
propagate = bc.Label()
210+
_yield = bc.Label()
216211

217212
instrs[-1:] = [
218213
_setup_block(stopiter, lineno=lineno),

ddtrace/internal/wrapping/generators.py

Lines changed: 20 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
import sys
22

3-
from bytecode import Compare
3+
import bytecode as bc
44
from bytecode import Instr
5-
from bytecode import Label
65

76
from ddtrace.internal.compat import PYTHON_VERSION_INFO as PY
87

@@ -31,24 +30,21 @@
3130
if PY >= (3, 11):
3231

3332
def wrap_generator(instrs, code, lineno):
34-
from bytecode import TryBegin
35-
from bytecode import TryEnd
36-
3733
instrs[0:0] = [
3834
Instr("RETURN_GENERATOR", lineno=lineno),
3935
Instr("POP_TOP", lineno=lineno),
4036
]
4137

42-
stopiter = Label()
43-
loop = Label()
44-
genexit = Label()
45-
exc = Label()
46-
propagate = Label()
47-
_yield = Label()
38+
stopiter = bc.Label()
39+
loop = bc.Label()
40+
genexit = bc.Label()
41+
exc = bc.Label()
42+
propagate = bc.Label()
43+
_yield = bc.Label()
4844

49-
try_stopiter = TryBegin(stopiter, push_lasti=False)
50-
try_stopiter_2 = TryBegin(stopiter, push_lasti=False)
51-
try_except = TryBegin(genexit, push_lasti=True)
45+
try_stopiter = bc.TryBegin(stopiter, push_lasti=False)
46+
try_stopiter_2 = bc.TryBegin(stopiter, push_lasti=False)
47+
try_except = bc.TryBegin(genexit, push_lasti=True)
5248

5349
instrs[-1:] = [
5450
try_stopiter,
@@ -62,7 +58,7 @@ def wrap_generator(instrs, code, lineno):
6258
loop,
6359
Instr("PRECALL", 1, lineno=lineno),
6460
Instr("CALL", 1, lineno=lineno),
65-
TryEnd(try_stopiter),
61+
bc.TryEnd(try_stopiter),
6662
_yield,
6763
try_except,
6864
Instr("YIELD_VALUE", lineno=lineno),
@@ -72,7 +68,7 @@ def wrap_generator(instrs, code, lineno):
7268
Instr("LOAD_FAST", "__ddgensend", lineno=lineno),
7369
Instr("SWAP", 2, lineno=lineno),
7470
Instr("JUMP_BACKWARD", loop, lineno=lineno),
75-
TryEnd(try_except),
71+
bc.TryEnd(try_except),
7672
try_stopiter_2,
7773
genexit, # except GeneratorExit:
7874
Instr("PUSH_EXC_INFO", lineno=lineno),
@@ -100,7 +96,7 @@ def wrap_generator(instrs, code, lineno):
10096
Instr("SWAP", 2, lineno=lineno),
10197
Instr("POP_EXCEPT", lineno=lineno),
10298
Instr("JUMP_BACKWARD", _yield, lineno=lineno),
103-
TryEnd(try_stopiter_2),
99+
bc.TryEnd(try_stopiter_2),
104100
stopiter, # except StopIteration:
105101
Instr("PUSH_EXC_INFO", lineno=lineno),
106102
Instr("LOAD_CONST", StopIteration, lineno=lineno),
@@ -120,7 +116,7 @@ def wrap_generator(instrs, code, lineno):
120116
def _compare_exc(label, lineno):
121117
"""Compat helper for comparing exceptions."""
122118
if PY < (3, 9):
123-
return Instr("COMPARE_OP", Compare.EXC_MATCH, lineno=lineno)
119+
return Instr("COMPARE_OP", bc.Compare.EXC_MATCH, lineno=lineno)
124120
return Instr("JUMP_IF_NOT_EXC_MATCH", label, lineno=lineno)
125121

126122
def _jump_if_false(label, lineno):
@@ -154,12 +150,12 @@ def _call_variadic(lineno):
154150
return Instr("CALL_FUNCTION_EX", 0, lineno=lineno)
155151

156152
def wrap_generator(instrs, code, lineno):
157-
stopiter = Label()
158-
loop = Label()
159-
genexit = Label()
160-
exc = Label()
161-
propagate = Label()
162-
_yield = Label()
153+
stopiter = bc.Label()
154+
loop = bc.Label()
155+
genexit = bc.Label()
156+
exc = bc.Label()
157+
propagate = bc.Label()
158+
_yield = bc.Label()
163159

164160
instrs[-1:] = [
165161
_setup_block(stopiter, lineno),
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
fixes:
3+
- |
4+
tracing: Fix an issue with some integrations, such as OpenAI, that caused an
5+
exception on start-up when using gevent.

0 commit comments

Comments
 (0)