Instagram / MonkeyType

A Python library that generates static type annotations by collecting runtime types
Other
4.74k stars 170 forks source link

Return type not traced when returning constant (Python ≥ 3.12) #325

Open Feuermurmel opened 7 months ago

Feuermurmel commented 7 months ago

When returning a constant from a function or implicitly returning None, the return type is not captured in the trace:

def foo():
    return 5

def bar():
    print("hello")
$ monkeytype run foo_2.py 
hello
$ sqlite3 monkeytype.sqlite3 "select * from monkeytype_call_traces where module = 'foo'"
2024-02-03 14:09:53.060108  foo foo {}  NULL    NULL
2024-02-03 14:09:53.060120  foo bar {}  NULL    NULL

I think the problem is that starting with Python 3.12, a new opcode RETURN_CONST is used in these cases. This fixes the problem for me:

diff --git a/monkeytype/tracing.py b/monkeytype/tracing.py
index e1e1eeb..a35497c 100644
--- a/monkeytype/tracing.py
+++ b/monkeytype/tracing.py
@@ -177,6 +177,7 @@ def get_func(frame: FrameType) -> Optional[Callable[..., Any]]:

 RETURN_VALUE_OPCODE = opcode.opmap["RETURN_VALUE"]
+RETURN_CONST_OPCODE = opcode.opmap["RETURN_CONST"]
 YIELD_VALUE_OPCODE = opcode.opmap["YIELD_VALUE"]

 # A CodeFilter is a predicate that decides whether or not a the call for the
@@ -258,7 +259,7 @@ class CallTracer:
         elif last_opcode == YIELD_VALUE_OPCODE:
             trace.add_yield_type(typ)
         else:
-            if last_opcode == RETURN_VALUE_OPCODE:
+            if last_opcode in (RETURN_VALUE_OPCODE, RETURN_CONST_OPCODE):
                 trace.return_type = typ
             del self.traces[frame]
             self.logger.log(trace)

I haven't checked if this is the only case where new or changed opcodes break stuff, a lot of things have changed in recent Python versions.