Comment exécutez-vous un script Python en tant que service dans Windows?

260

J'esquisse l'architecture d'un ensemble de programmes qui partagent divers objets interdépendants stockés dans une base de données. Je veux que l'un des programmes agisse comme un service qui fournit une interface de niveau supérieur pour les opérations sur ces objets, et que les autres programmes accèdent aux objets via ce service.

Je vise actuellement Python et le framework Django comme technologies pour implémenter ce service. Je suis presque sûr de savoir comment démoniser le programme Python sous Linux. Cependant, il s'agit d'un élément de spécification facultatif que le système doit prendre en charge Windows. J'ai peu d'expérience avec la programmation Windows et aucune expérience avec les services Windows.

Est-il possible d'exécuter un programme Python en tant que service Windows (c'est-à-dire de l'exécuter automatiquement sans connexion utilisateur)? Je n'aurai pas nécessairement à implémenter cette partie, mais j'ai besoin d'une idée approximative de la façon dont cela serait fait pour décider de concevoir selon ces lignes.

Edit: Merci pour toutes les réponses jusqu'à présent, elles sont assez complètes. Je voudrais savoir encore une chose: comment Windows est-il au courant de mon service? Puis-je le gérer avec les utilitaires natifs de Windows? Quel est l'équivalent de mettre un script start / stop dans /etc/init.d?

Hanno Fietz
la source
2
Vérifiez ce modèle de service Windows, il utilise l' API win32service .
CMS

Réponses:

254

Oui, vous pouvez. Je le fais en utilisant les bibliothèques pythoncom fournies avec ActivePython ou qui peuvent être installées avec pywin32 (extensions Python pour Windows).

Il s'agit d'un squelette de base pour un service simple:

import win32serviceutil
import win32service
import win32event
import servicemanager
import socket


class AppServerSvc (win32serviceutil.ServiceFramework):
    _svc_name_ = "TestService"
    _svc_display_name_ = "Test Service"

    def __init__(self,args):
        win32serviceutil.ServiceFramework.__init__(self,args)
        self.hWaitStop = win32event.CreateEvent(None,0,0,None)
        socket.setdefaulttimeout(60)

    def SvcStop(self):
        self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
        win32event.SetEvent(self.hWaitStop)

    def SvcDoRun(self):
        servicemanager.LogMsg(servicemanager.EVENTLOG_INFORMATION_TYPE,
                              servicemanager.PYS_SERVICE_STARTED,
                              (self._svc_name_,''))
        self.main()

    def main(self):
        pass

if __name__ == '__main__':
    win32serviceutil.HandleCommandLine(AppServerSvc)

Votre code irait dans la main()méthode - généralement avec une sorte de boucle infinie qui pourrait être interrompue en vérifiant un indicateur, que vous définissez dans la SvcStopméthode

Ricardo Reyes
la source
21
Après avoir codé cela, comment dire à Windows de l'exécuter en tant que service?
Kit
33
@Kit: exécutez votre script avec la ligne de commande avec le paramètre "install". Ensuite, vous pourrez voir votre application dans la liste des services de Windows, où vous pouvez la démarrer, l'arrêter ou la configurer pour qu'elle démarre automatiquement
Ricardo Reyes
16
Vous donnez une mention spéciale à pythoncom et vous l'importez dans votre exemple de code. Le problème est que vous n'utilisez jamais pythoncom nulle part dans votre exemple de code, vous ne l'importez que. Pourquoi lui donner une mention spéciale et ensuite ne pas montrer son utilisation?
Buttons840
10
Pourquoi pour l' socket.setdefaulttimeout(60)est? Est-il nécessaire pour un service ou a-t-il été simplement copié accidentellement à partir d'un service existant? :)
Timur
7
chrisumbel.com/article/windows_services_in_python Celui-ci est un exemple similaire mais plus complet
csprabala
41

Bien que j'aie voté en faveur de la réponse choisie il y a quelques semaines, en attendant, j'ai eu beaucoup plus de mal avec ce sujet. C'est comme avoir une installation Python spéciale et utiliser des modules spéciaux pour exécuter un script en tant que service est tout simplement la mauvaise façon. Qu'en est-il de la portabilité et autres?

