Написать программу для проверки наличия комментария в заголовке файлов с расширением c js и py
Поскольку все мы знаем, что файлы с расширением .h называются заголовочными файлами в C. Эти заголовочные файлы, как правило, содержат объявления функций, которые мы можем использовать в нашей основной программе на C, например, например, для этого необходимо включить stdio.h в нашу программу на C использовать функцию printf () в программе. Возникает вопрос: можно ли создать собственный заголовочный файл?
Ответ на вышесказанное — да . Заголовочные файлы — это просто файлы, в которых вы можете объявить свои собственные функции, которые вы можете использовать в своей основной программе, или их можно использовать при написании больших программ на Си.
ПРИМЕЧАНИЕ. Обычно заголовочные файлы содержат определения типов данных, прототипов функций и команд препроцессора языка Си.
Ниже приведен краткий пример создания собственного заголовочного файла и его использования соответствующим образом.
-
Создание myhead.h: напишите приведенный ниже код, а затем сохраните файл как myhead.h, или вы можете дать любое имя, но расширение должно быть .h, указывая его заголовочный файл.
// Не рекомендуется помещать определения функций
// в заголовочном файле. В идеале должно быть только
// объявления функций. Назначение этого кода
// только для демонстрации работы заголовочных файлов.
void add( int a, int b)
printf ( "Added value=%d\n" , a + b);
void multiply( int a, int b)
printf ( "Multiplied value=%d\n" , a * b);
/ * Это вызывает функцию add, написанную в myhead.h
и, следовательно, нет ошибки компиляции. * /
// То же самое для функции умножения в myhead.h
printf ( "BYE!See you Soon" );
ПРИМЕЧАНИЕ. Приведенный выше код успешно компилируется и печатает приведенный выше вывод только в том случае, если вы создали файл заголовка и сохранили его в той же папке, в которой был сохранен указанный выше файл c.
Важные моменты:
Создание заголовочных файлов обычно требуется при написании больших программ на C, чтобы модули могли совместно использовать определения функций, прототипы и т. Д.
- Объявления функций и типов, глобальные переменные, структурные объявления и, в некоторых случаях, встроенные функции; определения, которые должны быть централизованы в одном файле.
- В заголовочном файле не используйте избыточные или другие заголовочные файлы; только минимальный набор утверждений.
- Не помещайте определения функций в заголовок. Поместите эти вещи в отдельный файл .c.
- Включите объявления для функций и переменных, определения которых будут видны компоновщику. Кроме того, определения структур данных и перечислений, которые совместно используются несколькими исходными файлами.
- Короче говоря, ставьте только то, что необходимо, и сохраняйте заголовок файла сжатым.
Эта статья просто для того, чтобы дать вам представление о создании заголовочных файлов и их использовании, но на самом деле это не то, что происходит, когда вы пишете большую программу на Си. Создание заголовочных файлов обычно требуется при написании больших программ на C, чтобы модули могли совместно использовать определения функций, прототипы и т. Д.
Пожалуйста, пишите комментарии, если вы обнаружите что-то неправильное, или вы хотите поделиться дополнительной информацией по обсуждаемой выше теме.
Мнение о том, что JavaScript не умеет взаимодействовать с файловой системой, является не совсем верным. Скорее, речь идет о том, что это взаимодействие существенно ограничено по сравнению с серверными языками программирования, такими как Node.js или PHP. Тем не менее, JavaScript умеет как получать (принимать), так и создавать некоторые типы файлов и успешно обрабатывать их нативными средствами.
В этой статье мы создадим три небольших проекта:
- Реализуем получение и обработку изображений, аудио, видео и текста в формате txt и pdf
- Создадим генератор JSON-файлов
- Напишем две программы: одна будет формировать вопросы (в формате JSON), а другая использовать их для создания теста
Получаем и обрабатываем файлы
Для начала создадим директорию, в которой будут храниться наши проекты. Назовем ее «Work-With-Files-in-JavaScript» или как Вам будет угодно.
В этой директории создадим папку для первого проекта. Назовем ее «File-Reader».
Создаем в ней файл «index.html» следующего содержания:
Здесь мы имеем контейнер-файлоприемник и инпут с типом «file» (для получения файла; мы будем работать с одиночными файлами; для получения нескольких файлов инпуту следует добавить атрибут «multiple»), который будет спрятан под контейнером.
Стили можно подключить отдельным файлом или в теге «style» внутри head:
Можете сделать дизайн по своему вкусу.
Не забываем подключить скрипт либо в head с атрибутом «defer» (нам нужно дождаться отрисовки (рендеринга) DOM; можно, конечно, сделать это в скрипте через обработку события «load» или «DOMContentLoaded» объекта «window», но defer намного короче), либо перед закрывающим тегом «body» (тогда не нужен ни атрибут, ни обработчик). Лично я предпочитаю первый вариант.
Откроем index.html в браузере:
Прежде чем переходить к написанию скрипта, следует подготовить файлы для приложения: нам потребуется изображение, аудио, видео, текст в формате txt, pdf и любом другом, например, doc. Можете использовать мою коллекцию или собрать свою.
Нам часто придется обращаться к объектам «document» и «document.body», а также несколько раз выводить результаты в консоль, поэтому предлагаю обернуть наш код в такое IIFE (это не обязательно):
Первым делом объявляем переменные для файлоприемника, инпута и файла (последний не инициализируем, поскольку его значение зависит от способа передачи — через клик по инпуту или бросание (drop) в файлоприемник):
Отключаем обработку событий «dragover» и «drop» браузером:
Для того, чтобы понять, зачем мы это сделали, попробуйте перенести изображение или другой файл в браузер и посмотрите, что произойдет. А происходит автоматическая обработка файлов, т.е. то, что мы собираемся реализовать самостоятельно в познавательных целях.
Обрабатываем бросание файла в файлоприемник:
Мы только что реализовали простейший механизм «dran'n'drop».
Обрабатываем клик по файлоприемнику (делегируем клик инпуту):
Приступаем к обработке файла:
Удаляем файлоприемник и инпут:
Способ обработки файла зависит от его типа:
Мы не будем работать с html, css и js-файлами, поэтому запрещаем их обработку:
Мы также не будем работать с MS-файлами (имеющими MIME-тип «application/msword», «application/vnd.ms-excel» и т.д.), поскольку их невозможно обработать нативными средствами. Все способы обработки таких файлов, предлагаемые на StackOverflow и других ресурсах, сводятся либо к конвертации в другие форматы с помощью различных библиотек, либо к использованию viewer'ов от Google и Microsoft, которые не хотят работать с файловой системой и localhost. Вместе с тем, тип pdf-файлов также начинается с «application», поэтому такие файлы мы будем обрабатывать отдельно:
Для остальных файлов получаем их «групповой» тип:
Посредством switch..case определяем конкретную функцию обработки файла:
Недавно заглянув на КиноПоиск, я обнаружила, что за долгие годы успела оставить более 1000 оценок и подумала, что было бы интересно поисследовать эти данные подробнее: менялись ли мои вкусы в кино с течением времени? есть ли годовая/недельная сезонность в активности? коррелируют ли мои оценки с рейтингом КиноПоиска, IMDb или кинокритиков?
Но прежде чем анализировать и строить красивые графики, нужно получить данные. К сожалению, многие сервисы (и КиноПоиск не исключение) не имеют публичного API, так что, приходится засучить рукава и парсить html-страницы. Именно о том, как скачать и распарсить web-cайт, я и хочу рассказать в этой статье.
В первую очередь статья предназначена для тех, кто всегда хотел разобраться с Web Scrapping, но не доходили руки или не знал с чего начать.
Off-topic: к слову, Новый Кинопоиск под капотом использует запросы, которые возвращают данные об оценках в виде JSON, так что, задача могла быть решена и другим путем.
Задача
- Этап 1: выгрузить и сохранить html-страницы
- Этап 2: распарсить html в удобный для дальнейшего анализа формат (csv, json, pandas dataframe etc.)
Инструменты
Регулярные выражения, конечно, нам пригодятся, но использовать только их, на мой взгляд, слишком хардкорный путь, и они немного не для этого. Были придуманы более удобные инструменты для разбора html, так что перейдем к ним. , lxml
Это две наиболее популярные библиотеки для парсинга html и выбор одной из них, скорее, обусловлен личными предпочтениями. Более того, эти библиотеки тесно переплелись: BeautifulSoup стал использовать lxml в качестве внутреннего парсера для ускорения, а в lxml был добавлен модуль soupparser. Подробнее про плюсы и минусы этих библиотек можно почитать в обсуждении. Для сравнения подходов я буду парсить данные с помощью BeautifulSoup и используя XPath селекторы в модуле lxml.html.
Это уже не просто библиотека, а целый open-source framework для получения данных с веб-страниц. В нем есть множество полезных функций: асинхронные запросы, возможность использовать XPath и CSS селекторы для обработки данных, удобная работа с кодировками и многое другое (подробнее можно почитать тут). Если бы моя задача была не разовой выгрузкой, а production процессом, то я бы выбрала его. В текущей постановке это overkill.
Загрузка данных
Первая попытка
Приступим к выгрузке данных. Для начала, попробуем просто получить страницу по url и сохранить в локальный файл.
Открываем полученный файл и видим, что все не так просто: сайт распознал в нас робота и не спешит показывать данные.
Разберемся, как работает браузер
Однако, у браузера отлично получается получать информацию с сайта. Посмотрим, как именно он отправляет запрос. Для этого воспользуемся панелью "Сеть" в "Инструментах разработчика" в браузере (я использую для этого Firebug), обычно нужный нам запрос — самый продолжительный.
Как мы видим, браузер также передает в headers UserAgent, cookie и еще ряд параметров. Для начала попробуем просто передать в header корректный UserAgent.
На этот раз все получилось, теперь нам отдаются нужные данные. Стоит отметить, что иногда сайт также проверяет корректность cookie, в таком случае помогут sessions в библиотеке Requests.
Скачаем все оценки
Теперь мы умеем сохранять одну страницу с оценками. Но обычно у пользователя достаточно много оценок и нужно проитерироваться по всем страницам. Интересующий нас номер страницы легко передать непосредственно в url. Остается только вопрос: "Как понять сколько всего страниц с оценками?" Я решила эту проблему следующим образом: если указать слишком большой номер страницы, то нам вернется вот такая страница без таблицы с фильмами. Таким образом мы можем итерироваться по страницам до тех, пор пока находится блок с оценками фильмов ( ).
Парсинг
Немного про XPath
XPath — это язык запросов к xml и xhtml документов. Мы будем использовать XPath селекторы при работе с библиотекой lxml (документация). Рассмотрим небольшой пример работы с XPath
Подробнее про синтаксис XPath также можно почитать на W3Schools.
Вернемся к нашей задаче
Теперь перейдем непосредственно к получению данных из html. Проще всего понять как устроена html-страница используя функцию "Инспектировать элемент" в браузере. В данном случае все довольно просто: вся таблица с оценками заключена в теге . Выделим эту ноду:
Каждый фильм представлен как или . Рассмотрим, как вытащить русское название фильма и ссылку на страницу фильма (также узнаем, как получить текст и значение атрибута).
Еще небольшой хинт для debug'a: для того, чтобы посмотреть, что внутри выбранной ноды в BeautifulSoup можно просто распечатать ее, а в lxml воспользоваться функцией tostring() модуля etree.
Резюме
В результате, мы научились парсить web-сайты, познакомились с библиотеками Requests, BeautifulSoup и lxml, а также получили пригодные для дальнейшего анализа данные о просмотренных фильмах на КиноПоиске.
Полный код проекта можно найти на github'e.
Далеко не каждая строка моего кода получается идеальной с первого же раза. Ну, в некоторых случаях… Иногда… Ладно – практически никогда. Правда заключается в том, что я трачу значительно больше времени на исправление своих собственных глупых ошибок, чем мне хотелось бы. Именно поэтому я использую статические анализаторы практически в каждом написанном мной файле JavaScript.
Статические анализаторы просматривают код и находят в нем ошибки, прежде чем вы его запустите. Они выполняют простые проверки, например, проверку синтаксиса принудительного исполнения (например, наличия табуляции вместо пробелов) и более глобальные проверки, такие как проверка того, чтобы функции не были слишком сложными. Статические анализаторы также ищут ошибки, которые невозможно найти в процессе тестирования, например, == вместо ===.
В больших проектах и при работе в больших командах вам не помешает небольшая помощь в поиске таких «простых» багов, которые на самом деле оказываются не такими простыми, как кажутся.
JSLint, JSHint и Closure Compiler
Есть три основных варианта статических анализаторов для JavaScript: JSLint, JSHint и Closure Compiler.
JSLint был первым статическим анализатором для JavaScript. Его можно запустить на официальном сайте или использовать одну из надстроек, которые можно запускать в локальных файлах. JSLint находит много важных ошибок, но он очень жесткий. Вот яркий пример:
JSLint показывает в этом коде две ошибки:
Первая проблема – это определение переменной i в условиях цикла. JSLint также не принимает оператор ++ в конце определения цикла. Он хочет, чтобы код выглядел следующим образом:
Я ценю создателей JSLint, но как по мне – это перебор. Он оказался жестким и для Антона Ковалева, поэтому он создал JSHint.
JSHint работает так же, как и JSLint, но он написан в дополнение к Node.js, а потому он более гибкий. JSHint включает большое количество опций, что позволяет выполнять пользовательские проверки путем написания своего собственного генератора отчетов.
Запустить JSHint можно с сайта, но в большинстве случаев лучше установить JSHint в качестве локального инструмента командной строки с помощью Node.js. Как только вы установите JSHint, его можно запустить в ваших файлах с помощью такой команды:
JSHint также включает плагины для популярных текстовых редакторов, поэтому его можно запускать в процессе написания кода.
CLOSURE COMPILER
Closure Compiler от Google – это совсем другая разновидность программы. Как предполагает его название, он представляет собой не только программу для проверки, но и компилятор. Он написан на Java и основан на анализаторе Rhino от Mozilla. Closure Compiler включает простой режим для выполнения базовой проверки кода, и более сложные режимы, позволяющие выполнять дополнительную проверку и обеспечивать соблюдение определений отдельных видов.
Closure Compiler сообщает об ошибках в коде JavaScript, но также создает минимизированные версии JavaScript. Компилятор удаляет пустое пространство, комментарии и неиспользуемые переменные и упрощает длинные выражения, делая скрипт максимально компактным.
Google сделал очень простую версию компилятора, доступную в сети, но скорее всего, вы захотите скачать Closure Compiler и запустить его локально.
Closure Compiler после проверки кода выводит список файлов в один минимизированный файл. Таким образом, вы можете запустить его, скачав файл compiler.jar.
Выбираем правильную программу проверки
В своих проектах я комбинирую Closure Compiler и JSHint. Closure Compiler выполняет минимизацию и базовую проверку, в то время как JSHint проводит более сложный анализ кода. Эти две программы отлично работают вместе, и каждая из них охватывает те области, которые не может охватить другая. Кроме того, я могу использовать возможности расширения JSHint, чтобы писать пользовательские программы проверки. Одна написанная мной общая программа проверяет определенные функции, которые мне не нужны, например вызов функций, которых не должно быть в моем проекте.
Теперь, когда мы рассмотрели несколько программ для проверки, давайте разберем немного плохого кода. Каждый из этих шести примеров представляет собой код, который не стоит писать, и ситуации, в которых программы проверки кода могут спасти вас.
В этой статье для большинства примеров используется JSHint, но Closure Compiler обычно выдает похожие предупреждения.
JavaScript – это язык с динамической типизацией. Вам не нужно определять типы в процессе написания кода, при этом они существуют при запуске.
JavaScript предлагает два оператора сравнения для управления такими динамическими типами: == и ===. Давайте рассмотрим это на примере.
Оператор сравнения == — это остатки языка С, в который JavaScript уходит корнями. Его использование практически всегда является ошибкой: сравнивание значений отдельно от типов редко является тем, что разработчик на самом деле хочет сделать. На самом деле, число «сто двадцать три» отличается от строки «один два три». Эти операторы легко неправильно написать и еще легче неправильно прочесть. Проверьте этот код с помощью JSHint и вы получите следующее:
Неопределенные переменные и поздние определения
Давайте начнем с простого кода:
Видите баг? Я совершаю эту ошибку каждый раз. Запустите этот код, и вы получите ошибку:
Давайте сделаем проблему немного более сложной:
Запустите этот код, и вы получите следующее:
Этот второй пример работает, но у него есть очень неожиданные побочные действия. Правила для определения переменных JavaScript и области видимости оказываются, в лучшем случае, запутанными. В первом случае JSHint сообщит следующее:
Во втором случае он сообщит такое:
Первый пример поможет вам избежать ошибки времени выполнения программы. Вам не нужно тестировать свое приложение — JSHint найдет ошибку за вас. Второй пример хуже, так как в результате тестирования вы не найдете баг.
Проблема второго примера коварно незаметная и сложная. Переменная myVar теперь исчезла из своей области видимости и поднялась в глобальную область. Это означает, что она будет существовать и иметь значение Hello, World даже после запуска функции test. Это называется «загрязнение глобальной области видимости».
Переменная myVar будет существовать для каждой другой функции, которая будет запущена после функции test. Запустите следующий код после того, как выполните функцию test:
Вы все равно получите Hello, World. Переменная myVar будет висеть по всему вашему коду как шаблон, который приводит к сложным багам, которые вы будете искать всю ночь перед релизом, а все потому, что вы забыли вписать var.
Думаю, нет нужды рассказывать хабрапользователю что такое Git / GitHub, pre-commit и как наносить ему hook справа. Перейдем сразу к делу.
В сети много примеров хуков, большинство из них на shell'ах, но ни один автор не уделил внимание одному важному моменту — хук приходится таскать из проекта в проект. На первый взгляд — ничего страшного. Но вдруг появляется необходимость внести изменения в хук, который уже живет в 20 проектах… Или внезапно нужно переносить разработку с Windows на Linux, а хук на PowerShell'е… Что делать? . PROFIT…
«Лучше так: 8 пирогов и одна свечка!»
- выполнять проверку отправляемого в репозиторий кода на валидность (например: соответствие требованиям PEP8, наличие документации итд);
- выполнять комплексную проверку проекта (юнит-тесты итд);
- прерывать операцию commit'а в случае обнаружения ошибок и отображать подробный журнал для разбора полетов.
Понятно, что сам хук — всего лишь стартер, а всю особую уличную магию выполняет запускаемый им скрипт. Попробуем написать такой скрипт. Заинтересовавшимся — добро пожаловать под кат.
pre-commit.py
Но прежде, чем скачивать готовый пример из сети приступать к разработке, рассмотрим принимающие им параметры. Заодно на их примере расскажу как все работает.
- -c или --check[скрипт1… скриптN] — запуск скриптов проверки на валидность. Скрипт должен располагаться в том же каталоге, что и pre-commit.py. Иначе — нужно указать полный путь. Каждому скрипту будут «скармливаться» файлы из текущего коммита.
- -t или --test[тест1… тестN] — запуск юнит-тестов и прочих скриптов, которым не требуются файлы текущего коммита. Тест должен располагаться в каталоге текущего проекта. Иначе — нужно указать полный путь.
- -e или --execпуть_к_интерпретатору — полный путь (с именем файла) к интерпретатору, который будет выполнять скрипты из --check и --test. Если параметр не указать — будет использован интерпретатор, которым выполняется pre-commit.py.
- -v или --verbose — включает подробное логирование. Если не указан — в лог записывается консольный вывод тех скриптов, выполнение которых завершилось с кодом ошибки.
- -o или --openlogпуть_к_просмотрщику — полный путь (с именем файла) к программе, которой будем просматривать лог.
- -f или --forcelog — принудительное открытие лога. Если не указан — лог открывается только в случае обнаружения ошибок. Параметр применим, если указан --openlog.
Параметры командной строки
Для начала настроим парсер параметров командной строки. Здесь будем использовать модуль argparse (или «на пальцах» неплохо объясняют здесь и здесь), так как он входит в базовый пакет Python.
Запустим скрипт со следующими параметрами:
И выведем содержимое params на экран:
Теперь значения всех параметров находятся в словаре params и их легко можно получить по одноименному ключу.
Добавим проверку наличия основных параметров:
Все хорошо, но можно немного упростить себе жизнь, без ущерба гибкости. Мы знаем, что в 99% случаев скрипт валидации один и называется он, к примеру, 'pep8.py', а скрипт юнит-тестов в нашей власти каждый раз называть одинаково (и часто он тоже будет один). Аналогично с отображением лога — всегда будем использовать одну и ту же программу (пусть это будет «Блокнот»). Внесем изменения в конфигурацию парсера:
И добавим установку значений по умолчанию:
После внесения изменений код настройки парсера должен выглядеть так:
Теперь строка запуска скрипта стала короче:
содержимое params:
Параметры победили, едем дальше.
Последней строкой кода еще немного упростим себе жизнь — создаем алиас, которым будем пользоваться дальше по коду вместо logging.info.
Shell
- запуск подпроцесса (с помощью Popen);
- считывание данных с консоли подпроцесса и их преобразования;
- запись считанных данных в лог, если подпроцесс завершился с кодом ошибки.
- command — аргумент для Popen. Собственно то, что будет запускать в Shell'е. Но вместо цельной строки («python main.py») рекомендуют задавать списком (['python', 'main.py']);
- force_report — управление выводом в лог. Может принимать значения: True — принудительный вывод в лог, False — вывод, если получен код ошибки, None — запретить вывод в лог.
Head revision
Список файлов текущего commit'а легко получается с помощью консольной команды Git — «diff». В нашем случае потребуются измененные или новые файлы:
В результате targets будет содержать нечто подобное:
Самый мучительный этап завершен — дальше будет проще.
Проверка на валидность
Здесь все просто — пройдемся по всем скриптам, заданным в --check, и запустим каждый со списком targets:
Пример содержимого лога на коде не прошедшем проверку на валидность:
[ SHELL ] C:\python34\python.exe c:\dev\projects\pre-commit-tool\pep8.py C:\dev\projects\example\demo\daemon_example.py (code: 1):
C:\dev\projects\example\demo\daemon_example.py:8:80: E501 line too long (80 > 79 characters)
Запуск тестов
Аналогично поступаем и с юнит-тестами, только без targets:
[UPD] Отображаем лог
В зависимости от глобального кода результата и параметров --openlog и --forcelog, принимаем решение — отображать лог или нет:
Примечание. Работает в версиях Python 2.6 (и выше) и 3.х. На версиях, ниже 2.6 — тесты не проводились
И не забываем в конце скрипта вернуть в оболочку Git код результата:
Все. Скрипт готов к использованию.
Корень зла
Хук — это файл с именем «pre-commit» (без расширения), который нужно создать в каталоге: /.git/hooks/
Конечно, так делать не рекомендуется :) Используйте любой способ, который вам нравится, но один.
Приемочные испытания
Тренироваться будем «на кошках»:
Тестовый commit имеет новые, переименованные\измененные и удаленные файлы. Также, включены файлы, не содержащие код; сам код содержит ошибки оформления и не проходит один из юнит-тестов. Создадим хук с валидацией, тестами и открытием подробного лога:
И пробуем выполнить commit. Подумав пару секунд, Git desktop просигналит об ошибке:
А в соседнем окне блокнот отобразит следующее:
[ SHELL ] git diff --cached --name-only --diff-filter=ACM (code: 0):
.gitattributes1
demo/daemon_example.py
main.py
test.py
test2.py
[ SHELL ] C:\python34\python.exe c:\dev\projects\pre-commit-tool\pep8.py C:\dev\projects\example\demo\daemon_example.py C:\dev\projects\example\main.py C:\dev\projects\example\test.py C:\dev\projects\example\test2.py (code: 1):
C:\dev\projects\example\demo\daemon_example.py:8:80: E501 line too long (80 > 79 characters)
C:\dev\projects\example\demo\daemon_example.py:16:5: E303 too many blank lines (2)
C:\dev\projects\example\demo\daemon_example.py:37:5: E303 too many blank lines (2)
C:\dev\projects\example\demo\daemon_example.py:47:5: E303 too many blank lines (2)
C:\dev\projects\example\main.py:46:80: E501 line too long (90 > 79 characters)
C:\dev\projects\example\main.py:59:80: E501 line too long (100 > 79 characters)
C:\dev\projects\example\main.py:63:80: E501 line too long (115 > 79 characters)
C:\dev\projects\example\main.py:69:80: E501 line too long (105 > 79 characters)
C:\dev\projects\example\main.py:98:80: E501 line too long (99 > 79 characters)
C:\dev\projects\example\main.py:115:80: E501 line too long (109 > 79 characters)
C:\dev\projects\example\main.py:120:80: E501 line too long (102 > 79 characters)
C:\dev\projects\example\main.py:123:80: E501 line too long (100 > 79 characters)
[ SHELL ] C:\python34\python.exe test.py (code: 1):
Test 1 - passed
Test 2 - passed
[!] Test 3 FAILED
[ SHELL ] C:\python34\python.exe test2.py (code: 0):
Test 1 - passed
Test 2 - passed
Повторим этот же commit, только без подробного лога:
[ SHELL ] C:\python34\python.exe c:\dev\projects\pre-commit-tool\pep8.py C:\dev\projects\example\demo\daemon_example.py C:\dev\projects\example\main.py C:\dev\projects\example\test.py C:\dev\projects\example\test2.py (code: 1):
C:\dev\projects\example\demo\daemon_example.py:8:80: E501 line too long (80 > 79 characters)
C:\dev\projects\example\demo\daemon_example.py:16:5: E303 too many blank lines (2)
C:\dev\projects\example\demo\daemon_example.py:37:5: E303 too many blank lines (2)
C:\dev\projects\example\demo\daemon_example.py:47:5: E303 too many blank lines (2)
C:\dev\projects\example\main.py:46:80: E501 line too long (90 > 79 characters)
C:\dev\projects\example\main.py:59:80: E501 line too long (100 > 79 characters)
C:\dev\projects\example\main.py:63:80: E501 line too long (115 > 79 characters)
C:\dev\projects\example\main.py:69:80: E501 line too long (105 > 79 characters)
C:\dev\projects\example\main.py:98:80: E501 line too long (99 > 79 characters)
C:\dev\projects\example\main.py:115:80: E501 line too long (109 > 79 characters)
C:\dev\projects\example\main.py:120:80: E501 line too long (102 > 79 characters)
C:\dev\projects\example\main.py:123:80: E501 line too long (100 > 79 characters)
[ SHELL ] C:\python34\python.exe test.py (code: 1):
Test 1 - passed
Test 2 - passed
[!] Test 3 FAILED
Исправим ошибки, повторим commit, и — вот он, долгожданный результат: Git desktop не ругается, а блокнот показывает пустой pre-commit.log. PROFIT.
[UPD] Вместо заключения
Конечно, данный скрипт — не панацея. Он полезен, когда все необходимые проверки ограничиваются локальным запуском проверочных скриптов. В комплексных проектах обычно применяется концепция Непрерывной интеграции (или CI), и здесь на помощь приходят Travis (для Linux и OS X) и его аналог AppVeyor (для Windows).
[UPD] Еще одна альтернатива — overcommit. Довольно функциональный инструмент для управления хуками Git. Но есть нюансы — для работы overcommit необходимо локально развернуть интерпретатор Ruby.
Читайте также: