Search code examples
pythoncpythonpython-internals

How does CPython's interpreter know to print the result of the last expression?


I've been digging around the source code to figure-out at which point the result is printed. For example:

>>> x = 1
>>> x + 2
3

The above two statements are compiled to:

  1           0 LOAD_CONST               0 (1)
              3 STORE_NAME               0 (x)
              6 LOAD_CONST               1 (None)
              9 RETURN_VALUE

and

  1           0 LOAD_NAME                0 (x)
              3 LOAD_CONST               0 (2)
              6 BINARY_ADD
              7 RETURN_VALUE

The first statement doesn't print anything because None is the returned value. The second returns the result of the addition.

CPython's interactive loop calls PyRun_InteractiveOneObjectEx() for each input. This gets the AST and invokes run_mod() to compile that AST to byte code and then evaluate the result in the virtual machine. The returned Python object that PyRun_InteractiveOneObjectEx() gets is simply the top of the VM's stack.

So far all of this is what I would expect. But then the returned value seems to be thrown away! When is this printed by the REPL?

As an aside, I can see that interactive mode does change the tokenizer; it invokes PyOS_Readline with the sys.ps1 prompt (">>> " by default). I checked for a similar change in pythonrun.c, but no luck.


Solution

  • You are showing disassemblies of the bytecode as generated by having the code in a function. That isn't how interactive code is compiled: it uses a special 'single' mode (3rd parameter to compile(), if you were doing the equivalent in Python code). In this mode, the POP_TOP opcode that discards the value of each expression gets turned into a PRINT_EXPR instead. The reason why x = 1 prints nothing is that statements leave nothing on the stack that needs to be popped, so this transformation doesn't apply.