Je suis tombé sur le merveilleux gestionnaire de services sans succion , ce qui a rendu la gestion des services Windows vraiment simple et sensée. Je me suis dit que puisque je pouvais passer des options à un service installé, je pouvais tout aussi bien sélectionner mon exécutable Python et passer mon script en option.

Je n'ai pas encore essayé cette solution, mais je vais le faire dès maintenant et mettre à jour ce message tout au long du processus. Je suis également intéressé par l'utilisation de virtualenvs sur Windows, donc je pourrais proposer un tutoriel tôt ou tard et y accéder ici.

mknaf
la source
Tu as de la chance? Je construis un site très simple pour un client et je n'ai pas besoin d'utiliser toute la pile Apache. La création du service par moi-même a également semblé être une invitation à des problèmes, comme je l'ai lu dans d'autres commentaires.
Jaran
Oui, cela fonctionne et c'est très facile à faire. Vous donnez simplement le chemin et les arguments du script. J'ai réussi à faire fonctionner la mienne sans console au cas où quelqu'un se retrouverait avec une fenêtre de console.
kmcguire
Bien que cela fonctionne apparemment, il y a d'autres difficultés, en particulier lorsque vous "n'avez pas besoin d'utiliser toute la pile Apache": gunicorn par exemple ne fonctionne pas encore sous Windows, qui était en fait le showstopper pour moi.
mknaf
4
L'astuce consiste à exécuter python.exe en tant que service et votre script python en tant que paramètre: comme "nssm install MyServiceName c: \ python27 \ python.exe c: \ temp \ myscript.py"
poleguy
Fonctionne très bien! Sur un système avec plusieurs environnements virtuels, le chemin peut référencer l'exe de l'interpréteur Python dans le répertoire Scripts de l'environnement virtuel souhaité. Il semble que new-servicedans PowerShell devrait être en mesure de le faire, mais le démarrage (et la surveillance) d'un script en tant que service implique évidemment beaucoup plus de détails, que nssm prend très bien en charge.
Fred Schleifer
26

Le moyen le plus simple consiste à utiliser: NSSM - le gestionnaire de service sans succion:

1 - faites le téléchargement sur https://nssm.cc/download

2 - installez le programme python en tant que service: Win prompt as admin

c:> nssm.exe installe WinService

3 - Sur la console NSSM:

chemin: C: \ Python27 \ Python27.exe

Répertoire de démarrage: C: \ Python27

Arguments: c: \ WinService.py

4 - Vérifiez les services créés sur services.msc

Adriano RPL
la source
J'avais l'habitude d'utiliser nssm.exe pour installer mon Visual Studio C ++ .exe en tant que service, et maintenant je peux également utiliser nssm.exe pour mon Python .pyc en tant que service. Merci.
etoricky
Remarque: si votre script * .py se trouve dans un dossier avec de l'espace (par exemple: C: \ Program Files \ myapp.py), vous devez spécifier des arguments entre guillemets: Arguments: "C: \ Program Files \ myapp.py"
Yury Kozlov
Comment fournir un environnement virtuel?
shaik moeed
24

La façon la plus simple d'y parvenir est d'utiliser la commande native sc.exe:

sc create PythonApp binPath= "C:\Python34\Python.exe --C:\tmp\pythonscript.py"

Références:

  1. https://technet.microsoft.com/en-us/library/cc990289(v=ws.11).aspx
  2. Lors de la création d'un service avec sc.exe, comment passer les paramètres de contexte?
pyOwner
la source
Je pense que c'est un problème avec votre commande ou votre application elle-même. Quoi qu'il en soit, consultez ce support.microsoft.com/en-us/help/886695/…
pyOwner
Mon application fonctionne bien en dehors du service, et j'ai utilisé le même code ci-dessus sans résultat.
nimeresam
Comment fournir un environnement virtuel?
shaik moeed
Avez-vous essayé virtualenv?
pyOwner
1
Ça ne marche pas. Un service Windows doit exposer une certaine interface que le package pywin32 fait. Cependant, un script Python simple et ancien ne suffira pas.
Siddhartha Gandhi
23

