Как может python subprocess.Popen увидеть select.poll, а потом нет? (выберите объект «модуль» не имеет атрибута «опрос») - PullRequest
9 голосов
/ 01 февраля 2012

Я использую (удивительную) библиотеку mrjob от Yelp для запуска своих программ на python в Amazon Elastic Map Reduce.Это зависит от подпроцесса в стандартной библиотеке Python.На моем Mac с Python2.7.2 все работает как положено

Однако, когда я переключился на использование точно такого же кода на Ubuntu LTS 11.04 также с python2.7.2, я столкнулся с чем-то странным:

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, модуль выбора как-то меняется!Это генерируется печатью dir (select) непосредственно перед попыткой использования select.poll ().

    ['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, с подпроцессом, использующим communication_with_select ... и я застрял рукойМодифицированная стандартная библиотека ...

Есть совет?: -)

Ответы [ 2 ]

3 голосов
/ 11 сентября 2014

У меня была похожая проблема, и оказалось, что gevent заменяет встроенный модуль select на gevent.select.select, у которого нет метода poll (так как это метод блокировки).Однако по какой-то причине по умолчанию gevent не исправляет subprocess, который использует select.poll.

. Простое решение - заменить subprocess на gevent.subprocess:

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

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

ЕслиВы делаете это перед импортом библиотеки mrjob, она должна работать нормально.

1 голос
/ 25 января 2013

Извините за написание полного ответа вместо комментария, в противном случае я бы потерял отступ кода.

Я не могу вам помочь напрямую, поскольку что-то кажется очень строго связанным с вашим кодом, но я могу помочь вам выяснитьполагаясь на тот факт, что модули 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 достаточно хороша, иначе вывозможно, потребуется изменить его.

...