Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 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 @@ -290,7 +291,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
2 changes: 1 addition & 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
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
49 changes: 47 additions & 2 deletions ddtrace/internal/wrapping/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,53 @@
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, 14):
CONTEXT_HEAD.parse(
r"""
load_const {context_enter}
push_null
call 0
pop_top
"""
)
CONTEXT_RETURN.parse(
r"""
push_null
load_const {context_return}
swap 3
call 1
"""
)

CONTEXT_RETURN_CONST = Assembly()
CONTEXT_RETURN_CONST.parse(
r"""
load_const {context_return}
push_null
load_const {value}
call 1
"""
)

CONTEXT_FOOT.parse(
r"""
try @_except lasti
push_exc_info
load_const {context_exit}
push_null
call 0
pop_top
reraise 2
tried

_except:
copy 3
pop_except
reraise 1
"""
)
elif sys.version_info >= (3, 13):
CONTEXT_HEAD.parse(
r"""
Expand Down
81 changes: 80 additions & 1 deletion ddtrace/internal/wrapping/generators.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,86 @@
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_attr $close
call 0
swap 2
pop_except
load_const None
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