Il existe quelques alternatives pour installer en tant que service pratiquement n'importe quel exécutable Windows.

Méthode 1: utilisez instsrv et srvany à partir de rktools.exe

Pour Windows Home Server ou Windows Server 2003 (fonctionne également avec WinXP), les outils du Kit de ressources Windows Server 2003 sont fournis avec des utilitaires pouvant être utilisés en tandem à cet effet, appelés instsrv.exe et srvany.exe . Consultez cet article Microsoft KB KB137890 pour plus de détails sur l'utilisation de ces utilitaires.

Pour Windows Home Server, il existe un excellent wrapper convivial pour ces utilitaires nommé à juste titre " Any Service Installer ".

Méthode 2: utilisez ServiceInstaller pour Windows NT

Il existe une autre alternative à l'aide de ServiceInstaller pour Windows NT ( téléchargeable ici ) avec des instructions python disponibles . Contrairement à son nom, il fonctionne également avec Windows 2000 et Windows XP. Voici quelques instructions sur la façon d'installer un script python en tant que service.

Installation d'un script Python

Exécutez ServiceInstaller pour créer un nouveau service. (Dans cet exemple, on suppose que python est installé sur c: \ python25)

Service Name  : PythonTest
Display Name : PythonTest 
Startup : Manual (or whatever you like)
Dependencies : (Leave blank or fill to fit your needs)
Executable : c:\python25\python.exe
Arguments : c:\path_to_your_python_script\test.py
Working Directory : c:\path_to_your_python_script

Après l'installation, ouvrez l'applet Services du Panneau de configuration, sélectionnez et démarrez le service PythonTest.

Après ma réponse initiale, j'ai remarqué qu'il y avait déjà des questions et réponses étroitement liées sur SO. Voir également:

Puis-je exécuter un script Python en tant que service (sous Windows)? Comment?

Comment informer Windows d'un service que j'ai écrit en Python?

popcnt
la source
Je viens de remarquer qu'il existe déjà d'autres questions / réponses similaires: stackoverflow.com/questions/32404/… stackoverflow.com/questions/34328/…
popcnt
Service Installer ne fonctionne pas sur une architecture 64 bits, donc l'option 1 devient l'option goto.
Noah Campbell
Le lien ci-dessus vers ServiceInstaller ne fonctionne plus. Je l'ai trouvé ici: sites.google.com/site/conort/…
LarsH
2
hors note, je ne pense pas que ce NTserait nécessairement "contraire" au nom, du moins pas dans le discours des programmeurs. Il fait simplement référence à «l' architecture NT », par opposition à la « marque NT ». Cela dit, selon les discussions sur wikipedia, cela fait débat, car "ce n'est pas un terme officiel de Microsoft", mais il existe néanmoins une tradition avec cette ligne de pensée.
n611x007
15

Explication étape par étape comment le faire fonctionner:

1- Créez d'abord un fichier python selon le squelette de base mentionné ci-dessus. Et enregistrez-le dans un chemin par exemple: "c: \ PythonFiles \ AppServerSvc.py"

import win32serviceutil
import win32service
import win32event
import servicemanager
import socket


class AppServerSvc (win32serviceutil.ServiceFramework):
    _svc_name_ = "TestService"
    _svc_display_name_ = "Test Service"


    def __init__(self,args):
        win32serviceutil.ServiceFramework.__init__(self,args)
        self.hWaitStop = win32event.CreateEvent(None,0,0,None)
        socket.setdefaulttimeout(60)

    def SvcStop(self):
        self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
        win32event.SetEvent(self.hWaitStop)

    def SvcDoRun(self):
        servicemanager.LogMsg(servicemanager.EVENTLOG_INFORMATION_TYPE,
                          servicemanager.PYS_SERVICE_STARTED,
                          (self._svc_name_,''))
        self.main()

    def main(self):
        # Your business logic or call to any class should be here
        # this time it creates a text.txt and writes Test Service in a daily manner 
        f = open('C:\\test.txt', 'a')
        rc = None
        while rc != win32event.WAIT_OBJECT_0:
            f.write('Test Service  \n')
            f.flush()
            # block for 24*60*60 seconds and wait for a stop event
            # it is used for a one-day loop
            rc = win32event.WaitForSingleObject(self.hWaitStop, 24 * 60 * 60 * 1000)
        f.write('shut down \n')
        f.close()

