Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion ddtrace/debugging/_expressions.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
from typing import Tuple
from typing import Union

from bytecode import BinaryOp
from bytecode import Bytecode
from bytecode import Compare
from bytecode import Instr
Expand Down Expand Up @@ -301,7 +302,12 @@ def _compile_arg_operation(self, ast: DDASTType) -> Optional[List[Instr]]:
raise ValueError("Invalid argument: %r" % a)
if cb is None:
raise ValueError("Invalid argument: %r" % b)
return cv + ca + cb + [Instr("BUILD_SLICE", 2), Instr("BINARY_SUBSCR")]

if PY >= (3, 14):
subscr_instruction = Instr("BINARY_OP", BinaryOp.SUBSCR)
else:
subscr_instruction = Instr("BINARY_SUBSCR")
return cv + ca + cb + [Instr("BUILD_SLICE", 2), subscr_instruction]

if _type == "filter":
a, b = args
Expand Down
7 changes: 6 additions & 1 deletion ddtrace/internal/assembly.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ def transform_instruction(opcode: str, arg: t.Any) -> t.Tuple[str, t.Any]:
opcode = "LOAD_ATTR"
arg = (True, arg)
elif opcode.upper() == "LOAD_ATTR" and not isinstance(arg, tuple):
arg = (False, arg)
arg = (sys.version_info >= (3, 14), arg)

return opcode, arg

Expand Down Expand Up @@ -157,6 +157,11 @@ def parse_try_end(self, line: str) -> t.Optional[bc.TryEnd]:

def parse_opcode(self, text: str) -> str:
opcode = text.upper()

# `dis` doesn't include `LOAD_METHOD` in 3.14.0rc1
if sys.version_info >= (3, 14) and opcode == "LOAD_METHOD":
return opcode

if opcode not in dis.opmap:
raise ValueError("unknown opcode %s" % opcode)

Expand Down
4 changes: 2 additions & 2 deletions ddtrace/internal/bytecode_injection/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ class InvalidLine(Exception):
# the stack to the state prior to the call.

