Python 2.6.4 marshal.load 不接受用 subprocess.Popen 创建的打开文件对象
这真是个不幸的情况。Maya 2011自带的是2.6.4版本的Python,但这段代码在这个版本上无法运行:
pipe = subprocess.Popen( 'p4 -G edit C:/somefile.txt', stdout=subprocess.PIPE).stdout
try:
while 1:
record = marshal.load( pipe )
list.append( record )
except EOFError:
pass
不过,其他的2.5版本和最新的2.6.5版本都能正常工作,偏偏就是Maya自带的这个版本不行!它会报出这个错误:
# Error: TypeError: file <maya console> line 3: marshal.load() arg must be file #
那该怎么办呢?
为了继续生活,我把代码改成了直接输出一个文件,这样marshal.load就可以加载这个实际的文件。虽然这样可以用,但感觉很无奈。
Maya 2011通过一个压缩包来访问Python,所以我做了个小测试,把2.6.5的Python26/Lib压缩成一个zip文件,然后把指向2.6.4 zip的sys.path条目换成指向2.6.5 zip的。虽然我还没深入测试,但看起来是有效的。我也不确定这个方法比之前的好还是不好。
理想情况下(我觉得),我希望在2.6.4版本中能找到某种方法,让它在不混用Python版本或频繁写临时文件的情况下正常工作。有没有什么好主意?
关于marshal.loads()的更新
尝试使用marshal.loads(),虽然没有报错,但仍然不能完全正常工作。我感觉自己在摸索中前行。使用perforce时,如果每个文件操作都单独进行会非常慢,必须一次性处理。原来的代码是这样做的:
files = [a,b,c...n] # Huge list of files
p = subprocess.Popen(cmd, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
(fi, pipe) = (p.stdin, p.stdout)
# Fill the command up with all the files
for file in files:
fi.write(file + '\n')
fi.close()
# Get all the results
try:
while 1:
record = marshal.load( pipe )
listData.append( record )
except EOFError:
pass
我不知道为什么在2.5.x/2.6.5中这样做有效,但确实有效;listData最终变成了所有文件结果的列表。我搞不清楚如何用marshal.loads()来实现这个,只能得到第一个结果。这种方法几乎是直接从perforce上复制过来的,所以我知道这是正确的做法。我很容易想象我只是还不太会使用subprocess和marshal。
更新2:marshal.loads()确实有效!
经过更多测试,pipe.read()提供了所有数据,但里面有一些空字符或者其他我不太理解的东西,所以marshal.loads()只读取了第一个条目。在这种情况下,我可以通过"{"来分割数据并收集信息。
listData = []
results = pipe.read().split("{")
# Skip the first entry since it's empty
for result in results[1:]:
listData.append( marshal.loads( "{" + result) )
感谢Cristian给我指明了方向,希望任何升级到Maya 2011并使用perforce的人都能顺利一些。
3 个回答
最后我选择了这个。
import shutil
import os
import tempfile
def decode_list(outputstr):
tmpfolder = tempfile.mkdtemp()
try:
binname = os.path.join(tmpfolder, "p4_decode.bin")
with open(binname, "wb") as binfile:
binfile.write(outputstr)
with open(binname, "rb") as binfile:
ret = []
while True:
try:
item = marshal.load(binfile)
ret.append(item)
except EOFError:
return ret
finally:
if tmpfolder and os.path.isdir(tmpfolder):
shutil.rmtree(tmpfolder)
这是一个老问题,但我想分享一下我找到的内容,帮助其他遇到这个问题的人。
不幸的是,senyacap的回答在Windows上似乎不太管用,使用marshal.load(tempf)
时会出错,提示临时文件不是一个文件对象。而且用tempf.file
传入也不行。
所以,这里有一个(很糟糕的)解决办法(特别是针对Perforce,因为没有其他方法可以使用marshal
模块),就是这个:
p = subprocess.Popen ( cmd, stdout = subprocess.PIPE )
( stdoutdata, stderrdata ) = p.communicate ( )
unmarshalled = []
while stdoutdata:
record = marshal.loads ( stdoutdata )
unmarshalled.append ( record )
remarshalled = marshal.dumps ( record )
stdoutdata = stdoutdata [ len ( remarshalled ) : ]
我遇到了和你一样的问题,就是用 -G 选项处理 p4 的输出时,反序列化(unmarshalling)出错。marshal.loads(str)
只读取了第一个记录,而 marshal.load(StringIO(str))
则报错说 'marshal.load() 的参数必须是文件'。
我没有按照建议去拆分,而是用了一个临时文件的解决办法:
import subprocess, marshal, tempfile
tempf = tempfile.TemporaryFile()
subprocess.Popen(cmd, stdout=tempf).communicate()
tempf.seek(0)
try:
while 1:
record = marshal.load(tempf)
listData.append( record )
except EOFError:
pass
tempf.close()
需要注意的是,Python 会在你关闭临时文件后自动帮你删除它。