This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

classification
Title: compile-flag for single-execution to return value instead of printing it
Type: enhancement Stage: resolved
Components: Interpreter Core Versions: Python 3.4
process
Status: closed Resolution: rejected
Dependencies: Superseder:
Assigned To: Nosy List: Albert.Zeyer, georg.brandl, terry.reedy
Priority: normal Keywords:

Created on 2013-02-25 13:11 by Albert.Zeyer, last changed 2022-04-11 14:57 by admin. This issue is now closed.

Messages (6)
msg182934 - (view) Author: Albert Zeyer (Albert.Zeyer) * Date: 2013-02-25 13:11
`compile(s, "<interactive>", "single")` would generate a code object which prints the value of the evaluated string if that is an expression. This is what you would normally want in a REPL.

Instead of printing the value, it might make more sense to return it and to leave it to the developer - there are many cases where it shouldn't end up on stdout but somewhere else.

There could be an additional compile-flag which would make a code-object returning the value instead of printing it.

Note that I have come up with a workaround:

def interactive_py_compile(source, filename="<interactive>"):
    c = compile(source, filename, "single")

    # we expect this at the end:
    #   PRINT_EXPR     
    #   LOAD_CONST
    #   RETURN_VALUE    
    import dis
    if ord(c.co_code[-5]) != dis.opmap["PRINT_EXPR"]:
        return c
    assert ord(c.co_code[-4]) == dis.opmap["LOAD_CONST"]
    assert ord(c.co_code[-1]) == dis.opmap["RETURN_VALUE"]

    code = c.co_code[:-5]
    code += chr(dis.opmap["RETURN_VALUE"])

    CodeArgs = [
        "argcount", "nlocals", "stacksize", "flags", "code",
        "consts", "names", "varnames", "filename", "name",
        "firstlineno", "lnotab", "freevars", "cellvars"]
    c_dict = dict([(arg, getattr(c, "co_" + arg)) for arg in CodeArgs])
    c_dict["code"] = code

    import types
    c = types.CodeType(*[c_dict[arg] for arg in CodeArgs])
    return c


My related StackOverflow question:
http://stackoverflow.com/questions/15059372/python-use-of-eval-in-interactive-terminal-how-to-get-return-value-what-compi
msg199892 - (view) Author: Georg Brandl (georg.brandl) * (Python committer) Date: 2013-10-14 14:11
If you know you have an expression and would like the value, you should use the "eval" mode.

In the case of "single", you can adapt sys.displayhook to change where to print (if at all) the values.  Note that "single" sometimes prints multiple values (try e.g. "for i in range(10):\n    i\n").
msg199942 - (view) Author: Albert Zeyer (Albert.Zeyer) * Date: 2013-10-14 19:15
I don't know that I have an expression and I want it also to work if it is not an expression. Basically I really want the 'single' behavior. (My not-so-uncommon use case: Have an interactive shell where the output on stdout does not make sense. Also I might want to save references to returned values.)

displayhook is not an option in any serious bigger project because you don't want to do overwrite that globally.
msg200322 - (view) Author: Terry J. Reedy (terry.reedy) * (Python committer) Date: 2013-10-18 22:42
Albert: enhancements can only go in future releases.

Also, when a core developer rejects a suggestion and closes an issue, it is better to just request a re-open, or otherwise post to python-list or python-ideas to refine the idea and possible gather more support. We really do not like Status header setting wars.

Mode 'single' is not what you actually want. Its only purpose is add the print that you do not want*. It is otherwise the same as mode 'exec' (except for requiring exactly 1 statement rather than 0 to n). What you are asking is that 'single' act like 'eval' for expressions.  That is what your interactive_py_compile effectively does. However, this can be done much easier and without being CPython 2 specific as follows:

def ee_compile(code, src=''):
    try:
        return compile(code, src, 'eval')
    except SyntaxError:
        return compile(code, src, 'exec')  # or 'single' would work

a = eval(ee_compile('1+1'))
b = eval(ee_compile('c = 3'))
print(a, b, c)
# 2 None 3

With 2.7, your function gives the exact same result. I could not get it to run on 3.3: even after removing the ord calls and changing chr to bytes (and making the arg a tuple), the CodeType call failed.

* I believe the only reason 'single' exists, as a variant of 'exec', is to make loops like the following print non-None values of expressions but otherwise ignore them.

for statement in user_input():
  if statement:
    exec(compile(statement, '<input>', 'single'))

You can replace the last line with
    v = eval(ee_compile(statement, '<input>', 'single'))
    process(v)

Anyway, I agree with Georg that we do not need to modify compile. I have opened a separate issue #19290 about clarifying compile modes and their interaction with eval.
msg200930 - (view) Author: Albert Zeyer (Albert.Zeyer) * Date: 2013-10-22 12:20
Thanks a lot for the long and detailed response! I didn't meant to start a header war; I thought that my request was misunderstood and thus the header changes were by mistake. But I guess it is a good suggestion to leave that decision to a core dev.

I still thing that this would have been more straight-forward in the first place:

for statement in user_input():
  if statement:
    value = exec(compile(statement, '<input>', 'single'))
    if value is not None: print value

Because it is more explicit. But because introducing such an incompatible change is bad, I thought it's a good idea to add another compile-mode.

Your `ee_compile` seems somewhat inefficient to me because you call `compile` twice and I don't like solutions like this very much (try one thing, then try another thing) as rock-solid solutions. (Of course, neither is `interactive_py_compile`, that one just shows what I want.)
msg200982 - (view) Author: Georg Brandl (georg.brandl) * (Python committer) Date: 2013-10-22 19:13
Terry, thanks for your explanation; I was trying to find some time to write something similar...
History
Date User Action Args
2022-04-11 14:57:42adminsetgithub: 61496
2013-10-22 19:13:27georg.brandlsetmessages: + msg200982
2013-10-22 12:20:29Albert.Zeyersetmessages: + msg200930
2013-10-18 22:42:07terry.reedysetstatus: open -> closed

versions: - Python 3.1, Python 2.7, Python 3.2, Python 3.3, Python 3.5
nosy: + terry.reedy

messages: + msg200322
resolution: rejected
stage: resolved
2013-10-14 19:15:08Albert.Zeyersetstatus: closed -> open
resolution: rejected -> (no value)
messages: + msg199942
2013-10-14 14:11:39georg.brandlsetstatus: open -> closed

nosy: + georg.brandl
messages: + msg199892

resolution: rejected
2013-02-25 13:11:26Albert.Zeyercreate