11

我正在使用来自 Yelp 的(很棒的)mrjob库在 Amazon 的 Elastic Map Reduce 中运行我的 python 程序。它取决于标准 python 库中的子进程。在我运行 python2.7.2 的 mac 上,一切都按预期工作

然而,当我切换到在 Ubuntu LTS 11.04 上也使用与 python2.7.2 完全相同的代码时,我遇到了一些奇怪的事情:

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

      _build_steps 中的文件“/usr/local/lib/python2.7/dist-packages/mrjob-0.3.1-py2.7.egg/mrjob/emr.py”,第 1212 行
        步骤 = self._get_steps()
      文件“/usr/local/lib/python2.7/dist-packages/mrjob-0.3.1-py2.7.egg/mrjob/runner.py”,第 1003 行,在 _get_steps
        标准输出,标准错误 = steps_proc.communicate()
      文件“/usr/lib/python2.7/subprocess.py”,第 754 行,在通信中
        返回self._communicate(输入)
      _communicate 中的文件“/usr/lib/python2.7/subprocess.py”,第 1302 行
        标准输出,标准错误 = self._communicate_with_poll(输入)
      _communicate_with_poll 中的文件“/usr/lib/python2.7/subprocess.py”,第 1332 行
        轮询器 = select.poll()
    AttributeError:“模块”对象没有属性“轮询”

这似乎是 subprocess 而不是 mrjob 的问题。

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

    如果是 mswindows:
        ...剪断...
    别的:
        导入选择
        _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__', '错误', '选择']

没有名为“民意调查”的属性!?!?它是如何消失的?

所以,我硬编码_has_poll=False,然后mrjob愉快地继续它的工作,在AWS EMR中运行我的工作,子进程使用communicate_with_select......我被一个手工修改的标准库困住了......

有什么建议吗?:-)

4

2 回答 2

5

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

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

import gevent.monkey
gevent.monkey.patch_all(subprocess=True)

import sys
import gevent.subprocess
sys.modules['subprocess'] = gevent.subprocess

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

于 2014-09-10T20:24:50.223 回答
2

很抱歉写了完整的答案而不是评论,否则我会失去代码缩进。

我无法直接帮助您,因为某些东西似乎与您的代码密切相关,但我可以帮助您找出,依靠 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 实现足够好,否则你可能需要修改它。

于 2013-01-25T18:25:34.530 回答