我正在尝试使用ipyparallel
并行here中的某些代码。简而言之,我可以使函数在apply_sync()
之外正常工作,但我似乎无法让它们在其中工作(我发誓我之前有这个工作,但我找不到一个版本的代码没有破坏)。一个简单的例子:
def test3(fname = '7_1197_.txt'):
import subprocess
command = 'touch data/sentiment/' + fname + '.test'
child = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE)
while True:
out = child.stdout.read(1)
if out == '' and child.poll() != None:
return
test3() #this works, creates a file with the .test extention
results = view.map_sync(test3, speeches) #this doesn't work. No files created.
这是我实际要使用的函数的简短版本。它本身很好用。在apply_sync()
中,它会根据java
调整htop
个进程,但似乎没有从这些进程中获得任何回复。
def test2(fname = '7_1197_.txt'):
import subprocess
settings = ' -mx5g edu.stanford.nlp.sentiment.SentimentPipeline'
inputFile = ' -file data/sentiment/' + fname
command = 'java ' + settings + inputFile
child = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE)
results = []
while True:
out = child.stdout.read(1)
if out == '' and child.poll() != None:
return ''.join(results)
if out != '':
results.extend(out)
test2() #Works fine, produces output
results = view.map_sync(test2, speeches) #Doesn't work: the results are empty strings.
我尝试了一个返回命令变量的版本。发送到Popen
的命令很好,并且在命令行中手动粘贴时可以正常工作。我想也许这只是管道问题,但更改命令以将输出重定向到' > '+fname+'.out'
的文件在apply_sync()
调用内也不起作用(不生成输出文件)。
我应该怎么做才能从系统回调中获得stdout
?
答案 0 :(得分:1)
我看到两个潜在的陷阱。一个用于阻止,一个用于丢失文件。对于丢失的文件,您应确保引擎和本地会话位于同一工作目录中,或确保使用绝对路径。一种在本地和远程同步路径的快速方法:
client[:].apply_sync(os.chdir, os.getcwd())
说:获取本地 cwd,然后在任何地方调用os.chdir
,以便我们共享相同的工作目录。如果您在IPython会话中,快捷方式是:
%px cd {os.getcwd()}
至于阻塞,我的第一个想法是:你是否可以在并行运行时使用Python 3?如果是这样,child.stdout.read
将返回字节而不是 text 。在Python 2中,str is bytes
,所以out == ''
将起作用,但在Python 3中,条件out == ''
将永远不会是真的,因为b'' != u''
,并且您的函数将永远不会返回。 / p>
一些更有用的信息:
stdout.read(N)
将读取最多该字节数,并在输出完成时截断。这很有用,因为read(1)
将循环许多次,即使输出都等待读取。stdout.read()
将只返回一个空的字节串,所以你只需要在返回之前检查,而不是child.poll()
。 (只要您没有在FD上设置NOWAIT,这是正确的,这是一些高级用法)。所以这里有一些函数的实现,具有不同的目标。
第一个似乎是使用Popen.communicate来完成你当前的目标,如果你实际上不想对部分输出做任何事情和/或你在函数中没有任何关系,那么这是最简单的选择。等待输出:
def simple(fname = '7_1197_.txt'):
import subprocess
command = 'echo "{0}" && touch -v data/sentiment/{0}.test'.format(fname)
child = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE)
# if we aren't doing anything with partial outputs,
# child.communicate() does all of our waiting/capturing for us:
out, err = child.communicate()
return out
(包含stderr捕获,使用stderr=subprocess.PIPE
或将stderr合并到带有stderr=subprocess.STDOUT
的stdout中可能也很有用。)
这是另一个例子,将stderr收集到stdout中,然后读取块:
def chunked(fname = '7_1197_.txt'):
import subprocess
command = 'echo "{0}" && touch data/sentiment/{0}.test'.format(fname)
child = subprocess.Popen(command, shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
)
chunks = []
while True:
chunk = child.stdout.read(80) # read roughly one line at a time
if chunk:
chunks.append(chunk)
continue
else:
# read will only return an empty bytestring when output is finished
break
return b''.join(chunks)
请注意,我们可以使用if not chunk
条件来确定输出何时结束,而不是if chunk == ''
,因为空字节串是Falsy。如果我们没有对部分输出做某事,那么实际上没有理由使用它而不是上面更简单的.communicate()
版本。
最后,这是一个可以与IPython一起使用的版本,它不是捕获并返回输出,而是重新显示它,我们可以用来在客户端显示部分输出:
def chunked_redisplayed(fname = '7_1197_.txt'):
import sys, subprocess
command = 'for i in {{1..20}}; do echo "{0}"; sleep 0.25; done'.format(fname)
child = subprocess.Popen(command, shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
)
while True:
chunk = child.stdout.read(80) # read roughly one line at a time
if chunk:
sys.stdout.write(chunk.decode('utf8', 'replace'))
continue
else:
# read will only return an empty bytestring when output is finished
break
在客户端中,如果您使用map_async
代替map_sync
,则可以查看result.stdout
,这是目前为止的stdout-streams 列表,所以你可以查看进展情况:
amr = view.map_async(chunked_redisplayed, speeches)
amr.stdout # list of stdout text, updated in the background as output is produced
amr.wait_interactive() # waits and shows progress
amr.get() # waits for and returns the actual result