我有一个包含Python语句的文件,我想以这样一种方式运行Python,即打印到stdout,如果这些命令在REPL中运行,将显示什么。
例如,如果文件是
1 + 4
'a' + 'b'
然后输出应为
>>> 1 + 4
5
>>> 'a' + 'b'
'ab'
有办法做到这一点吗?
答案 0 :(得分:4)
(不是那样)快速且(大部分)脏了:
import sys
import code
infile = open('cmd.py')
def readcmd(prompt):
line = infile.readline()
if not line:
sys.exit(0)
print prompt,line.rstrip()
return line.rstrip()
code.interact(readfunc=readcmd)
还有很大的改进空间,但现在已经很晚了。无论如何,例如:
sh$ cat cmd.py
1 + 4
'a' + 'b'
1/0
def f(x):
return x*2
f(3)
sh$ python console.py
Python 2.7.3 (default, Mar 13 2014, 11:03:55)
[GCC 4.7.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> 1 + 4
5
>>> 'a' + 'b'
'ab'
>>>
>>> 1/0
Traceback (most recent call last):
File "<console>", line 1, in <module>
ZeroDivisionError: integer division or modulo by zero
>>>
>>> def f(x):
... return x*2
...
>>> f(3)
6
答案 1 :(得分:4)
您可以使用replwrap
中的pexpect
来实现此目标,即使采用python
方法:
from pexpect import replwrap
with open("commands.txt", "r") as f:
commands = [command.strip() for command in f.readlines()]
repl = replwrap.python()
for command in commands:
print ">>>", command
print repl.run_command(command),
返回:
python replgo.py
>>> 1 + 4
5
>>> 'a' + 'b'
'ab'
您需要获取最新版本的pexpect。
答案 2 :(得分:3)
一些神奇的魔法可以帮到这里:
import ast
import itertools
def main():
with open('test.txt', 'r') as sr:
parsed = ast.parse(sr.read())
sr.seek(0)
globals_ = {}
locals_ = {}
prev_lineno = 0
for node in ast.iter_child_nodes(parsed):
source = '\n'.join(itertools.islice(sr, 0, node.lineno - prev_lineno))[:-1]
print('>>> {}'.format(source))
if isinstance(node, ast.Expr):
print(eval(source, globals_, locals_))
else:
exec(source, globals_, locals_)
prev_lineno = node.lineno
if __name__ == '__main__':
main()
输入:
1 + 4
'a' + 'b'
a = 1
a
输出:
>>> 1 + 4
5
>>> 'a' + 'b'
ab
>>> a = 1
>>> a
1
这样做是通过使用ast
模块解析源代码来查找每个单独语句的起始行和结束行号,然后根据它是否eval
或exec
调用是一种陈述或表达。
上下文保存在globals_
和locals_
。
通过使用一些python沙箱来执行eval
和exec
,您可以使这更安全。
答案 3 :(得分:1)
您可以将输入传输到pythons“code”模块。它将显示输出,但不会显示输入。
$ echo '1 + 1' | python -m code
Python 2.7.10 (v2.7.10:15c95b7d81dc, May 23 2015, 09:33:12)
[GCC 4.2.1 (Apple Inc. build 5666) (dot 3)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> 2
答案 4 :(得分:0)
我们可以通过欺骗 Python 命令启动交互式会话来实现此目的。这可以通过 unbuffer
来完成,它通常与 Linux 发行版中的 expect
工具一起提供。这种方法非常通用,适用于在交互调用时表现不同的各种程序。
以下命令将启动 REPL,即使(当前为空)输入来自管道:
$ printf '' | unbuffer -p python3
Python 3.8.8 (default, Feb 19 2021, 11:04:50)
[GCC 9.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
一些烦人的行为使这项工作与交互式会话略有不同。首先,unbuffer
一遇到EOF就会退出,所以我们需要一个小的延迟来确保Python有足够的时间启动:
$ (sleep 1; printf '') | unbuffer -p python3
Python 3.8.8 (default, Feb 19 2021, 11:04:50)
[GCC 9.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>>
请注意,这次我们设法获得了 >>>
提示。
其次,我们的输入命令不会作为输出的一部分得到回显。例如:
$ (sleep 1; printf 'print("a" * 10)\n'; sleep 1) | unbuffer -p python3
Python 3.8.8 (default, Feb 19 2021, 11:04:50)
[GCC 9.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> aaaaaaaaaa
>>>
请注意,我们的输入 print("a" * 10)\n
不会出现在 >>>
提示之后,即使结果确实被打印出来。
我们可以使用 tee
将我们的命令复制到 stdout 和 REPL(我们使用进程替换执行)来解决这个问题:
$ (sleep 1; printf 'print("a" * 10)\n'; sleep 1) | tee >(unbuffer -p python3)
Python 3.8.8 (default, Feb 19 2021, 11:04:50)
[GCC 9.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> print("a" * 10)
aaaaaaaaaa
>>>
这似乎行为正常,但我们必须在每行之间设置延迟。这是一个自动执行此操作的脚本,从标准输入读取行:
#!/usr/bin/env bash
set -e
cat | (sleep 1; while IFS='' read -r LINE
do
sleep 0.2
echo "$LINE"
done; sleep 1) | tee >(unbuffer -p python3)
这似乎可以完成工作(我正在使用 printf
但这对于单独的文件同样有效;请注意,REPL 需要两个换行符来执行缩进块,就像在交互式会话中一样) :
$ printf 'if True:\n print("hello")\nelse:\n print("world")\n\n12345\n' | ./repl.sh
Python 3.8.8 (default, Feb 19 2021, 11:04:50)
[GCC 9.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> if True:
... print("hello")
... else:
... print("world")
...
hello
>>> 12345
12345
>>>
如果您想去除最终的 head
和启动噪音,例如
tail
和 >>>
$ printf 'if True:\n print("hello")\nelse:\n print("world")\n\n12345\n' | ./repl.sh | tail -n+4 | head -n-1
>>> if True:
... print("hello")
... else:
... print("world")
...
hello
>>> 12345
12345