3

In an interactive Python shell, I can get the result of the last statement by using an underscore:

>>> x = 1
>>> y = 2
>>> x+y
3
>>> print(_)
3

How to make this code work outside of an interactive shell, like from a Python script?

For example, I have code.py :

x = 1
y = 2
x+y

I want to run

python show_the_latest.py code.py

and get 3 as the output:

$ python show_the_latest.py code.py
3

Limitations:

  • The script is given by a user. I don't know the last statement.
  • Parsing the script is possible. But it should be a proper parsing with building an AST, not a hack.
Gino Mempin
  • 25,369
  • 29
  • 96
  • 135
Dmitry Petrov
  • 1,490
  • 1
  • 19
  • 34
  • 3
    `print(r)`, perhaps? It’s not clear why you’d need anything like this when running outside the Python REPL. – esqew May 05 '23 at 20:38
  • 3
    The code in your question doesn't work in an interactive shell either. Instead of `r = x + y`, you would need to enter simply `x + y`. The `_` trick only works when the previous line contained only an expression, not an assignment. – slothrop May 05 '23 at 20:45
  • 1
    `_` contains the last thing that was printed by the REPL. But when you run a script, nothing is automatically printed, so there's no need for `_`. This is a convenience so you don't have to assign everything to a variable and print it by hand. – Barmar May 05 '23 at 20:46
  • @esqew I need to simulate the same behavior as SQL script where the last statement is the result of the script that needs to be output. – Dmitry Petrov May 05 '23 at 20:49
  • Have you tried building the AST yourself? I think you'd just need to do up a `NodeTransformer` to wrap the last expression/statement with `print()`. – wjandrea May 05 '23 at 22:50
  • @wjandrea yes, not bash specific - I need Python code - `show_the_latest.py `. Added your line. Thank you! Re AST - I was hopping there are some standard way of building it :) – Dmitry Petrov May 06 '23 at 00:38

1 Answers1

0

This is a complete hack, but it works for your given example. The downside is that its wrapping the last line of the code to be executed into a print() function, so if the target routine actually has a print statement last, you're going to get a None as output:

#show_the_latest.py
import sys
import subprocess

try:
    target = sys.argv[1]
except IndexError:
    print("Target routine was not passed in")
    sys.exit()

with open(target) as program:
    code = [line.strip() for line in program.readlines() if line.strip() != ""]
    
if code:
    code[-1] = f"print({code[-1]})"
    to_execute = ["python", "-c", "; ".join(code)]
    subprocess.Popen(to_execute)

code.py

x = 1
y = 2
x+y

Then python show_the_latest.py code.py will output 3

Note that this is definitely not completely robust, and there should probably be a more complete line validation check within the list comprehension that is building the code to be executed. For example, in its current form, if any line contains a comment #, then nothing after that will be executed, because the -c modifier of the python executable will consider everything after the # to be part of the comment. The check I did include will make sure that blank lines don't result in a syntax error.

nigh_anxiety
  • 1,428
  • 2
  • 4
  • 12
  • Thank you for the answer. However, I'm looking for a more robust solution. There are multiple cases when the last line does not actually reflects the last statements - comment, under `if`, function, etc. – Dmitry Petrov May 06 '23 at 19:14