使用重定向的stdin在子进程中引发SystemExit

时间:2013-11-04 03:17:40

标签: python python-3.x

背景:我最初回答了this fine gentleman's question关于使用threading解决方案在中间程序中打开非阻塞交互式解释器的问题。他指出它有效,但它对他表现不佳(可以理解,因为threading)。在惶恐不安的情况下,面对高CPU利用率,我转向multiprocessing以获得更高性能的解决方案。

multiprocessing用于此类事情的基本问题是子进程不共享主进程'STDIN - 我可以解决这个问题,但是......

问题:虽然我确实找到了解决方案(请参阅主题),但我的解决方案存在一个持久性问题:通过调用code.interact()退出exit()会话(即提升SystemExit)给出了这个追溯:

Traceback (most recent call last):
  File "/usr/lib/python3.2/multiprocessing/process.py", line 267, in _bootstrap
    self.run()
  File "/usr/lib/python3.2/multiprocessing/process.py", line 116, in run
    self._target(*self._args, **self._kwargs)
  File "./test2.py", line 6, in interp
    code.interact(local=locs)
  File "/usr/lib/python3.2/code.py", line 287, in interact
    console.interact(banner)
  File "/usr/lib/python3.2/code.py", line 223, in interact
    more = self.push(line)
  File "/usr/lib/python3.2/code.py", line 245, in push
    more = self.runsource(source, self.filename)
  File "/usr/lib/python3.2/code.py", line 74, in runsource
    self.runcode(code)
  File "/usr/lib/python3.2/code.py", line 90, in runcode
    exec(code, self.locals)
  File "<console>", line 1, in <module>
  File "/usr/lib/python3.2/site.py", line 382, in __call__
    raise SystemExit(code)
SystemExit: None

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/lib/python3.2/multiprocessing/process.py", line 278, in _bootstrap
    sys.stderr.write(e.args[0] + '\n')
TypeError: unsupported operand type(s) for +: 'NoneType' and 'str'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "./test2.py", line 12, in <module>
    p.start()
  File "/usr/lib/python3.2/multiprocessing/process.py", line 132, in start
    self._popen = Popen(self)
  File "/usr/lib/python3.2/multiprocessing/forking.py", line 126, in __init__
    code = process_obj._bootstrap()
  File "/usr/lib/python3.2/multiprocessing/process.py", line 286, in _bootstrap
    util.info('process exiting with exitcode %d' % exitcode)
UnboundLocalError: local variable 'exitcode' referenced before assignment

转载:这里是重现的代码:

#!/usr/bin/python3

def interp(locs,handle):
    import code, os, sys
    sys.stdin = os.fdopen(handle)
    code.interact(local=locs)

if __name__ == '__main__':
    from multiprocessing import Process
    import sys
    p=Process(target=interp,args=(locals(),sys.stdin.fileno()))
    p.start()
    import time
    time.sleep(20)

在互动口译员处,您必须输入exit()。正确执行ctrl-D退出。这是踢球者:做raise SystemExit 优雅地退出。 什么?!

进一步调查:此处是process.py中存在问题的块:

   except SystemExit as e:
        if not e.args:
            exitcode = 1
        elif type(e.args[0]) is int:
            exitcode = e.args[0]
        else:
            sys.stderr.write(e.args[0] + '\n') #exception here
            exitcode = 1

在该行之前直接插入调试语句表明e.args是长度为1的元组(None,)。我猜是有道理的。

问题:发生了什么事?! SystemExit生成的exit()版本如何获得(None,)的{​​{1}}元组?这显然不会发生 - 只有e.args表示raise SystemExit

我也愿意接受改进我的代码的方法(或者我对链接线程的回答),但主要是我想知道我是否正在做一些我明确不应该做的事情。在子进程中重定向e.args == ()似乎是无害的,但是......

决议:感谢Peters先生,结果发现这是python 3.2中的一个错误。在3.3中,违规行转为:sys.stdinsys.stderr.write(str(e.args[0]) + '\n')不再导致爆炸。凉。

1 个答案:

答案 0 :(得分:3)

我在这里无法洞察你的真实问题,但可以清除exit()之谜。它是记录在案的行为,来自site模块文档:

quit(code=None) 
exit(code=None) 
    Objects that when printed, print a message like “Use quit() or
    Ctrl-D (i.e. EOF) to exit”, and when called, raise SystemExit with
    the specified exit code

因此exit()exit(None)相同,而None来自quit。但还有别的东西! exitQuitterLib/_sitebuiltins.py中定义的__call__类的实例,并采用此def __call__(self, code=None): # Shells like IDLE catch the SystemExit, but listen when their # stdin wrapper is closed. try: sys.stdin.close() except: pass raise SystemExit(code) 方法:

sys.stdin

我很惊讶他们关闭{{1}}。有关?不知道。评论意味着它是一个技巧,以便像IDLE这样的程序知道用户真的想要退出。

BTW,我正在查看当前(3.4.0a4 +)开发分支的源代码,因此与您正在使用的Python可能存在细微差别。