Interpreter-style output in Python 3 (maybe about

2019-07-24 23:57发布

问题:

I'm making a little toy command window with Tk, and currently trying to make it copy some interpreter behavior.

I'd never scrutinized the interpreter before, but it's decisions on when to print a value are a little mystifying.

>>> 3 + 4  # implied print(...)
7
>>> 3      # implied print(...)
3
>>> a = 3  # no output, no implied print(...), bc result is None maybe?
>>> None   # no output, no print(...) implied... doesn't like None?
>>> print(None)  # but it doesn't just ban all Nones, allows explicit print()
None
>>> str(None) # unsurprising, the string 'None' is just a string, and echoed
'None'

The goal is to mimic this behavior, printing some Nones, not others (made slightly more complicated because I'm not entirely sure what the rules are).

So, turning to my program, I have history_text and entry_text, which are StringVar()s that control a label above an entry box in the Tk window. Then the following event is bound to the Return key, to process commands and update the history with the result.

def to_history(event):
    print("command entered")  # note to debugging window

    last_history = history_text.get()

    # hijack stdout
    buffer = io.StringIO('')
    sys.stdout = buffer

    # run command, output to buffer
    exec(entry_text.get())

    # buffered output to a simple string
    buffer.seek(0)
    buffer_str = ''
    for line in buffer.readlines():
        # maybe some rule goes here to decide if an implied 'print(...)' is needed
        buffer_str = buffer_str + line + '\n'

    # append typed command for echo
    new_history = entry_text.get() + '\n' + buffer_str

    # cleanup (let stdout go home)
    sys.stdout = sys.__stdout__
    buffer.close()

    history_text.set(last_history + "\n" + new_history)
    entry_text.set('')

As is, it does not provide any output for a simple entry of '3' or 'None' or even '3 + 4'. Adding an implied print() statement all the time seems to print too often, I don't skip the print for 'None' or 'a = 3' type statements.

I found some documentation for sys.displayhook, which seems to govern when the interpreter will actually display a result, but I'm not sure how to use it here. I thought I could just wrap sys.displayhook() around my exec() call, and have it do all this work for me... but found that it does not imply print() statements for statements like '3 + 4' or '3'.

Any suggestions? Am I on the right track with sys.displayhook?

回答1:

The interpreter prints out repr(result) only if result is not None.

There are no "implied prints" like you thought.

  • 3 + 4 results to 7, so repr(7) is printed
  • a = 3 is an assignment, I think nothing is printed because it does not work with eval
  • None results to None, so nothing is printed
  • print(None) results to None (because the print function returns nothing), so nothing is printed. However, the print function itself printed the None.

I honestly didn't read your code, but here's a function that takes a string with code and produces the same output as the interpreter would:

def interactive(code):
    try:
        result = eval(code)
        if result is not None:
            print(repr(result))
    except SyntaxError:
        exec(code)