Легко настраиваемый python daemon

    В какой-то момент мне потребовалось демонизировать некоторое действие на python. В сети валялась куча примеров подобной деятельности разной степени собранности. Так как в дальнейшем предполагалось использовать код демонизации в дальнейшей деятельности я решил разнести на разные части настройки и собственно демона.
    В итоге файлов получилось три:
    • Родительские классы — с небольшим изменением классы взятые из интернет статей
    • Классы настройки — реакция на сигналы, реакция на команды и набор статических настроек для запуска
    • Скрипт запуска — собирает первые два в собственно демона


    Дальше я попробую описать логику работы всех трех.

    Сразу скажу, что все есть на Гитхабе. Потому как если вы легко читаете питон — читать мой весьма неумелый текст может оказаться гораздо сложнее.

    Собственно в первом файлике описывать толком нечего: Это почти неизменные три класса, взятые из этой статьи. Из изменений там только то, что к самому демону был прикреплен класс обработчик сигналов, и добавление сигналов в список обрабатываемых было вкручено в собственно процедуру демонизации.

    Вторая часть будет чуть интереснее. Там присутствует три класса:
    1) SigFunctionsCon — содержит реакцию на сигналы. При инициализации получает экземпляр демона, чтобы уметь обращаться к его методам. Каждый метод должен соответствовать сигналу, который он обрабатывает названием. Например так:
    def SIGTERM(self):
    		sys.stderr.write("BB!\n")
    		sys.exit(0)
    

    Внутренние методы и поля могут быть какими угодно.

    2)ReactFunctionCon — содержит реакцию на консольные команды. При инициализации так же получает демона. Каждый метод по названию должен соответствовать команде на которую он будет реагировать и может принимать аргументы (то, что собственно идет за командой в командной строке). Например:
    	def stmess(self,message):
    		print message
    		self.__ourdaemon.start()


    3)StatCon — содержит всякие статические настройки демона. На данный момент выглядит так:
    class StatCon:
    	
    	Help = "Autmation has be applied to distribution sistem feeder for a long time, aspecially as related to protection and the restoration of some parts of the feeder."
    	
    	def run(self):
    		while(True):
    			time.sleep(1)
    	
    	PidFile = "/tmp/daemon-naprimer.pid"
    	
    	Inputter = "/dev/null"
    	
    	Outputter = "/dev/null"
    	
    	Errorer = "/home/espresso/lid"

    Соответственно —
    Хелп строка, выводимая при неправильной передаче аргументов в какую-либо функцию (Возможно следует сделать команду хелп по умолчанию, которая выводит это сообщение?).
    Метод run — собственно то, для чего все затевалось — то, что демон делает.
    Адрес pid файла — для хранения процесса и все такое.
    Ввод, вывод, ошибки — логгирование и прочее. По умолчанию отсылается в /dev/null

    Центровой скрипт представляет интерес исключительно кодом. В общем говоря он наследует класс демона, собирает все настройки с предыдущего файла и раскладывает их по демону, и принимает команды.

    Ну и собственно вопросы:
    Что не так, что не очень так?
    Как по вашему следует ли как-то приписывать к этому GPL, или не стоит больгеносить, и все это слишком несерьезно?
    Достаточно ли адекватно я указал предыдущих авторов?

    Заранее спасибо.

    Ссылки:
    Гитхаб
    Изначальный скрипт
    Share post
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More
    Ads

    Comments 10

      +4
      PEP-8?
      — нет, не слышали...

      Даже в пределах одного файла пересекаются несколько стилей именования переменных и методов — очень не удобно читать. Хоть бы стиль причесали раз код вместе соединили.
        0
        Начал приводить в чувство. Действительно нехорошо получилось. Но это буду исправлять. =)
        0
        Статья, которую вы привели — очень клевая, она мне в свое время помогла понять, как это делается.
        А вот то, как вы обернули все это в какой-то странный класс с Help-ом и Errorer-ом — это вообще не круто.
          0
          А почему не круто?
          Идея плохая или реализация? А если идея — то чем плохая?
            0
            И идея, и реализация :) Идея — потому что один класс из статьи это уже вполне самодостаточный и универсальный код, который будет полезен всем. Написание велосипедов сверх этого кода — это уже требование каждого отдельного проекта. Поэтому полезность скрипта уменьшается только для проектов подобного плана. Да, не спорю, если у Вас регулярно возникают подобные задачи — то переиспользование кода это путь в светлое будущее :)

            По реализации. Как написали выше — ну вообще не круто. Зачем мне бегать по трем файлам и искать где настраиваются сигналы, например? А они реально размазаны по всем трем файлам. То что сигналы относятся к демону Вы поняли и сделали соотв. метод в правильном месте, но реализация заполнения массива хендлеров — оторви и выбрось. Вместо всей этой вакханалии с кучей классов, сделайте декоратор, который будет обрабатывать хендлеры и подготавливать все необходимое для последующей настройки. Например:
            from daemon import Daemon, signal
            from signal import SIGTERM
            
            class MyDaemon(Daemon):
            
              def run(self):
                pass
            
              @signal(SIGTERM)
              @staticmethod
              def handler1(signum, frame):
                pass
            

            Да, Вам придется подумать как правильно и красиво это написать, но читабельность и переиспользуемость повысится на порядок — все будет в одном месте, будет видно что и как управляет демоном.

            Про reacts4daemon можно написать практически тоже самое — почему что-то внешнее должно решать как ведет себя мой демон. Добавьте метод типа do_action() к демону — пусть он сам парсит что может делать, а что нет. Если кто-то захочет — то переопределит этот метод в своем HisDaemon. Получим минус несколько классов и плюс к читаемости и пониманию.

            Статическая конфигурация? Зачем Вам писать столько строк кода, если Вы их все равно дублируете — внесите настройки в сам класс MyDaemon. Тогда у Вас останется один подключаемый файл (daemon.py с основным классом и декоратором) и один основной файл, который несет в себе конфигурацию. Взглянув на последний будут видны и настройки, и порядок выполенения кода/сигналов. И под себя нужно будет менять один основной файл, а не несколько как сейчас :)
              0
              По-хорошему оба Ваших файла «настроек» можно объединить в один, что-то типа такого:
              import sys
              from signal import SIGTERM
              from daemon import RichDaemon, signal
              
              class MyDaemon(RichDaemon):
              
                usage = """ 
                            Usage: .....
                             start - Start daemon
                             ...
                        """
                pidfile = "/var/run/daemon.pid"
                stderr = "/dev/null"
              
                def run(self):
                  pass
              
                @signal(SIGTERM)
                def handler1(self, signum, frame):
                  pass
              
              if "__main__" == __name__:
                my_daemon = MyDaemon()
                my_daemon.do_action(*sys.argv[1:])
              

              Что будет гораздо проще воспринимать :)
                0
                Спасибо огромное, за развернутые ответы =)
                В моем случае — это весьма повторяемая задача. И, вроде, как раз пытался сделать максимальную вынесенность в отдельный файл всех настроек.
                Я, честно говоря, не нашел настроек сигналов вне настроечного класса. По поводу декоратора — в любом случае очень классный вариант, я сейчас буду активно думать. По поводу всего остального занимаюсь осмыслением сказанного Вами. Мне кажется, что такой вариант чуть дольше для каждоразового переписывания под нового демона.
                Еще раз спасибо, я очень и очень буду думать =)
            0
            Несколько лет назад использовал изначальный скрипт в собственном проекте, правда, пришлось его допиливать немного (дописывал метод status, например)… даже думал выложить свою версию. А потом понял, что это всё в общем-то бессмысленно, когда есть upstart и куча аналогичных продуктов.
              +1
              А почему бы не воспользоваться уже готовыми решениями:

              или даже circus?
                +2
                А чем python-daemon не угодил?

                Only users with full accounts can post comments. Log in, please.