Fabric как новый Bash: проверка ДЗ в OTUS
Чаще всего Fabric вспоминают, когда речь идёт об автоматизации деплоя и работы с удалёнными нодами. Но на самом деле он так же прекрасно справляется и с автоматизацией локальных задач. Он удобнее обычного баша тем, что в нём есть вся гибкость Python, и круче чистого Python множеством готовых админских функций.
Лично я пользуюсь им для локальной автоматизации довольно часто. Один из примеров — проверка домашних заданий в OTUS. С его помощью я ускоряю проверку стилей и статического анализа кода.
Давайте посмотрим, как это выглядит
Прежде всего, у меня создана папка
Работаем мы локально, так что нам нужна функция
import os from fabric.api import local, lcd, task, settings
Поскольку ДЗ студенты сдают в виде git-репозиториев, мне нужна функция, которая будет клонировать их в локальные папки.
def prepare_repo(repo, pull=True): project_path = repo.split(':')[1] if project_path.endswith('.git'): project_path = project_path[:-4] if not os.path.exists(project_path): os.makedirs(project_path) local('git clone {} {}'.format(repo, project_path)) elif pull: with lcd(project_path): local('git pull') return project_path
Часто проекты проходят несколько этапов проверок, функция может проверять, есть ли уже репозиторий, и если есть — вытягивать обновления.
А дальше у меня есть несколько тасков: flake, pylint, mccabe и т.д. Они все примерно одинаковые, так что приведу в пример только одну:
@task @task def flake8(repo, pull=True): project_path = prepare_repo(repo, pull) with lcd(project_path): with settings(warn_only=True): result = local('/path/to/venv/otus/bin/flake8 .') return result.return_code == 0
Получив из функции подготовки репозитория путь до папки, мы переключаемся в неё, используя lcd. Это аналог cd, только для работы локально. После мы включаем режим
Аналогично устроены и остальные проверки. Но на практике я редко запускаю их отдельно. Кроме отдельных проверок, у меня есть одна общая:
@task def all(repo): prepare_repo(repo, True) checks = [] checks.append(flake8(repo, False)) checks.append(pylint(repo, False)) checks.append(mccabe(repo, False)) … if all(checks): print('Perfect!')
Таким образом, чаще всего я запускаю именно одну эту задачу, которая проверяет весь код, выводя основные нарушения, если они есть.
Разумеется, проверка кода на этом не заканчивается. Стилистические замечания лишь малая часть процесса. Но благодаря автоматизации, эта часть делается быстро и оставляет больше времени на остальной анализ.