首页 文章

python subprocess.Popen怎么能看到select.poll然后呢? (选择'module'对象没有属性'poll')

提问于
浏览
9

我正在使用Yelp中的(真棒)mrjob库在亚马逊的Elastic Map Reduce中运行我的python程序 . 它取决于标准python库中的子进程 . 从我的mac运行python2.7.2,一切都按预期工作

但是,当我在使用python2.7.2的Ubuntu LTS 11.04上使用完全相同的代码时,我遇到了一些奇怪的事情:

mrjob加载作业,然后尝试使用子进程与其子进程通信并生成此错误:

File "/usr/local/lib/python2.7/dist-packages/mrjob-0.3.1-py2.7.egg/mrjob/emr.py", line 1212, in _build_steps
        steps = self._get_steps()
      File "/usr/local/lib/python2.7/dist-packages/mrjob-0.3.1-py2.7.egg/mrjob/runner.py", line 1003, in _get_steps
        stdout, stderr = steps_proc.communicate()
      File "/usr/lib/python2.7/subprocess.py", line 754, in communicate
        return self._communicate(input)
      File "/usr/lib/python2.7/subprocess.py", line 1302, in _communicate
        stdout, stderr = self._communicate_with_poll(input)
      File "/usr/lib/python2.7/subprocess.py", line 1332, in _communicate_with_poll
        poller = select.poll()
    AttributeError: 'module' object has no attribute 'poll'

这似乎是子进程的问题,而不是mrjob .

我挖到/usr/lib/python2.7/subprocess.py并发现在导入期间运行:

if mswindows:
        ... snip ...
    else:
        import select
        _has_poll = hasattr(select, 'poll')

通过编辑,我验证它确实设置了_has_poll == True . 这是正确的;在命令行上轻松验证 .

但是,当执行进展到使用Popen._communicate_with_poll时,选择模块已经改变了!这是通过在尝试使用select.poll()之前打印dir(select)生成的 .

['EPOLLERR', 'EPOLLET', 'EPOLLHUP', 'EPOLLIN', 'EPOLLMSG', 
    'EPOLLONESHOT', 'EPOLLOUT', 'EPOLLPRI', 'EPOLLRDBAND', 
    'EPOLLRDNORM', 'EPOLLWRBAND', 'EPOLLWRNORM', 'PIPE_BUF', 
    'POLLERR', 'POLLHUP', 'POLLIN', 'POLLMSG', 'POLLNVAL', 
    'POLLOUT', 'POLLPRI', 'POLLRDBAND', 'POLLRDNORM',
    'POLLWRBAND', 'POLLWRNORM', '__doc__', '__name__', 
    '__package__', 'error', 'select']

没有名为'poll'的属性!?!?怎么消失了?

所以,我硬编码_has_poll = False,然后mrjob愉快地继续其工作,在AWS EMR中运行我的工作,子进程使用communic_with_select ...而且我坚持使用手工修改的标准库...

任何建议? :-)

2 回答

  • 1

    我有一个类似的问题,事实证明gevent用 gevent.select.select 取代了内置的 select 模块,它没有 poll 方法(因为它是一种阻塞方法) . 但由于某种原因,默认情况下gevent不会修补使用 select.pollsubprocess .

    一个简单的解决方法是用 gevent.subprocess 替换 subprocess

    import gevent.monkey
    gevent.monkey.patch_all(subprocess=True)
    
    import sys
    import gevent.subprocess
    sys.modules['subprocess'] = gevent.subprocess
    

    如果在导入mrjob库之前执行此操作,它应该可以正常工作 .

  • 3

    很抱歉写一个完整的答案而不是评论,否则我会丢失代码缩进 .

    我无法直接帮助你,因为某些内容似乎与你的代码紧密相关,但我可以通过依赖Python模块可以是任意对象的事实来帮助你找到,尝试类似的东西:

    class FakeModule(dict):
        def __init__(self, origmodule):
            self._origmodule = origmodule
        self.__all__ = dir(origmodule)
    
        def __getattr__(self, attr):
        return getattr(self._origmodule, attr)
    
    
        def __delattr__(self, attr):
            if attr == "poll":
                raise RuntimeError, "Trying to delete poll!"
            self._origmodule.__delattr__(attr)
    
    
    def replaceSelect():
        import sys
        import select
        fakeselect = FakeModule(select)
    
        sys.modules["select"] = fakeselect
    
    replaceSelect()
    
    import select
    del select.poll
    

    你将获得如下输出:

    Traceback (most recent call last):
      File "domy.py", line 27, in <module>
        del select.poll
      File "domy.py", line 14, in __delattr__
        raise RuntimeError, "Trying to delete poll!"
    RuntimeError: Trying to delete poll!
    

    通过在代码中调用replaceSelect(),你应该能够获得有人删除poll()的回溯,这样你就可以理解为什么了 .

    我希望我的FakeModule实现足够好,否则你可能需要修改它 .

相关问题