if __name__ == '__main__':
    win32serviceutil.HandleCommandLine(AppServerSvc)

2 - Lors de cette étape, nous devons enregistrer notre service.

Exécutez l'invite de commande en tant qu'administrateur et tapez:

sc create TestService binpath = "C: \ Python36 \ Python.exe c: \ PythonFiles \ AppServerSvc.py" DisplayName = "TestService" start = auto

le premier argument de binpath est le chemin de python.exe

le deuxième argument de binpath est le chemin de votre fichier python que nous avons déjà créé

Ne manquez pas que vous devez mettre un espace après chaque " = signe ".

Ensuite, si tout va bien, vous devriez voir

[SC] SUCCÈS CreateService

Maintenant, votre service python est installé en tant que service Windows maintenant. Vous pouvez le voir dans Service Manager et registre sous:

HKEY_LOCAL_MACHINE \ SYSTEM \ CurrentControlSet \ Services \ TestService

3- Ok maintenant. Vous pouvez démarrer votre service sur le gestionnaire de services.

Vous pouvez exécuter chaque fichier python qui fournit ce squelette de service.

Seckin Sanli
la source
Il y a beaucoup de mauvais exemples sur la façon d'utiliser SetEvent(self.hWaitStop)et WaitForSingleObject. Basé sur une copie irréfléchie de la réponse sélectionnée ici probablement. C'est un bon moyen de le faire qui fonctionne correctement pour les arguments "debug" et "stop". (La partie sur l'utilisation de SC semble redondante quand HandleCommandLinefait le travail, et peut exécuter le débogage.)
Alias_Knagg
3

pysc: Service Control Manager sur Python

Exemple de script à exécuter en tant que service provenant de pythonhosted.org :

from xmlrpc.server import SimpleXMLRPCServer

from pysc import event_stop


class TestServer:

    def echo(self, msg):
        return msg


if __name__ == '__main__':
    server = SimpleXMLRPCServer(('127.0.0.1', 9001))

    @event_stop
    def stop():
        server.server_close()

    server.register_instance(TestServer())
    server.serve_forever()

Créer et démarrer le service

import os
import sys
from xmlrpc.client import ServerProxy

import pysc


if __name__ == '__main__':
    service_name = 'test_xmlrpc_server'
    script_path = os.path.join(
        os.path.dirname(__file__), 'xmlrpc_server.py'
    )
    pysc.create(
        service_name=service_name,
        cmd=[sys.executable, script_path]
    )
    pysc.start(service_name)

    client = ServerProxy('http://127.0.0.1:9001')
    print(client.echo('test scm'))

Arrêter et supprimer le service

import pysc

service_name = 'test_xmlrpc_server'

pysc.stop(service_name)
pysc.delete(service_name)
pip install pysc
Seliverstov Maksim
la source
3
Est-ce que quelqu'un sait pourquoi cela a obtenu un downvote? Cela ressemble à une bonne solution.
Jarrod Chesney
3

J'ai commencé à héberger en tant que service avec pywin32 .

Tout allait bien mais j'ai rencontré le problème selon lequel le service n'a pas pu démarrer dans les 30 secondes (délai par défaut pour Windows) au démarrage du système. C'était critique pour moi car le démarrage de Windows a eu lieu simultanément sur plusieurs machines virtuelles hébergées sur une machine physique, et la charge IO était énorme. Les messages d'erreur étaient:

Error 1053: The service did not respond to the start or control request in a timely fashion.

Error 7009: Timeout (30000 milliseconds) waiting for the <ServiceName> service to connect.

J'ai beaucoup combattu avec pywin, mais j'ai fini par utiliser NSSM comme cela était proposé dans cette réponse . C'était très facile d'y migrer.

flam3
la source
2

nssm en python 3+

(J'ai converti mon fichier .py en .exe avec pyinstaller )

nssm: comme dit précédemment

  • exécutez nssm install {ServiceName}
  • Sur la console NSSM:

    chemin: chemin \ vers \ votre \ programme.exe

    Répertoire de démarrage: chemin \ vers \ votre \ #même que le chemin mais sans votre programme.exe

    Arguments: vide

Si vous ne souhaitez pas convertir votre projet en .exe

  • créer un fichier .bat avec python {{your python.py file name}}
  • et définissez le chemin d'accès au fichier .bat
codeur
la source
Comment fournir un environnement virtuel?
shaik moeed
1

Un exemple pywin32 complet utilisant une boucle ou un sous-fil

Après avoir travaillé dessus pendant quelques jours, voici la réponse que j'aurais souhaité trouver, en utilisant pywin32 pour le garder agréable et autonome.

Il s'agit d'un code de travail complet pour une solution basée sur une boucle et une solution basée sur un thread. Il peut fonctionner à la fois sur python 2 et 3, même si je n'ai testé que la dernière version sur 2.7 et Win7. La boucle devrait être bonne pour interroger le code, et la bande de roulement devrait fonctionner avec plus de code de type serveur. Il semble bien fonctionner avec le serveur wsgi de la serveuse qui n'a pas de méthode standard pour s'arrêter gracieusement.

Je voudrais également noter qu'il semble y avoir beaucoup d'exemples, comme celui-ci qui sont presque utiles, mais en réalité trompeurs, car ils ont coupé et collé d'autres exemples à l'aveuglette. Je peux me tromper. mais pourquoi créer un événement si vous ne l'attendez jamais?

Cela dit, je me sens toujours sur un terrain quelque peu fragile, en particulier en ce qui concerne la propreté de la sortie de la version thread, mais au moins je pense qu'il n'y a rien de trompeur ici.

Pour exécuter, copiez simplement le code dans un fichier et suivez les instructions.

mettre à jour:

Utilisez un simple indicateur pour terminer le thread. Le bit important est que le "thread fait" s'imprime.
Pour un exemple plus élaboré sortant d'un fil de serveur non coopératif, voir mon article sur le serveur wsgi de la serveuse .

# uncomment mainthread() or mainloop() call below
# run without parameters to see HandleCommandLine options
# install service with "install" and remove with "remove"
# run with "debug" to see print statements
# with "start" and "stop" watch for files to appear
# check Windows EventViever for log messages

import socket
import sys
import threading
import time
from random import randint
from os import path

import servicemanager
import win32event
import win32service
import win32serviceutil
# see http://timgolden.me.uk/pywin32-docs/contents.html for details


def dummytask_once(msg='once'):
    fn = path.join(path.dirname(__file__),
                '%s_%s.txt' % (msg, randint(1, 10000)))
    with open(fn, 'w') as fh:
        print(fn)
        fh.write('')


def dummytask_loop():
    global do_run
    while do_run:
        dummytask_once(msg='loop')
        time.sleep(3)


class MyThread(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)

    def run(self):
        global do_run
        do_run = True
        print('thread start\n')
        dummytask_loop()
        print('thread done\n')

    def exit(self):
        global do_run
        do_run = False


class SMWinservice(win32serviceutil.ServiceFramework):
    _svc_name_ = 'PyWinSvc'
    _svc_display_name_ = 'Python Windows Service'
    _svc_description_ = 'An example of a windows service in Python'

    @classmethod
    def parse_command_line(cls):
        win32serviceutil.HandleCommandLine(cls)

    def __init__(self, args):
        win32serviceutil.ServiceFramework.__init__(self, args)
        self.stopEvt = win32event.CreateEvent(None, 0, 0, None)  # create generic event
        socket.setdefaulttimeout(60)

    def SvcStop(self):
        servicemanager.LogMsg(servicemanager.EVENTLOG_INFORMATION_TYPE,
                            servicemanager.PYS_SERVICE_STOPPED,
                            (self._svc_name_, ''))
        self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
        win32event.SetEvent(self.stopEvt)  # raise event

    def SvcDoRun(self):
        servicemanager.LogMsg(servicemanager.EVENTLOG_INFORMATION_TYPE,
                            servicemanager.PYS_SERVICE_STARTED,
                            (self._svc_name_, ''))
        # UNCOMMENT ONE OF THESE
        # self.mainthread()
        # self.mainloop()

    # Wait for stopEvt indefinitely after starting thread.
    def mainthread(self):
        print('main start')
        self.server = MyThread()
        self.server.start()
        print('wait for win32event')
        win32event.WaitForSingleObject(self.stopEvt, win32event.INFINITE)
        self.server.exit()
        print('wait for thread')
        self.server.join()
        print('main done')

    # Wait for stopEvt event in loop.
    def mainloop(self):
        print('loop start')
        rc = None
        while rc != win32event.WAIT_OBJECT_0:
            dummytask_once()
            rc = win32event.WaitForSingleObject(self.stopEvt, 3000)
        print('loop done')


if __name__ == '__main__':
    SMWinservice.parse_command_line()
Alias_Knagg
la source
0

La réponse acceptée utilise win32serviceutilworks mais est compliquée et rend le débogage et les changements plus difficiles. Il est beaucoup plus facile d'utiliser NSSM ( le Non-Sucking Service Manager) . Vous écrivez et déboguez confortablement un programme Python normal et quand il fonctionne enfin, vous utilisez NSSM pour l'installer en tant que service en moins d'une minute:

À partir d'une invite de commandes élevée (admin), vous exécutez nssm.exe install NameOfYourServiceet vous remplissez ces options:

  • chemin : (le chemin vers python.exe par exemple C:\Python27\Python.exe)
  • Arguments : (le chemin vers votre script python, par exemple c:\path\to\program.py)

Soit dit en passant, si votre programme imprime des messages utiles que vous souhaitez conserver dans un fichier journal, NSSM peut également gérer cela et bien plus pour vous.

ndemou
la source
Oui, c'est un double de la réponse d'Adriano. J'ai voté pour cette réponse et j'ai essayé de la modifier, mais après les modifications, je cherchais une nouvelle réponse.
ndemou
Comment fournir un environnement virtuel?
shaik moeed
0

Pour tous ceux qui veulent créer un service dans VENV ou Pycharm !!!!!!!

Après avoir lu toutes les réponses et créé des scripts, si vous pouvez exécuter python service.py installet python service.py debug, maispython service.py start sans réponse.

C'est peut-être dû à un problème de venv, car le service Windows démarre votre service par exec PROJECT\venv\Lib\site-packages\win32\pythonservice.exe.

Vous pouvez utiliser powershellou cmdpour tester votre service pour trouver plus de détails sur l'erreur.

PS C:\Users\oraant> E:

PS E:\> cd \Software\PythonService\venv\Lib\site-packages\win32

PS E:\Software\PythonService\venv\Lib\site-packages\win32> .\pythonservice.exe -debug ttttt
Debugging service ttttt - press Ctrl+C to stop.
Error 0xC0000004 - Python could not import the service's module

Traceback (most recent call last):
  File "E:\Software\PythonService\my_service.py", line 2, in <module>
    import win32serviceutil
ModuleNotFoundError: No module named 'win32serviceutil'

(null): (null)

Si vous obtenez une erreur comme moi, vous pouvez vérifier ma réponse dans une autre question, je l'ai corrigée et poste mon code ici .

oraant
la source
-1

https://www.chrisumbel.com/article/windows_services_in_python

  1. Suivez le PySvc.py

  2. changer le dossier dll

Je sais que c'est vieux mais j'étais coincé là-dessus pour toujours. Pour moi, ce problème spécifique a été résolu en copiant ce fichier - pywintypes36.dll

De -> Python36 \ Lib \ site-packages \ pywin32_system32

À -> Python36 \ Lib \ site-packages \ win32

setx /M PATH "%PATH%;C:\Users\user\AppData\Local\Programs\Python\Python38-32;C:\Users\user\AppData\Local\Programs\Python\Python38-32\Scripts;C:\Users\user\AppData\Local\Programs\Python\Python38-32\Lib\site-packages\pywin32_system32;C:\Users\user\AppData\Local\Programs\Python\Python38-32\Lib\site-packages\win32
  1. changer le chemin vers le dossier python en

cd C:\Users\user\AppData\Local\Programs\Python\Python38-32

  1. NET START PySvc
  2. NET STOP PySvc
gunarajulu renganathan
la source