Без шелухи

Автоматизация задач в Python-проекте

by

Когда разрабатываешь библиотеку или приложение, всегда найдутся задачи, которые выполняешь изо дня в день:

JS-разработчикам повезло (ха): у них в package.json есть специальная секция scripts для таких штук:

{
...
"scripts": {
"format": "prettier --write \"src/**/*.ts\"",
"lint": "tslint -p tsconfig.json",
"test": "jest --coverage --config jestconfig.json",
},
...
}

Для Питона ничего подобного не предусмотрено. Можно, конечно, сделать по sh-скрипту на каждую задачу, но это замусоривает каталог проекта, да и хотелось бы все такие задачи держать вместе. А ставить отдельный таск-раннер или использовать встроенный в IDE совсем уж странно.

Хорошая новость: на линуксе и макоси уже есть отличное средство автоматизации задач для любых проектов — мейкфайлы (Makefile).

Makefile для любых задач

Возможно, вы, как и я, думали, что мейкфайлы — странная штука из 70-х годов прошлого века, которая нужна для сборки кода на C. Всё так ツ Но они прекрасно подходят для автоматизации вообще любых задач.

Вот как это может выглядеть в питонячем проекте. Создаём файл с названием Makefile:

coverage:  ## Run tests with coverage
coverage erase
coverage run --include=dadata/* -m pytest -ra
coverage report -m

deps: ## Install dependencies
pip install black coverage flake8 mypy pylint pytest tox

lint: ## Lint and static-check
flake8 dadata
pylint dadata
mypy dadata

push: ## Push code with tags
git push && git push --tags

test: ## Run tests
pytest -ra

И запускаем, например, линтер с тестами:

$ make lint coverage

flake8 dadata
pylint dadata
...
mypy dadata
...
coverage erase
coverage run —include=dadata/* -m pytest -ra
...
coverage report -m
Name Stmts Miss Cover Missing
--------------------------------------------------
dadata/__init__.py 3 0 100%
dadata/client.py 56 0 100%
--------------------------------------------------
TOTAL 59 0 100%

Возможности

Цепочки действий

Задача может включать несколько действий, как lint в примере выше:

lint:
flake8 dadata
pylint dadata
mypy dadata

Каждое действие выполняется в отдельном подпроцессе, так что если нужно выполнить связанную цепочку действий (например, cd и git pull) — объединяем их через &&:

schemas:
cd iuliia/schemas && git pull && cd ../..

Зависимости между задачами

Допустим, задача test должна обязательно сначала выполнять линтинг, а потом уже запускать тесты. Указываем lint как зависимость для test, и готово:

test: lint
pytest -ra

Можно указать несколько зависимостей — через пробел.

Либо задачи могут явно вызывать друг друга:

lint:
flake8 dadata
pylint dadata
mypy dadata

test:
pytest -ra

prepare:
make lint
make test

И ещё много чего

make умеет использовать переменные, подавлять вывод и игнорировать ошибки в отдельных командах. Подробнее об этом читайте в замечательной статье Ивана Немытченко (PDF, Markdown).

Если этого мало — смотрите подробнейшее руководство по автоматизации через мейкфайлы.

В дикой природе

Пара примеров мейкфайлов из моих проектов:

Мейкфайлы отлично подходят для автоматизации рутинных задач вне зависимости от языка, на котором вы пишете. Используйте их!

И подписывайтесь на «Oh My Py»