Python 2.6.4 marshal.load 不接受用 subprocess.Popen 创建的打开文件对象

2 投票
3 回答
1817 浏览
提问于 2025-04-16 01:18

这真是个不幸的情况。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 个回答

1

最后我选择了这个。

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

这是一个老问题,但我想分享一下我找到的内容,帮助其他遇到这个问题的人。

不幸的是,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 ) : ]
2

我遇到了和你一样的问题,就是用 -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 会在你关闭临时文件后自动帮你删除它。

撰写回答