Forum Programmation.python Programmer un daemon

Posté par . Licence CC by-sa
Tags : aucun
1
7
sept.
2015

Au cas où vous auriez la bonté d’aider une moule autodidacte qui apprend Python sur le tas je vous expose mon problème…

Le contexte :

J’ai un script Python3 qui récupère les posts du bouchot pour les stocker dans une base SQLite, actuellement si je veux que ce script tourne de manière régulière je lui colle une entrée dans ma crontab, ça juste marche mais ça ne me semble pas propre. Donc ce que je voudrais faire c’est que ce script puisse tourner en tâche de fond et faire sa requête GET pas forcément à intervalles réguliers mais en fonction du nombre de posts récupérés à la dernière exécution. Je me dis donc que ce serait bien que ce script soit « daemonizé ».

Donc vu que j’y connais rien et que je suis une buse complète je me suis dit qu’il faudrait commencer par un test simple. Donc voilà ce que je fais :

Prout.py :

#!/usr/bin/env python3

from time import sleep

def prout():
  print("prout !")
  sleep(3)

while True:
  prout()

daemon_prout.py :

#!/usr/bin/env python3

import daemon

from Prout import prout

context = daemon.DaemonContext()
with context:
  prout()

Quand je lance daemon_prout.py ça flatule bien toutes les 3 secondes, par contre ça ne le fait pas en arrière plan…

Qu’est-ce que je fais mal ?

J’ai essayé d’autres approches, soit ça me rend bien la main mais ça "prout !" pas, soit ça "prout !" mais ça ne me rend pas la main (ça ne tourne donc pas en tâche de fond).

Je pense qu’il y a des choses à paramétrer sur l’objet context mais je suis perdu. J’ai essayé avec context.files_preserves = [1,2,3] mais ça ne change rien.

Je sais que je pourrais lancer mon script à l’aide de nohup mais je voudrais bien comprendre le principe des daemons en Python3…

