TIL: how to debug randomly hanging Python applications

Usually, if a Python-based application hangs, you either read logs or grab one of the PBD-based solutions, attaching to the application, and uses the Python console for investigation. The approach is straightforward; for example, you installed pdb-attach, and add a few lines to your application:

import pdb_attach
pdb_attach.listen(50000)

and expect that “magic” will just works:

> python -m pdb_attach <PID> 50000
(Pdb) YOU HAVE PDB SESSION HERE

But sometimes, magic is broken, and my theory is (I didn’t search for proof) that this is due to GIL. So, sometimes, no PDB prompt after you have attached to the application with PDB. In my case, the application hang in the multiprocessing.Process call where I used a gRPC server. The gRPC server didn’t react to the termination request, the process cannot stop, and like aggravating circumstances, all these are a part of PyTest that hang 1 of 20 executions.

This is a general PDB-based debuggers issue, which means all other tools like pyrasite-shell and PyTest PBD integration also don’t work. The only option here is GDB for Python, which is surprisingly amazing! First of all, you need to install Python extension for GDB.

sudo apt-get install python3.9-dbg

Then you can connect to your Python application which is a regular Python process with GDB, and explore the call-stack!

> gdb

(GDB) attach <PID>
(GDB) py-bt

If you use not APT-based Linux, search for proper instruction here.

Coursera, Глубокое обучение

Обычно я довольно скептично отношусь к около-IT онлайн-курсам. То слишком много воды, то слишком мало нового, то слишком медленный прогресс. Выходит проще взять документацию по интересующей тематике, найти подходящие заметки в блогах или книги и разобраться самостоятельно. До того как я наткнулся на специализацию по Глубокому обучению от Эндрю Ына, единственным исключением для меня был разве что курс на той же Coursera по алгоритмам Тима Рафгардена. А вот с курсом Глубоким обучением я сильно увлекся тематикой.

Курс Глубокое обучение прекрасен по большому счету вообще во всем: хорошо структурированная и продуманная теоретическая часть, интересная практическая часть на Python с использованием NumPy и переходом к TensorFlow в конце второй ступени, адекватные домашние задания. До того как начать этот курс я попробовал начать курс на fast.ai, но был довольно сильно разочарован сильнейшим перекосом в сторону практики, где всё демонстрируется на основе собственной надстройки толи над TensorFlow, толи над PyTourch. В принципе название курса на fast.ai – “Практическое машинное обучение для кодера” верное, делай что сказали без понимания базы и будет тебе счастье. Ну, возможно, кого-то такой подход и устраивает, я через пару недель сдался и пошел искать нечто более глубокое, так как имея ответ на вопрос “почему”, дойти до “как” в разы проще.

Если говорить про уровень начальной подготовки для специализации Глубокое обучение, то нужно помнить кое-какие моменты из старших классов школы, такие как производные, умножение матриц и простейшие элементы из математической нотации (такие как ∑, ℝ). Кроме того, нужно иметь хотя бы поверхностное представление о Python и Jupiter (бывший IPython). В зависимости от начальной теоретической базы, в неделю на курс будет уходить где-то от 4 и до 8 часов если исходить из стандартного темпа прохождения курса.

Все домашние задания в рамках курса выполняются в “тетрадях” Jupiter на серверах Coursera. Хотя если захочется копнуть поглубже и развить тему самостоятельно, то возникает необходимость либо в аренде мощностей в облаке (AWS, PaperSpace, и т.п.) либо сборке собственного компьютера под свои задачи, так как требуется довольно производительно GPU с поддержкой CUDA. После многочисленных таймаутов и отвалившихся сессий у облачных провайдеров я просто собрал себе подходящий компьютер дома. Но, еще раз подчеркну, это НЕ нужно если идти исключительно в рамках курса.

На данный момент я прошел 2 курса из 5 в рамках специализации и надо признать, понимание того “что такое DL” появилось и начинает обретать какие-то форму. Возможно, я изменю свое мнение о специализации когда дойду до конца, но пока что всё просто великолепно

Устанавливаем OpenCV 3 для Python 3 на macOS

Конфигурация OpenCV 3 + Python 3 на macOS внезапно оказалась немного неожиданной в настройке. Изначально я ожидал что надо будет пару раз вызвать Brew, но оказалось несколько сложнее. Последовательность:

Смотрим где находятся site-packages для интерпретатора, которым собираемся пользоваться, понадобится ниже для создания симлинки на OpenCV:

>>> import site; site.getsitepackages()
['/usr/local/Cellar/python3/3.6.0/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages', '/Library/Python/3.6/site-packages']
>>>

Ставим, собираем, делаем линку (если нет TBB и/или Qt5, ий стоит либо поставить заранее, либо убрать соответствующие флаги):

brew install numpy --with-python3
brew install opencv3 --with-tbb --with-qt5 --with-python3 --with-examples --with-contrib --c++11
sudo mkdir -p /Library/Python/3.6/site-packages
sudo ln -s /usr/local/opt/opencv3/lib/python3.6/site-packages/cv2.cpython-36m-darwin.so /Library/Python/3.6/site-packages/cv2.so

Почему нужно отдельно создавать линку да и еще с таким загадочным именем как cv2 я так и не разгадал. Проверить что все работает довольно просто:

>>> import cv2; cv2.__version__
'3.2.0'
>>>

Генератор CMakeLists.txt файлов

Довольно часто возникает необходимость быстренько написать тестовое приложение на C++ и опробовать в нем что-то. IDE я не слишком люблю, а каждый раз где-то выискивать завалявшийся шаблон к CMake-у довольно лениво. После очередных поисков запилил небольшой вспомогательный скриптик (само собой на Python) для генерации CMakeLists.txt.

