http://habrahabr.ru/post/248871/
Думаю многим знакома следующая ситуация. В вашем проекте есть различные действия, которые нужно выполнять время от времени. Для каждого действия вы создаёте отдельный скрипт на питоне. Чтобы далеко не лазить, скрипт кладёте в корень проекта. Через некоторое время вся корневая директория проекта замусоривается этими скриптами и вы решаете сложить их в отдельную директорию. Теперь начинаются проблемы. Если указать интерпретатору python путь до скрипта, включающий эту новую директорию, то внутри скрипта не будут работать импорты пакетов, находящися в корне проекта т.к. корня проекта не будет в sys.path. Эту проблему можно решить несколькими способами. Можно изменять sys.path в каждом скрипте, добавляя туда корень проекта. Можно написать утилитку для запуска ваших скриптов, которая будет изменять sys.path перед запуском скрипта или просто будет лежать в корне проекта. Можно ещё что-то придумать. Мне надоело каждый раз изобретать колесо и я создал велосипед
runscript на котором с удовольствием катаюсь.
Установить библиотеку можно с помощью pip:
$ pip install runscript
После установки библиотеки runscript, вы получаете в вашей системе новую консольную команду run с помощью которой можно запускать скрипты. По-умолчанию, команда run ищет скрипты в под-каталоге script текущего каталога.
Давайте рассмотрим простой пример. Создадим каталог script. Создадим пустой файл script/__init__.py, превратив этот каталог в python-пакет. Теперь создадим файл script/preved.py со следующим содержимым:
def main(**kwargs):
print(‘Preved, medved!’)
Скрипт готов. Теперь мы можем его запустить:
$ run preved
Preved, medved!
Ура! Скрипт работает. Вот собственно и всё, что делает библиотека runscript. Я серьёзно :) Команда run запускает функцию main из файла, имя которого вы ей передали в командной строке. Оказалось, что даже такой простой фунционал очень удобен. Я с удивлением заметил, что пользуюсь утилиткой run в каждом своём проекте т.к. везде есть простенькие скрипты, которые нужно запускать.
Со временем утилита run обросла рядом полезных полезностей, о которых я сейчас расскажу.
Получение параметров через командную строку
Чтобы передать вашему скрипту какие-либо параметры через командную строку, вам нужно описать эти параметры в функции setup_arg_parser внутри вашего скрипта. Эта функция получает на вход объект
ArgumentParser, в который вы можете добавить нужные опции. Далее, когда скрипт будет вызван, значения параметров командной строки будут переданы фунции main. Пример скрипта:
def setup_arg_parser(parser):
parser.add_argument(‘-w’, ‘--who’, default=’medved’)
def main(who, **kwargs):
print(‘Preved, {}’.format(who))
Запускаем:
$ run preved
Preved, medved
$ run preved -w anti-medved
Preved, anti-medved
Обратите внимание, как фунция main получила параметры командной строки — в виде обычных именованных параметров. Всегда нужно указывать **kwargs т.к. кроме нужных вам параметров, передаются значения всех глобальных для утитилы run параметров (читайте о них ниже).
Активация Django
Если вы пытались использовать фреймворк Django в ваших консольных скриптах, то знаете, что нужно сделать кое-что, иначе ничего не будет. Кое-что заключается в создании environment переменной DJANGO_SETTINGS_MODULE, cодержащей путь до модуля с настройками. Обычно в python скрипт добавляют следующие строки:
import os
os.environ[‘DJANGO_SETTINGS_MODULE’] = ‘settings’
Начиная с django 1.7 нужно также выполнить
import django
django.setup()
Для того чтобы выполнять автоматически эти действия в скриптах, запускаемых через run, нужно создать в корне проекта файл с именем run.ini, содержащим следующие настройки:
[global]
django_settings_module = settings
django_setup = yes
Профилирование
Добавив ключик --profile при вызове скрипта, получим файл с результатами профилирования работы нашего скрипта, который можно посмотреть в kcachegrind. Результат сохраняется в каталог var/<script_name>.prof.out, так что не забудьте создать этот каталог. Также нужно установить модуль
pyprof2calltree, который нужен, чтобы сохранить результат профилирования в формате
kcachegrind.
$ run preved --profile
Preved, medved
$ ls var/
preved.prof.out
Настройка мест поиска скриптов
По-умолчанию, утилита run ищет скрипт в двух пакетах: grab.script и script. Пакет grab.script добавлен в этот список, потому что во многих проектах парсинга сайтов я запускаю команду crawl из grab.script пакета. Если вам нужно изменить места для поиска скриптов, создайте следующую настройку в run.ini файле:
[global]
search_path = package1.script,foo,bar
Теперь если мы выполним команду `run preved`, то утилита run попытается импортировать модуль preved в следующем порядке:
- package1.script.preved
- foo.preved
- bar.preved
Использование lock-файлов
Иногда бывает нужно запретить одновременную работу нескольких экземпляров скрипта. Например, мы вызываем скрипт каждую минуту с помощью cron и хотим не допустить одновременной работы нескольких копий скрипта, что может произойти, если работа одной из копий затянется больше, чем на минуту. С помощью опции --lock-key мы можем передать имя lock-файла, который будет создан в каталоге var/run. Например, --lock-key foo приведёт к созданию файла var/run/foo.lock.
Другой способ задать имя lock-файла — создание функции get_lock_key внутри вашего скрипта. Результат её работы будет использован утилитой run, для формирования имени lock-файла. Фунция будет полезна на тот, случай, если вы хотите генерировать имя lock-файла в зависимости от параметров, передаваемых скрипту.
import time
def get_lock_key(who, **kwargs):
return 'the-{}-lock'.format(who)
def setup_arg_parser(parser):
parser.add_argument('-w', '--who', default='medved')
def main(who, **kwargs):
print('Preved, {}'.format(who))
time.sleep(1)
Запускаем одновременно две копии скрипта и видим:
$ run preved -w anti-medved & run preved -w anti-medved
[1] 25277
Trying to lock file: var/run/the-anti-medved-lock.lock
Preved, anti-medved
Trying to lock file: var/run/the-anti-medved-lock.lock
File var/run/the-anti-medved-lock.lock is already locked. Terminating.
[1]+ Done run preved -w anti-medved
Я рассказал об основных возможностях библиотеки runscript. Надеюсь, она окажется вам полезной.
В случае вопросов по поводу работы библиотеки можно всегда посмотреть в исходный код, который на данный момент довольно маленький:
github.com/lorien/runscript/blob/master/runscript/cli.py