INJECTION_ASSEMBLY = Assembly()
if PY >= (3, 14):
raise NotImplementedError("Python >= 3.14 is not supported yet")
if PY >= (3, 15):
raise NotImplementedError("Python >= 3.15 is not supported yet")
elif PY >= (3, 13):
INJECTION_ASSEMBLY.parse(
r"""
Expand Down
3 changes: 2 additions & 1 deletion ddtrace/internal/symbol_db/symbols.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,8 @@ def get_fields(cls: type) -> t.Set[str]:
return {
code.co_names[b.arg]
for a, b in zip(*(islice(t, i, None) for i, t in enumerate(tee(dis.get_instructions(code), 2))))
if a.opname == "LOAD_FAST" and a.arg == 0 and b.opname == "STORE_ATTR"
# Python 3.14 changed this to LOAD_FAST_BORROW
if a.opname.startswith("LOAD_FAST") and a.arg & 15 == 0 and b.opname == "STORE_ATTR"
}
except AttributeError:
return set()
Expand Down
128 changes: 127 additions & 1 deletion ddtrace/internal/wrapping/asyncs.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,133 @@
ASYNC_GEN_ASSEMBLY = Assembly()
ASYNC_HEAD_ASSEMBLY = None

if PY >= (3, 12):
if PY >= (3, 14):
ASYNC_HEAD_ASSEMBLY = Assembly()
ASYNC_HEAD_ASSEMBLY.parse(
r"""
return_generator
pop_top
"""
)

COROUTINE_ASSEMBLY.parse(
r"""
get_awaitable 0
load_const None
presend:
send @send
yield_value 2
resume 3
jump_backward_no_interrupt @presend
send:
end_send
"""
)

ASYNC_GEN_ASSEMBLY.parse(
r"""
try @stopiter
copy 1
store_fast $__ddgen
load_attr (False, 'asend')
store_fast $__ddgensend
load_fast $__ddgen
load_attr (True, '__anext__')
call 0
loop:
get_awaitable 0
load_const None
presend0:
send @send0
tried
try @genexit lasti
yield_value 3
resume 3
jump_backward_no_interrupt @loop
send0:
end_send
yield:
call_intrinsic_1 asm.Intrinsic1Op.INTRINSIC_ASYNC_GEN_WRAP
yield_value 3
resume 1
push_null
swap 2
load_fast $__ddgensend
swap 2
call 1
jump_backward @loop
tried
genexit:
try @stopiter
push_exc_info
load_const GeneratorExit
check_exc_match
pop_jump_if_false @exc
pop_top
load_fast $__ddgen
load_attr (True, 'aclose')
call 0
get_awaitable 0
load_const None
presend1:
send @send1
yield_value 4
resume 3
jump_backward_no_interrupt @presend1
send1:
end_send
pop_top
pop_except
load_const None
return_value
exc:
pop_top
push_null
load_fast $__ddgen
load_attr (False, 'athrow')
push_null
load_const sys.exc_info
call 0
call_function_ex
get_awaitable 0
load_const None
presend2:
send @send2
yield_value 4
resume 3
jump_backward_no_interrupt @presend2
send2:
end_send
swap 2
pop_except
jump_backward @yield
tried
stopiter:
push_exc_info
load_const StopAsyncIteration
check_exc_match
pop_jump_if_false @propagate
pop_top
pop_except
load_const None
return_value
propagate:
reraise 0
"""
)


elif PY >= (3, 12):
ASYNC_HEAD_ASSEMBLY = Assembly()
ASYNC_HEAD_ASSEMBLY.parse(
r"""
Expand Down
4 changes: 2 additions & 2 deletions ddtrace/internal/wrapping/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,8 @@
CONTEXT_RETURN = Assembly()
CONTEXT_FOOT = Assembly()

if sys.version_info >= (3, 14):
raise NotImplementedError("Python >= 3.14 is not supported yet")
if sys.version_info >= (3, 15):
raise NotImplementedError("Python >= 3.15 is not supported yet")
elif sys.version_info >= (3, 13):
CONTEXT_HEAD.parse(
r"""
Expand Down
80 changes: 79 additions & 1 deletion ddtrace/internal/wrapping/generators.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,85 @@
GENERATOR_ASSEMBLY = Assembly()
GENERATOR_HEAD_ASSEMBLY = None

if PY >= (3, 12):
if PY >= (3, 14):
GENERATOR_HEAD_ASSEMBLY = Assembly()
GENERATOR_HEAD_ASSEMBLY.parse(
r"""
return_generator
pop_top
"""
)

GENERATOR_ASSEMBLY.parse(
r"""
try @stopiter
copy 1
store_fast $__ddgen
load_attr $send
store_fast $__ddgensend
push_null
load_const next
load_fast $__ddgen

loop:
call 1
tried

yield:
try @genexit lasti
yield_value 3
resume 1
push_null
swap 2
load_fast $__ddgensend
swap 2
jump_backward @loop
tried

genexit:
try @stopiter
push_exc_info
load_const GeneratorExit
check_exc_match
pop_jump_if_false @exc
pop_top
load_fast $__ddgen
load_method $close
call 0
swap 2
pop_except
return_value

exc:
pop_top
push_null
load_fast $__ddgen
load_attr $throw
push_null
load_const sys.exc_info
call 0
call_function_ex
swap 2
pop_except
jump_backward @yield
tried

stopiter:
push_exc_info
load_const StopIteration
check_exc_match
pop_jump_if_false @propagate
pop_top
pop_except
load_const None
return_value

propagate:
reraise 0
"""
)

elif PY >= (3, 12):
GENERATOR_HEAD_ASSEMBLY = Assembly()
GENERATOR_HEAD_ASSEMBLY.parse(
r"""
Expand Down
Loading