Началось всё с того, что я захотел установить Python3 и Eric5 на свою Kubuntu 12.04. Так как в репозиториях «Панголина» присутствует только Eric4, то было решено ставить всё руками из исходников. По сути, в такой установке нет ничего сложного: ставлю Python3, затем SIP, далее PyQt4, потом QScintilla2. На первый взгляд всё прошло гладко. Я спешно перехожу в каталог с дистрибутивом (не побоюсь этого слова) Eric5 и набираю заветные
sudo python3 install.py
Каково же было моё удивление, когда скрипт выдал в консоли
Found PyQt4
Sorry, please install QtHelp.
Error: No module named QtHelp
Забавно, думаю, может глючит установщик? Запускаю там же в консоли интерпретатор третьего питона, ввожу
>>> import PyQt4.QtHelp
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ImportError: No module named QtHelp
Выходит, что QtHelp действительно не установился. Поскольку свободного времени у меня было предостаточно, я, по старой привычке, оставшейся ещё со времён использования Windows, решил просто пересобрать PyQt4. Запустил процесс сборки и пошёл жарить пельмени, в предвкушении вкусного ужина и скорейшего решения проблемы. Пожарил, поел, покурил, вернулся к ноуту, проверил — то же самое, проблема осталась.
Решил обратиться ко всезнающему Гуглу. Всезнающий проблему констатировал, а вот решения так и не выдал. Кто-то её так и не решил, у кого-то её просто нет. Хотя вот тут написано, что баг исправлен. Да уж, таки исправлен, ага.
Тут ничего не остаётся, как взять в руки скальпель и начать глубокое расследование. Первым делом меня интересовал консольный вывод скрипта configure.py, ведь именно он отвечает за генерацию исходников на C++, из которых потом make соберёт бинарные файлы библиотек. Текста этот скрипт выводит довольно много, но мне были важны лишь те части, которые касались проблемного QtHelp:
... These PyQt modules will be built: QtCore, QtGui, QtNetwork, QtDBus,Checking to see if the QtGui module should be built...
Checking to see if the QtHelp module should be built...
Checking to see if the QtMultimedia module should be built...
QtDeclarative, QtOpenGL, QtScript, QtScriptTools, QtSql, QtSvg, QtTest,
QtWebKit, QtXml, QtXmlPatterns, QtDesigner.
Здесь видно, что скрипт проверял возможность сборки модуля QtHelp, но, судя по всему, проверка не увенчалась успехом, т.к. в окончательном списке модулей, готовых к построению, QtHelp отсутствует. И действительно, заглянув в тот каталог, откуда устанавливается PyQt4, можно заметить, что скриптом configure.py для всех Qt-модулей были созданы одноимённые подкаталоги (QtCore, QtQui, QtNetwork и т.д.), содержащие код на C++. Для всех, кроме QtHelp.
Тут надо бы заглянуть внутрь скрипта и посмотреть две вещи. Во-первых, что из себя представляет список «modules will be built». Во-вторых, как туда попадают данные. Открываю configure.py в Kate, ищу заветный текст и вижу вот какой кусок кода:
sipconfig.inform("The Qt mkspecs directory is in %s." % qt_datadir)
sipconfig.inform("These PyQt modules will be built: %s." % ", ".join(pyqt_modules))
sipconfig.inform("The PyQt Python package will be installed in %s." % opts.pyqtmoddir)
Ага, данные хранятся в списке pyqt_modules. Как они туда заносятся? Наверняка, при проверке очередного модуля. Теперь стоит поискать в скрипте текст «Checking to see if the <bla-bla-bla> module should be built...»:
def check_module(mname, incfile, test, extra_include_dirs=None, extra_lib_dirs=None, extra_libs=None):
"""See if a module can be built and, if so, add it to the global list of
modules.
mname is the name of the module.
incfile is the name of the include file needed for the test.
test is a C++ statement being used for the test.
extra_include_dirs is an optional list of extra include directories.
extra_lib_dirs is an optional list of extra library directories.
extra_libs is an optional list of extra libraries.
"""
# Check that the module is enabled if we are not automatically enabling all
# modules.
if len(opts.enabled) > 0 and mname not in opts.enabled:
return
# Check the module's main .sip file exists.
if os.access(os.path.join(src_dir, "sip", mname, mname + "mod.sip"), os.F_OK):
sipconfig.inform("Checking to see if the %s module should be built..." % mname)
if check_api(incfile, test, mname, extra_include_dirs=extra_include_dirs, extra_lib_dirs=extra_lib_dirs, extra_libs=extra_libs):
pyqt_modules.append(mname)
Бинго! Это как раз то, что нужно! Здесь видно, что при удачном вызове check_api(), список pyqt_modules пополнится ещё одним значением — именем проверяемого модуля. Изначально я хотел было расковырять вызов check_api(), и элементарная логика подсказывала, что это верный путь. Однако, уж не знаю что меня дёрнуло, но я решил посмотреть на код, который вызывает процедуру проверки check_module():
def check_modules(self):
...
# Note that the order in which we check is important for the
# consolidated module - a module's dependencies must be checked first.
pyqt_modules.append("QtCore")
check_module("QtGui", "qwidget.h", "new QWidget()")
check_module("QtHelp", "qhelpengine.h", "new QHelpEngine("foo")")
check_module("QtMultimedia", "QAudioDeviceInfo",
"new QAudioDeviceInfo()")
И тут я обратил внимание на то, что QtCore добавляется в pyqt_modules без всяких проверок. Жажда эксперимента взяла своё: а почему бы и QtHelp не добавить просто так? Ведь в случае фейла я ничего не потеряю и продолжу «ковыряния» дальше, а в случае успеха быстрее начну работу с любимой IDE. Сказано — сделано. Комментирую вызов check_module(«QtHelp»), вставляю на его место pyqt_modules.append(«QtHelp»). Запускаю скрипт заново и уже в окне Yakuake вижу заветное
Generating the C++ source for the QtHelp module...
Creating the Makefile for the QtHelp module...
Проверяю наличие каталога QtHelp с исходниками — он есть. Дальше дело за малым: запускаю долгие make и make install, и под чашку горячего кофе жду завершения сборки и установки. Пробую заново установить Eric5 — вуаля! Всё поставилось, IDE работает как часы!
Конечно, это довольно грязный хак. По-хорошему, стоило всё-таки подобрать ключик к двери, а не вырубать эту самую дверь топором. Но аналогичные действия совершал давным-давно ещё Пётр Великий, кто ж нам запретит теперь?
Автор: twr