Moulesquement.

  • # j'y connais rien mais faut vraiment que je m'y mettes

    Posté par . Évalué à 4.

    ici il semble y avoir un exemple
    http://stackoverflow.com/questions/4637420/efficient-python-daemon

    import daemon
    import time
    
    def do_something():
        while True:
            with open("/tmp/current_time.txt", "w") as f:
                f.write("The time is now " + time.ctime())
            time.sleep(5)
    
    def run():
        with daemon.DaemonContext():
            do_something()
    
    if __name__ == "__main__":
        run()
    • [^] # Re: j'y connais rien mais faut vraiment que je m'y mettes

      Posté par . Évalué à 2.

      Merci je vais essayer ça.

    • [^] # Re: j'y connais rien mais faut vraiment que je m'y mettes

      Posté par . Évalué à 2.

      Merci beaucoup pour ta réponse. Alors voilà le code que j’ai pour arriver à ce que je veux. Bon OK ce que je veux est assez débile en vérité, je ne vais pas faire ça pour mon programme final mais voilà :

      #!/usr/bin/env python3
      
      from time import sleep
      import daemon
      import lockfile
      import sys
      
      def prout():
        while True:   
          print("prout !")
          sleep(3)
      
      context = daemon.DaemonContext(
        working_directory='./',
        pidfile=lockfile.FileLock('./Prout.pid'),
        stdout=sys.stdout
        )
      
      def run():
        with context:
          prout()
      
      #if __name__ == "__main__":
      run()

      Le programme tourne bien en tâche de fond mais continue de flatuler sur la sortie standard…

      Comme tu vois j’ai commenté le if __name__ == "__main__", pour voir si ça marchait sans… va falloir que j’aille lire le bon post SO qui traite de cette incantation pour être moins bête…

      Bon, en l’état pour « tuer » ce daemon je dois faire un kill… plus qu’à trouver comment je peux implémenter un ./Prout.py start pour le démarrer et un ./Prout.py stop pour l’arrêter… et bien sûr je ne vais pas rediriger la stdout vers sys.stdout mais plutôt vers un fichier standard.

      Merci NeoX, tu gères !

  • # Pour quoi faire ?

    Posté par . Évalué à 6.

    Sinon, tu utilises une distrib moderne et tu laisses systemd s’occuper de tout !

    /etc/systemd/system/prout.service :

    [Service]
    ExecStart=/usr/local/prout.py
    
    [Install]
    WantedBy=multi-user.target
    
    • [^] # Re: Pour quoi faire ?

      Posté par . Évalué à 2.

      Oui c’est une solution… Par contre je ne veux pas lancer ce script en tant que root (j’imagine que systemd permet de spécifier un utilisateur, faut que je RTFM je sais…)

      Par contre je suppose que ça permet quand même moins de souplesse qu’utiliser la lib daemon de Python…

      • [^] # Re: Pour quoi faire ?

        Posté par . Évalué à 6.

        Allez, je t’aide :)

        [Service]
        ExecStart=/usr/local/prout.py
        User=prout
        
        [Install]
        WantedBy=multi-user.target
        

        Par contre, je ne suis pas sûr de voir en quoi tu aurais moins de souplesse en laissant systemd faire son taf… je veux bien un exemple :)

        • [^] # Re: Pour quoi faire ?

          Posté par . Évalué à 2.

          Je ne connais pas assez la lib daemon pour te trouver un exemple et peut-être bien que ça n’offre aucune souplesse supplémentaire. Alors j’en un autre argument à avancer : ne pas être dépendant de systemd ?

          • [^] # Re: Pour quoi faire ?

            Posté par . Évalué à 1.

            Ne pas dépendre de systemd, ou éviter systemd par religion ?

            "Quand certains râlent contre systemd, d'autres s'attaquent aux vrais problèmes." (merci Sinma !)

            • [^] # Re: Pour quoi faire ?

              Posté par . Évalué à 3.

              C’est surtout que je souhaite apprendre le Python et si je peux apprendre à faire des scripts multi-plateformes je préférerais. Sinon systemd je l’utilise depuis un petit moment sur mon poste personnel, j’en suis très content.

              J’ai réussi à daemonizé mon script (pas la prouteuse, le script qui récupère les posts à intervalle réguliers…) me reste à apprendre à le contrôler (commandes start, stop…) et là je m’aperçois qu’un Ctrl+d, qui va donc fermer le shell avec lequel j’ai lancé le script l’arrête :/ Maintenant que j’ai compris la base va falloir que que je lise plus en profondeur la doc de la lib daemon…

    • [^] # Re: Pour quoi faire ?

      Posté par . Évalué à 2.

      Comme on est vendredi…

      Sinon, tu utilises une distrib moderne et tu laisses systemd s’occuper de tout !

      Autant, je comprends bien que systemd => une distrib moderne, autant je ne vois pas bien l’équivalence…

      Une distrib moderne ≠> systemd.

  • # supervisord

    Posté par (page perso) . Évalué à 3.

    Le plus simple est certainement de laisse faire le boulot de "daemonization" à http://supervisord.org/

    • [^] # Re: supervisord

      Posté par (page perso) . Évalué à 3.

      Quel est l'avantage par rapport à utiliser le systeme d'init de la distrib ?

      • [^] # Re: supervisord

        Posté par (page perso) . Évalué à 2.

        • tu peux relancer le service via l'interface web (ou socket), donc sans être root
        • ne pas avoir à écrire un fichier de conf en bash illisible (si tu n'as pas systemd)
        • être indépendant du système d'init

        systemd supprime en bonne partie les deux derniers arguments. Perso, je faisais du supervisor, et je passe à systemd.

  • # Pourquoi tu dis que cela ne marche pas

    Posté par . Évalué à 3.

    Préambule:
    J'ai dernièrement réaliser un daemon en python (2.7) pour une cible embarqué, je te file un lien vers le tuto que j'ai suivi (et qui te sera très utile voir fin du post) et je n'ai pas porté le binding daemon lib sur ma cible mais utilisé les fonctions native du système (lib os) .

    Question : Tu dis que ton code ne marche pas en arrière plan. Peut tu préciser (1) Tu ne vois pas les prout et (2) quand tu fais un 'ps' vois tu un PID associé à ton daemon ?

    Si (1)=TRUE et (2)=Je vois mon daemon dans les pid, alors ta conclusion est erronée et ton daemon tourne. Simplement du fais qu'il ne dispose pas de la redirection de stdin, stdout et stderr tu ne vois pas d'affichage sur ta console.

    Si (2)=Je ne vois rien, alors tu as raison, la daemonisation en fonctionne pas.

    Référence : (Il faut rendre à cesar…etc)
    Voici en version 2.7

    Voici la version en 3.x

    Bonne flatulence

    • [^] # Re: Pourquoi tu dis que cela ne marche pas

      Posté par . Évalué à 2.

      Lorsque je fais un ps je vois bien un python3 ./daemon_prout.py mais ça me paraît normal vu que mon programme Prout.py fait un while True

      Je vais essayer la solution de NeoX (je n’ai pas besoin de deux fichiers déjà…) je vais faire simple. J’étais moi aussi tombé sur cette manière de faire en « pur Python », sans utiliser de lib dédiée, je m’y essayerai si la première solution ne fonctionne pas. Un grand merci pour ton aide.

      • [^] # Re: Pourquoi tu dis que cela ne marche pas

        Posté par . Évalué à 2.

        Mais de rien, donc si ton daemon est présent et tourne en mémoire c'est sans doute un simple soucis de pipe.

        je te suggere de faire
        import sys

        et

        daemon.DaemonContext(stdout=sys.stdout)

        pour récupérer le pipe de sortie et ainsi avoir l'affichage des sortie de ton daemon

Suivre le flux des commentaires

Note : les commentaires appartiennent à ceux qui les ont postés. Nous n'en sommes pas responsables.