Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

compile-flag for single-execution to return value instead of printing it #61496

Closed
albertz mannequin opened this issue Feb 25, 2013 · 6 comments
Closed

compile-flag for single-execution to return value instead of printing it #61496

albertz mannequin opened this issue Feb 25, 2013 · 6 comments
Labels
interpreter-core (Objects, Python, Grammar, and Parser dirs) type-feature A feature request or enhancement

Comments

@albertz
Copy link
Mannequin

albertz mannequin commented Feb 25, 2013

BPO 17294
Nosy @birkenfeld, @terryjreedy, @albertz

Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.

Show more details

GitHub fields:

assignee = None
closed_at = <Date 2013-10-18.22:42:07.718>
created_at = <Date 2013-02-25.13:11:26.444>
labels = ['interpreter-core', 'type-feature']
title = 'compile-flag for single-execution to return value instead of printing it'
updated_at = <Date 2013-10-22.19:13:27.061>
user = 'https://github.com/albertz'

bugs.python.org fields:

activity = <Date 2013-10-22.19:13:27.061>
actor = 'georg.brandl'
assignee = 'none'
closed = True
closed_date = <Date 2013-10-18.22:42:07.718>
closer = 'terry.reedy'
components = ['Interpreter Core']
creation = <Date 2013-02-25.13:11:26.444>
creator = 'Albert.Zeyer'
dependencies = []
files = []
hgrepos = []
issue_num = 17294
keywords = []
message_count = 6.0
messages = ['182934', '199892', '199942', '200322', '200930', '200982']
nosy_count = 3.0
nosy_names = ['georg.brandl', 'terry.reedy', 'Albert.Zeyer']
pr_nums = []
priority = 'normal'
resolution = 'rejected'
stage = 'resolved'
status = 'closed'
superseder = None
type = 'enhancement'
url = 'https://bugs.python.org/issue17294'
versions = ['Python 3.4']

@albertz
Copy link
Mannequin Author

albertz mannequin commented Feb 25, 2013

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

@albertz albertz mannequin added interpreter-core (Objects, Python, Grammar, and Parser dirs) type-feature A feature request or enhancement labels Feb 25, 2013
@birkenfeld
Copy link
Member

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").

@albertz
Copy link
Mannequin Author

albertz mannequin commented Oct 14, 2013

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.

@albertz albertz mannequin reopened this Oct 14, 2013
@terryjreedy
Copy link
Member

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 bpo-19290 about clarifying compile modes and their interaction with eval.

@albertz
Copy link
Mannequin Author

albertz mannequin commented Oct 22, 2013

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.)

@birkenfeld
Copy link
Member

Terry, thanks for your explanation; I was trying to find some time to write something similar...

@ezio-melotti ezio-melotti transferred this issue from another repository Apr 10, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
interpreter-core (Objects, Python, Grammar, and Parser dirs) type-feature A feature request or enhancement
Projects
None yet
Development

No branches or pull requests

2 participants