How can I check command running result?

前端 未结 1 736
眼角桃花
眼角桃花 2021-01-29 11:00

Inside a customised python interpreter, if run single command, I can see the command output.
But if I put commands into a file and run with execfile(\'file.py\'), except pri

相关标签:
1条回答
  • 2021-01-29 11:38

    'command output' is not really a valid term. The Python interactive interpreter will echo the result of any expression statement, unless that expression produced None. An expression statement is a top-level statement in a module body that only contains a single expression (as opposed to other simple statements such as assignment or import or compound statements such as if or while. From the linked documentation:

    In interactive mode, if the value is not None, it is converted to a string using the built-in repr() function and the resulting string is written to standard output [...] on a line by itself. (Expression statements yielding None are not written, so that procedure calls do not cause any output.)

    Doing so in a script would not be practical; you'd never be able to run any expression statement without the user of the script having to then decipher every little thing printed to the screen. Scripts usually want to control what is printed (to the point of not printing anything at all, but using logging instead, for example).

    If you are using execfile() to run scripts, you could switch to parsing the script into an abstract syntax tree, at which point you can transform the expression statement nodes to add printing. The resulting transformed AST can be fed directly to the compile() function, and the exec() function can take the output of the compilation step.

    I'd use an extra global function passed to exec() here:

    import ast
    
    class ExprStatementPrinter(ast.NodeTransformer):
        def visit_Expr(self, node):  # expression statement node
            # call the function __print_expr_result__, passing in the expression
            # contents; replace the original expression contents with that call.
            new_value = ast.Call(
                ast.Name('__print_expr_result__', ast.Load()),  # load as a global
                [node.value], [], None, None)  # node.value is the only argument
            # to be able to execute, we need to set the lineno and coll_offset; just
            # reuse the values of the expr node, then fix up any children
            node.value = ast.copy_location(new_value, node.value)
            return ast.fix_missing_locations(node)
    
    def print_expr_result(result):
        if result is not None:
            print repr(result)
    
    def execfile(filename):
        with open(filename) as source:
            tree = ast.parse(source.read())
        ExprStatementPrinter().visit(tree)  # updates the tree in-place
        codeobj = compile(tree, filename, 'exec')
        exec(codeobj, {'__print_expr_result__': print_expr_result})
    

    I used a double-underscore 'system' defined name for the print function to avoid collisions with anything the script might have defined.

    Demo:

    >>> example = '''\
    ... import sys
    ... sys.version_info[0],sys.version_info[1]
    ... 'Hello world!'
    ... None  # should not be printed
    ... '''
    >>> exec(example)  # no output
    >>> tree = ast.parse(example)
    >>> ExprStatementPrinter().visit(tree)
    <_ast.Module object at 0x1024c14d0>
    >>> codeobj = compile(tree, '', 'exec')
    >>> exec(codeobj, {'__print_expr_result__': print_expr_result})
    (2, 7)
    'Hello world!'
    

    Note: this only applies to the top-level file you execute. Any imported modules are still going to be run without transformation (just like they would in the Python interactive interpreter). You'd have to hook into the __import__ global to intercept the normal importing machinery.

    0 讨论(0)
提交回复
热议问题