На данный момент поддерживается только генерация приложений, как надоест конвертировать приложения в библиотеки, так будут и они генериться

Сам скриптик с руководством по использованию тут: https://github.com/astavonin/gen-cmake

На что бы заменить Python?

На прошлой неделе интегрировал наши замечательные автотесты не только нам, но и команде тестирования и задумался. А правильно ли я изначально сделал выбрав Python в качестве языка для реализации системы? Мне видится ряд плюсов и минусов, ну и некоторые дополнительные мысли созрели.

Плюсы

В качестве главного плюса этого выбора выступает простота самого языка и, как следствие, требованию к уровню разработчика необходимого для вхождения в проект очень низкие.
Динамическая природа языка способствует экспериментам в REPL и облегчает поиск оптимального решения.
Язык очень емкий, что сильно сокращает объем код и позволяет в пару строк написать очень не тривиальные для многих других языков вещи.
Огромное количество библиотек на все случаи жизни позволяет сосредоточиться на задаче, а не инструменте.
Ряд отличных IDE, например PyCharm, сильно облегчают разработку. Continue reading

Крик души о Python собеседованиях

Прибиваю в несколько офигевшем состоянии после проведенной пары мини-собеседований с SDET-ами на тему знания ими языка Python. Вообще, для меня Python, можно сказать что не родной язык, т.е. никаких вопросов с подковыркой я задать не могу хотя бы потому, что их просто не знаю. Поэтому, спрашивал то, с чем придется столкнуться при написании тестов для нашего нового тестового бота (который я как раз допилил). Список вопросов на который я пытался получить ответ:

  1. Расскажите про особенности многопоточности в Python. Зачем нужен GIL, его плюсы и минусы. (0 ответов)
  2. Как работает конструкция with open(…). Зачем она вообще нужна? (0 ответов)
  3. Что нужно добавить в класс, что бы при передаче объекта соответствующего типа в качестве аргумента функции print была выведена не информация о типе и адресе, а некая пользовательская строка. (1 ответ)
  4. Как в Python описываются абстрактные базовые классы? Зачем они нужны? (0 ответов)
  5. В чем основные особенности написания асинхронных приложений? (0 ответов)

Вот я думаю, может я что-то не то спрашивал? Просто мне не приходят в голову еще более простые вопросы.

Как бороться с динамической типизацией?

Я довольно часто писал на Python какие-то вспомогательные вещи, иногда сравнительно крупные, но почти всегда не знание типа объекта с которым работаешь было не критичным. Плюс возможность разрабатывать в IPython сильно облегчала жизнь. И так было до тех пор, пока я не решил плотно использовать Twisted. И оказалось что в IPython не попишешь нормально, а занание типов параметров в функциях обратного вызова и классов из обширного становится необходимостью.

И вот тут то я оказался в неком тупичке. Есть большое количество разнообразных классов со сложными интерфейсами. Перепробованные IDE (Eclipse, PyCharm и даже Emacs) не позволяют воспользоваться автодополнением в незнакомых им классах, что логично. В результате, весь код начинает выглядеть как пример ниже.

def _call_later(self, request):
"""
тут какое-то описание функции
@param request: тут какое-то описание параметра
@type request: Request (1)
"""

Да, безусловно, указывать 1 для каждой из создаваемых функций типы передаваемых параметров это решение, только оно выглядит как откровенный костыль. В итоге, у меня создается ощущение, что я как-то не верно использую этот замечательный язык… Что не так? Как с этим жить? Наиболее подходящим вариантом выглядит то, что все помнят параметры у API наизусть, я же вполне могу писать на C++ с подстановкой параметров исключительно из открытых файлов.

Разбитые надежды или просто непонимание?

В одной из лекций с PyCon US 2014 проскочила очень заинтересовавшая меня информация о том, что с Python 3.3 CPython поддерживает оптимизацию для классов, и старый вариант использования Python, когда класс могли просто заменить на Dict не верен в корне, т.к. Dict не поддерживает никакой типизации. Вроде все верно и логично: никак не ограничиваемый по данным ассоциативный массив против класса, в котором можно предсказать используемые типы и количество полей. Continue reading

Python 3.4, asyncio

Думаю, ни для кого не секрет то, что основная реализация языка программирования Python фактически не поддерживает многопоточности. Есть модули которые позволяют эмулировать потоки посредствам процессов, но подобный путь крайне требователен к ресурсам и поэтому его применимость крайне ограниченна, особенно для большого количества операций ввода/вывода. При этом, в подавляющем большинстве случаев, распараллеливание задач не несет какого-то серьезного практического смысла и просто является одним из возможных архитектурных решений. В качестве альтернативы потокам могут выступать асинхронные операции, а с учетом ограничений интерпретатора, подобный подход должен бы был быть родным подходом в Python уже много лет как. Тем не менее, появился долгожданный модуль asyncio только в Python 3.4, но это в любом случае лучше чем никогда.
Continue reading

Оптимизация кода Python

Решил прояснить для себя возможности оптимизации приложений написанных на Python. В интернете существует довольно много рекомендаций на этот счет, так что я просто пытаюсь свести всю информацию вместе и выяснить чем вызваны те или иные отличия.

Хотя, на первый взгляд кажется, что Python и быстрый код не совместимые понятия, это не совсем правда.

Все тесты проводились на Python 3.3.3 и, само собой, не обошлось без IPython, который ну просто killer-feature этого языка. Continue reading