Если занимаетесь автотестами на python, часто работаете с многопоточностью и хотите уменьшить количество boiler-plate кода – имеет смысл посмотреть на библиотеку easypy.   В очередном тестовом фреймворке на pytest наткнулся на удобный способ работы с многопоточностью, сначала подумал, что это свой велосипед, но оказалось, что внешняя зависимость. Звезд на гитхабе мало, документации тоже почти нет, но мне понравился подход по работе с многопоточностью и еще пара занятных штук. Сама библиотека представляет из себя набор не сильно связанных модулей, предоставляющих удобные обертки над разнообразным boiler-plate кодом. Хорошо подходит под задачи возникающие в процессе написания автотестов. Багов в библиотеке явных найти не удалось - все работает стабильно. Однако, не уверен, что использовать в production коде продукта хорошая идея – monkey-patching используется повсюду, не очень много unit тестов, мало документации, есть вероятность себе что-нибудь отстрелить. Пройдемся по частям библиотеки, начиная с тех, которые используются в автотестах чаще всего.  Самый полезный модуль, из-за него я, собственно, и решил этой библиотекой поделиться, как минимум, подобный подход можно реализовать и у себя. Класс MultiObject позволяет делать вот такое: Вывод будем таким: В качестве аргумента конструктору передается коллекция элементов. С помощью метода .call() указывается какой метод вызывать для каждого из этих элементов. Каждый вызов будет осуществлен в отдельном потоке. С помощью параметра workers можно указать количество потоков. По умолчанию, количество потоков равно количеству элементов. Кроме этого, можно вызывать напрямую методы переданных объектов (объекты должны иметь одинаковый интерфейс): Вывод: Метод send() является методом экземпляра класса Server. Библиотека сама вызывает этот метод для каждого экземпляра. Каждый вызов происходит в отдельном потоке, отрегулировать количество можно с помощью параметра workers.   Для сравнения, код на чистом threading для такой же ситуации будет выглядеть как-то так: Метод concurrent() Следующий полезный метод, можно использовать в качестве контекстного менеджера: Вывод: Можно использовать и напрямую - для запуска задачи в фоне. Если не указать параметры loop и sleep, метод внутри будет вызван один раз: Оба варианта – и метод concurrent и класс MultiObject очень удобно использовать, когда тестируемая система состоит из нескольких узлов и с этими узлами нужно что-то одновременно делать. Можно также использовать для failover тестов, когда во время какого-то тестового сценария с системой что-то идет не так – например, постоянно перезагружается какой-то случайный узел в системе, а мы параллельно с этой системой работаем.  Класс ListCollection - является надстройкой над стандартным list, добавляя несколько удобных методов и поддержку класса MultiObject: choose() позволяет выбрать один элемент из коллекции, указывая либо предикат, либо фильтр (значение какого-то поля объекта). select() выбирает подмножество элементов из коллекции. Также есть ряд методов типа shuffled(), sorted(), filtered(), without() которые отвечают за простые, но удобные, операции над коллекцией. Через параметр M можно создать объект MultiObject и сразу вызвать на всей коллекции метод send(), который выполнится в разных потоках. В результате получается такая конструкция: Опять же - очень удобно при работе с многоузловой системой - узлы можно хранить в ListCollection и выполнять операции в разных потоках. Небольшой модуль с константами для измерения времени и информации. Избавляет от необходимости писать свои, пример -  Поддерживаются математические операции - удобно оперировать. Предлагает несколько декораторов, которые удобно использовать при операциях с нестабильной системой: Позволяет подавить ошибку и вернуть значение из метода по умолчанию, даже в случае ошибки. Через аргументы acceptable и unacceptable можно контролировать на какие ошибки обращать внимание, а на какие - нет. Следующий декоратор - @retrying повторяет вызов метода, можно указать либо количество повторов, либо время, в течение которого эти повторы будут совершены и время между повторами. Плюс также можно указать какие ошибки нужно пропускать, а какие нет. И retrying и resilient хорошо помогают при уже упомянутых failover тестах, когда нужно опрашивать систему в то время, пока ей нехорошо. Куда же без реализации wait'а: wait ждет пока указанный предикат вернет что-то кроме False или None. Если за таймаут какое-либо другое значение не возвращается – падает по таймауту с текстом, указанным в параметре message. Два удобных метода, которые используются для генерации произвольных имен и текста: Установка - pip install real-easypy Для использования нужно чтобы был заимпортирован библиотечный модуль logging – import easypy.logging, иначе будет ругаться ошибками при использовании любого метода из библиотеки.  Еще раз ссылка на библиотеку - https://github.com/real-easypy/easypy