How do you run a Python script as a service in Windows?
Asked Answered
E

14

346

I am sketching the architecture for a set of programs that share various interrelated objects stored in a database. I want one of the programs to act as a service which provides a higher level interface for operations on these objects, and the other programs to access the objects through that service.

I am currently aiming for Python and the Django framework as the technologies to implement that service with. I'm pretty sure I figure how to daemonize the Python program in Linux. However, it is an optional spec item that the system should support Windows. I have little experience with Windows programming and no experience at all with Windows services.

Is it possible to run a Python programs as a Windows service (i. e. run it automatically without user login)? I won't necessarily have to implement this part, but I need a rough idea how it would be done in order to decide whether to design along these lines.

Edit: Thanks for all the answers so far, they are quite comprehensive. I would like to know one more thing: How is Windows aware of my service? Can I manage it with the native Windows utilities? What is the equivalent of putting a start/stop script in /etc/init.d?

Eliathas answered 28/8, 2008 at 14:28 Comment(0)
M
301

Yes you can. I do it using the pythoncom libraries that come included with ActivePython or can be installed with pywin32 (Python for Windows extensions).

This is a basic skeleton for a simple service:

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)

Your code would go in the main() method—usually with some kind of infinite loop that might be interrupted by checking a flag, which you set in the SvcStop method

Mitchum answered 28/8, 2008 at 14:39 Comment(25)
After coding this, how do I tell Windows to run this as a service?Pinery
@Kit: run your script with the from the command line with the parameter "install". Then you'll be able to see your application in Windows' Services list, where you can start it, stop it, or set it to start automaticallyMitchum
python my_script_as_service.py install ?Pinery
Script missing "import socket" at the top. You can also add "svc_description" to change the service's description. Thanks this saved me loads of hunting!Celebrate
You give special mention to pythoncom, and you import it in your example code. The problem is you never actually use pythoncom anywhere in your example code, you only import it. Why give it special mention and then not show its usage?Eyesight
Why for the socket.setdefaulttimeout(60) is? Is it needed for a service, or was it just accidentaly copied from some existing service? :)Hanson
Can you explain what you mean by "checking a flag" to stop the service? How do you do that?Madewell
@ewok: He means set an attribute (say, is_alive) on self indicating if SvcStop has been called yet, then set that attribute from SvcStop and check it every so often in SvcDoRun.Vatican
I'm with @Timur, what is the reason for the socket.setdefaulttimeout(60)?Siamese
This looks great, I replaced the empty main with a loop which writes to a file some text. The file is created but remains empty, any idea why? Moreover, when I do service start I get Starting service TestService, if I do next I do service stop I get Error stopping service: The service has not been started. (1062), what's wrong?Quota
@RicardoReyes so how do you uninstall such a service? in case it's remove as in install, start, stop and remove seen in command line help, where do they come from, particularly, are they invented by pywin32, or...?Reitman
Removing the service doesn't seem to work for me. The service always ends up in a state STOP_PENDING or similar and doesn't ever quit unless I kill it with sc queryexCamden
@csprabala URL is dead! :( (HTTP 404 - not found)Saari
@schlamar I am probably missing something simple. But I am not clear on how to actually call my code. If I include an infinite loop, like one that appends 'Hello World' to the bottom of a text file every minute, how to I actually have the service run that? I have gotten a windows service set up. I can manage it and all those goodies... but unclear on actually running my code when the service is started.Perkin
@RicardoReyes I tried installing the service on Windows 10 and worked perfectly, same command does not work on Windows Server 2012 - only returns list of options as if the "install" argument is not passed to it. No error messages. Any thoughts?Lyudmila
I used this code as it is and was able to install the service using python test_service.py install but when I try to start the service using python test_service.py start or NET START TestService it returns a message "The service is not responding to the control function", is it necessary to implement something in the SvcDoRun method ?Lexie
@Timur - I removed the "socket.setdefaulttimeout(60)" line and on windows7 it works without.Immuno
@SomeGuy I implemented the main method from chrisumbel.com/article/windows_services_in_python but I still get that error. Any progress?Pot
pywintypes.error: (1053, 'StartService', 'The service did not respond to the start or control request in a timely fashio n.')Voltaism
For those still struggling. This post on github gist works well gist.github.com/guillaumevincent/…Lizzettelizzie
@ErnestoMonroy A helpful thing from that link is the --hidden-import, which fixes the import error about win32timezone. Basically, in the pyinstaller command, add the flag --hidden-import=win32timezone, since apparently that module is necessary, but pyinstaller doesn't know that since we don't import it directly.Beefy
@ErnestoMonroy I also needed the if part in the ifmain block of that gist. Without it, things still work when running the service as debug, but then after install and then start, I get the dreaded "timely fashion" message, but that if fixed it.Beefy
If you get "The service did not respond to the start or control request in a timely fashion", this answer might help https://mcmap.net/q/94240/-how-to-create-windows-service-using-pythonMetamorphosis
Does pywin32 still exist? ERROR: Could not find a version that satisfies the requirement pywin32==305 (from versions: none)Ammonate
@Ammonate It does! I'm using it currently: pywin32Attrition
O
102

The simplest way is to use the: NSSM - the Non-Sucking Service Manager. Just download and unzip to a location of your choosing. It's a self-contained utility, around 300KB (much less than installing the entire pywin32 suite just for this purpose) and no "installation" is needed. The zip contains a 64-bit and a 32-bit version of the utility. Either should work well on current systems (you can use the 32-bit version to manage services on 64-bit systems).

GUI approach

1 - install the python program as a service. Open a Win prompt as admin

c:\>nssm.exe install WinService

2 - On NSSM´s GUI console:

path: C:\Python27\Python27.exe

Startup directory: C:\Python27

Arguments: c:\WinService.py

3 - check the created services on services.msc

Scripting approach (no GUI)

This is handy if your service should be part of an automated, non-interactive procedure, that may be beyond your control, such as a batch or installer script. It is assumed that the commands are executed with administrative privileges.

For convenience the commands are described here by simply referring to the utility as nssm.exe. It is advisable, however, to refer to it more explicitly in scripting with its full path c:\path\to\nssm.exe, since it's a self-contained executable that may be located in a private path that the system is not aware of.

1. Install the service

You must specify a name for the service, the path to the proper Python executable, and the path to the script:

nssm.exe install ProjectService "c:\path\to\python.exe" "c:\path\to\project\app\main.py"

More explicitly:

nssm.exe install ProjectService 
nssm.exe set ProjectService Application "c:\path\to\python.exe"
nssm.exe set ProjectService AppParameters "c:\path\to\project\app\main.py"

Alternatively you may want your Python app to be started as a Python module. One easy approach is to tell nssm that it needs to change to the proper starting directory, as you would do yourself when launching from a command shell:

nssm.exe install ProjectService "c:\path\to\python.exe" "-m app.main"
nssm.exe set ProjectService AppDirectory "c:\path\to\project"

This approach works well with virtual environments and self-contained (embedded) Python installs. Just make sure to have properly resolved any path issues in those environments with the usual methods. nssm has a way to set environment variables (e.g. PYTHONPATH) if needed, and can also launch batch scripts.

2. To start the service

nssm.exe start ProjectService 

3. To stop the service

nssm.exe stop ProjectService

4. To remove the service, specify the confirm parameter to skip the interactive confirmation.

nssm.exe remove ProjectService confirm
Outherod answered 27/9, 2017 at 14:5 Comment(7)
I used to use nssm.exe install my Visual Studio C++ .exe as service, and now I can use nssm.exe as well for my Python .pyc as service. Thanks.Kilogrammeter
Note: if your *.py script is located in a folder with space (e.g: C:\Program Files\myapp.py) need to specify arguments in quotes: Arguments: "C:\Program Files\myapp.py"Fascia
How to provide a virtual environment?Skedaddle
Do not loose more time and go for NSSM approach. For virtual environment, you only need to point to the python executable inside the virtualenv folder.Terbia
@Adriano P but when we want to run our app as a service on our client's system ? How to use nssm in that scenario?Excursive
This should be the go to approach. Took me less than 5 minutes to get my script installed and running as a service. For virtual environments, I think just pointing the Application parameter to the python executable of the virtual environment should work.Seniority
nssm is now a dead project. It's better to use the pywin32 approachSisal
C
53

Although I upvoted the chosen answer a couple of weeks back, in the meantime I struggled a lot more with this topic. It feels like having a special Python installation and using special modules to run a script as a service is simply the wrong way. What about portability and such?

I stumbled across the wonderful Non-sucking Service Manager, which made it really simple and sane to deal with Windows Services. I figured since I could pass options to an installed service, I could just as well select my Python executable and pass my script as an option.

I have not yet tried this solution, but I will do so right now and update this post along the process. I am also interested in using virtualenvs on Windows, so I might come up with a tutorial sooner or later and link to it here.

Camden answered 28/7, 2014 at 13:41 Comment(8)
Any luck? I'm building a very simple site for a client and don't need to use the whole Apache stack. Also building the service by myself has sounded like an invite for trouble too, as I have read from other comments.Fortran
Yes, this works and it is very easy to do. You just give the path and arguments for the script. I was able to get mine to run with out a console just in case someone ends up with a console window somehow.Liss
While this apparently works, there are other difficulties especially when you "don't need to use the whole Apache stack": gunicorn for example doesn't run on Windows yet, which actually was the showstopper for me.Camden
The trick here is to run python.exe as a service and your python script as the parameter: like "nssm install MyServiceName c:\python27\python.exe c:\temp\myscript.py"Pagepageant
Works great! On a system with multiple virtual environments, the path can reference the Python interpreter exe in the Scripts directory of the desired virtual environment. It seems like new-service in PowerShell should be able to do this, but starting (and monitoring) a script as a service evidently involves a lot more details, which nssm takes care of very nicely.Lit
It is relatively quite easy to launch python script as a service using NSSM (personally this is the easiest I feel), as also mentioned in this answer. See "How to use the NSSM" here: nssm.cc/usageDrudge
@FredSchleifer How to provide a virtual environment to NSSM?Skedaddle
@shaikmoeed You can extend the PYTHONPATH environment variable to the place of your libraries and scripts on the most right tab of the nssm.exe GUI.Diameter
C
41

The simplest way to achieve this is to use native command sc.exe:

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

References:

  1. https://technet.microsoft.com/en-us/library/cc990289(v=ws.11).aspx
  2. When creating a service with sc.exe how to pass in context parameters?
Chemash answered 7/12, 2016 at 12:23 Comment(6)
I think, it's a problem with your command or application itself. Anyway, check this support.microsoft.com/en-us/help/886695/…Chemash
My app works fine outside the service, and i used the same code above with no result.Dare
How to provide a virtual environment?Skedaddle
Did you try virtualenv?Chemash
This doesn't work. A Windows Service must expose a certain interface that the pywin32 package does. However, a plain-old Python script will not suffice.Froggy
If you run with powershell should use sc.exe, most of those who complained should be running in to that. See https://mcmap.net/q/94242/-sc-create-binpath-errorHarpsichord
J
24

Step by step explanation how to make it work :

1- First create a python file according to the basic skeleton mentioned above. And save it to a path for example : "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 - On this step we should register our service.

Run command prompt as administrator and type as:

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

the first argument of binpath is the path of python.exe

second argument of binpath is the path of your python file that we created already

Don't miss that you should put one space after every "=" sign.

Then if everything is ok, you should see

[SC] CreateService SUCCESS

Now your python service is installed as windows service now. You can see it in Service Manager and registry under :

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\TestService

3- Ok now. You can start your service on service manager.

You can execute every python file that provides this service skeleton.

Judicative answered 29/6, 2017 at 8:37 Comment(3)
There are lots of bad examples out there of how to use SetEvent(self.hWaitStop) and WaitForSingleObject. Based on thoughtless copying of the selected answer here probably. This is a good way to do it that works cleanly for both the "debug" end the "stop" arguments. (The part about using SC seems redundant when HandleCommandLine does the job, and can run debug. )Jodiejodo
It took me a while until I realized that not only you must put a space after the "=" sign, but also you CAN'T put a space before the sign.Prince
Thank you. This worked whereas I didn't manage to get the install argument from the accepted answer to work.Semifinalist
L
23

There are a couple alternatives for installing as a service virtually any Windows executable.

Method 1: Use instsrv and srvany from rktools.exe

For Windows Home Server or Windows Server 2003 (works with WinXP too), the Windows Server 2003 Resource Kit Tools comes with utilities that can be used in tandem for this, called instsrv.exe and srvany.exe. See this Microsoft KB article KB137890 for details on how to use these utils.

For Windows Home Server, there is a great user friendly wrapper for these utilities named aptly "Any Service Installer".

Method 2: Use ServiceInstaller for Windows NT

There is another alternative using ServiceInstaller for Windows NT (download-able here) with python instructions available. Contrary to the name, it works with both Windows 2000 and Windows XP as well. Here are some instructions for how to install a python script as a service.

Installing a Python script

Run ServiceInstaller to create a new service. (In this example, it is assumed that python is installed at 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

After installing, open the Control Panel's Services applet, select and start the PythonTest service.

After my initial answer, I noticed there were closely related Q&A already posted on SO. See also:

Can I run a Python script as a service (in Windows)? How?

How do I make Windows aware of a service I have written in Python?

Lachrymose answered 28/2, 2009 at 8:30 Comment(4)
I just noticed there are other similar Q&A already: #32904 #34828Lachrymose
Service Installer doesn't working on a 64 bit architecture so option 1 becomes the goto option.Spice
The above link to ServiceInstaller no longer works. I found it here: sites.google.com/site/conort/…Rowles
off note, I don't think NT would be necessarily "contrary" to the name, at least not in programmer-folk speech. It just refers to the "NT architecture", as opposed to the "NT brand". That said, according to talk on wikipedia this is up to debate, since "it's not an official Microsoft term", but there is nevertheless a tradition with this line of thinking.Reitman
H
6

pysc: Service Control Manager on Python

Example script to run as a service taken from 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()

Create and start 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'))

Stop and delete service

import pysc

service_name = 'test_xmlrpc_server'

pysc.stop(service_name)
pysc.delete(service_name)
pip install pysc
Hypersensitize answered 3/3, 2017 at 20:29 Comment(0)
H
6

nssm in python 3+

(I converted my .py file to .exe with pyinstaller)

nssm: as said before

  • run nssm install {ServiceName}
  • On NSSM´s console:

    path: path\to\your\program.exe

    Startup directory: path\to\your\ #same as the path but without your program.exe

    Arguments: empty

If you don't want to convert your project to .exe

  • create a .bat file with python {{your python.py file name}}
  • and set the path to the .bat file
Ham answered 4/6, 2019 at 21:42 Comment(1)
How to provide a virtual environment?Skedaddle
R
5

I started hosting as a service with pywin32.

Everything was well but I met the problem that service was not able to start within 30 seconds (default timeout for Windows) on system startup. It was critical for me because Windows startup took place simultaneous on several virtual machines hosted on one physical machine, and IO load was huge. Error messages were:

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.

I fought a lot with pywin, but ended up with using NSSM as it was proposed in this answer. It was very easy to migrate to it.

Regret answered 12/10, 2018 at 13:7 Comment(0)
D
3

This doesn't answer the original question, but might help other people that want to automatically start a Python script at Windows startup: Have a look at the Windows Task Scheduler instead, it is way easier if you just want to start a script after boot without all the service functionality of a Windows Service.

Create a new task, select "At startup" as trigger, "Start program" as action with "C:\Python39\python.exe" as the program (or wherever your python.exe is) and the full path to your script ("C:...\my_dir\xyz.py") as argument (you can use " if the path contains spaces). You can also select the path of your script (without the .py file, e.g. "C:...\my_dir") for "start in" if you use relative paths in your script, e.g. for logging.

Divide answered 15/6, 2022 at 7:51 Comment(0)
J
2

A complete pywin32 example using loop or subthread

After working on this on and off for a few days, here is the answer I would have wished to find, using pywin32 to keep it nice and self contained.

This is complete working code for one loop-based and one thread-based solution. It may work on both python 2 and 3, although I've only tested the latest version on 2.7 and Win7. The loop should be good for polling code, and the tread should work with more server-like code. It seems to work nicely with the waitress wsgi server that does not have a standard way to shut down gracefully.

I would also like to note that there seems to be loads of examples out there, like this that are almost useful, but in reality misleading, because they have cut and pasted other examples blindly. I could be wrong. but why create an event if you never wait for it?

That said I still feel I'm on somewhat shaky ground here, especially with regards to how clean the exit from the thread version is, but at least I believe there are nothing misleading here.

To run simply copy the code to a file and follow the instructions.

update:

Use a simple flag to terminate thread. The important bit is that "thread done" prints.
For a more elaborate example exiting from an uncooperative server thread see my post about the waitress wsgi server.

# 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()
Jodiejodo answered 26/1, 2020 at 21:52 Comment(2)
This is a program running in background without a console, where does the print command output the messages ?Accumulation
The prints are there to demonstrate that it works on the CLI. IIRC they will also be shown when running as a service with the debug flag.Jodiejodo
P
2

This answer is plagiarizer from several sources on StackOverflow - most of them above, but I've forgotten the others - sorry. It's simple and scripts run "as is". For releases you test you script, then copy it to the server and Stop/Start the associated service. And it should work for all scripting languages (Python, Perl, node.js), plus batch scripts such as GitBash, PowerShell, even old DOS bat scripts. pyGlue is the glue that sits between Windows Services and your script.

'''
A script to create a Windows Service, which, when started, will run an executable with the specified parameters.
Optionally, you can also specify a startup directory

To use this script you MUST define (in class Service)
1. A name for your service (short - preferably no spaces)
2. A display name for your service (the name visibile in Windows Services)
3. A description for your service (long details visible when you inspect the service in Windows Services)
4. The full path of the executable (usually C:/Python38/python.exe or C:WINDOWS/System32/WindowsPowerShell/v1.0/powershell.exe
5. The script which Python or PowerShell will run(or specify None if your executable is standalone - in which case you don't need pyGlue)
6. The startup directory (or specify None)
7. Any parameters for your script (or for your executable if you have no script)

NOTE: This does not make a portable script.
The associated '_svc_name.exe' in the dist folder will only work if the executable,
(and any optional startup directory) actually exist in those locations on the target system

Usage: 'pyGlue.exe [options] install|update|remove|start [...]|stop|restart [...]|debug [...]'
Options for 'install' and 'update' commands only:
        --username domain\\username : The Username the service is to run under
        --password password : The password for the username
        --startup [manual|auto|disabled|delayed] : How the service starts, default = manual
        --interactive : Allow the service to interact with the desktop.
        --perfmonini file: .ini file to use for registering performance monitor data
        --perfmondll file: .dll file to use when querying the service for performance data, default = perfmondata.dll
Options for 'start' and 'stop' commands only:
        --wait seconds: Wait for the service to actually start or stop.
                If you specify --wait with the 'stop' option, the service and all dependent services will be stopped,
                each waiting the specified period.
'''

# Import all the modules that make life easy
import servicemanager
import socket
import sys
import win32event
import win32service
import win32serviceutil
import win32evtlogutil
import os
from logging import Formatter, Handler
import logging
import subprocess


# Define the win32api class
class Service (win32serviceutil.ServiceFramework):
        # The following variable are edited by the build.sh script
        _svc_name_ = "TestService"
        _svc_display_name_ = "Test Service"
        _svc_description_ = "Test Running Python Scripts as a Service"
        service_exe = 'c:/Python27/python.exe'
        service_script = None
        service_params = []
        service_startDir = None

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

        # Configure logging to the WINDOWS Event logs
        def configure_logging(self):
                self.formatter = Formatter('%(message)s')
                self.handler = logHandler()
                self.handler.setFormatter(self.formatter)
                self.logger = logging.getLogger()
                self.logger.addHandler(self.handler)
                self.logger.setLevel(logging.INFO)

        # Stop the service
        def SvcStop(self):
                self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
                win32event.SetEvent(self.hWaitStop)

        # Run the service
        def SvcDoRun(self):
                self.main()

        # This is the service
        def main(self):

                # Log that we are starting
                servicemanager.LogMsg(servicemanager.EVENTLOG_INFORMATION_TYPE, servicemanager.PYS_SERVICE_STARTED,
                                                          (self._svc_name_, ''))

                # Fire off the real process that does the real work
                logging.info('%s - about to call Popen() to run %s %s %s', self._svc_name_, self.service_exe, self.service_script, self.service_params)
                self.process = subprocess.Popen([self.service_exe, self.service_script] + self.service_params, shell=False, cwd=self.service_startDir)
                logging.info('%s - started process %d', self._svc_name_, self.process.pid)

                # Wait until WINDOWS kills us - retrigger the wait for stop every 60 seconds
                rc = None
                while rc != win32event.WAIT_OBJECT_0:
                        rc = win32event.WaitForSingleObject(self.hWaitStop, (1 * 60 * 1000))

                # Shut down the real process and exit
                logging.info('%s - is terminating process %d', self._svc_name_, self.process.pid)
                self.process.terminate()
                logging.info('%s - is exiting', self._svc_name_)


class logHandler(Handler):
        '''
Emit a log record to the WINDOWS Event log
        '''

        def emit(self, record):
                servicemanager.LogInfoMsg(record.getMessage())


# The main code
if __name__ == '__main__':
        '''
Create a Windows Service, which, when started, will run an executable with the specified parameters.
        '''

        # Check that configuration contains valid values just in case this service has accidentally
        # been moved to a server where things are in different places
        if not os.path.isfile(Service.service_exe):
                print('Executable file({!s}) does not exist'.format(Service.service_exe), file=sys.stderr)
                sys.exit(0)
        if not os.access(Service.service_exe, os.X_OK):
                print('Executable file({!s}) is not executable'.format(Service.service_exe), file=sys.stderr)
                sys.exit(0)
        # Check that any optional startup directory exists
        if (Service.service_startDir is not None) and (not os.path.isdir(Service.service_startDir)):
                print('Start up directory({!s}) does not exist'.format(Service.service_startDir), file=sys.stderr)
                sys.exit(0)

        if len(sys.argv) == 1:
                servicemanager.Initialize()
                servicemanager.PrepareToHostSingle(Service)
                servicemanager.StartServiceCtrlDispatcher()
        else:
                # install/update/remove/start/stop/restart or debug the service
                # One of those command line options must be specified
                win32serviceutil.HandleCommandLine(Service)

Now there's a bit of editing and you don't want all your services called 'pyGlue'. So there's a script (build.sh) to plug in the bits and create a customized 'pyGlue' and create an '.exe'. It is this '.exe' which gets installed as a Windows Service. Once installed you can set it to run automatically.

#!/bin/sh
# This script build a Windows Service that will install/start/stop/remove a service that runs a script
# That is, executes Python to run a Python script, or PowerShell to run a PowerShell script, etc

if [ $# -lt 6 ]; then
        echo "Usage: build.sh Name Display Description Executable Script StartupDir [Params]..."
        exit 0
fi

name=$1
display=$2
desc=$3
exe=$4
script=$5
startDir=$6
shift; shift; shift; shift; shift; shift
params=
while [ $# -gt 0 ]; do
        if [ "${params}" != "" ]; then
                params="${params}, "
        fi
        params="${params}'$1'"
        shift
done

cat pyGlue.py | sed -e "s/pyGlue/${name}/g" | \
        sed -e "/_svc_name_ =/s?=.*?= '${name}'?" | \
        sed -e "/_svc_display_name_ =/s?=.*?= '${display}'?" | \
        sed -e "/_svc_description_ =/s?=.*?= '${desc}'?" | \
        sed -e "/service_exe =/s?=.*?= '$exe'?" | \
        sed -e "/service_script =/s?=.*?= '$script'?" | \
        sed -e "/service_params =/s?=.*?= [${params}]?" | \
        sed -e "/service_startDir =/s?=.*?= '${startDir}'?" > ${name}.py

cxfreeze ${name}.py --include-modules=win32timezone

Installation - copy the '.exe' the server and the script to the specified folder. Run the '.exe', as Administrator, with the 'install' option. Open Windows Services, as Adminstrator, and start you service. For upgrade, just copy the new version of the script and Stop/Start the service.

Now every server is different - different installations of Python, different folder structures. I maintain a folder for every server, with a copy of pyGlue.py and build.sh. And I create a 'serverBuild.sh' script for rebuilding all the service on that server.

# A script to build all the script based Services on this PC
sh build.sh AutoCode 'AutoCode Medical Documents' 'Autocode Medical Documents to SNOMED_CT and AIHW codes' C:/Python38/python.exe autocode.py C:/Users/russell/Documents/autocoding -S -T
Petes answered 1/11, 2020 at 22:22 Comment(0)
O
1

The accepted answer using win32serviceutil works but is complicated and makes debugging and changes harder. It is far easier to use NSSM (the Non-Sucking Service Manager). You write and comfortably debug a normal python program and when it finally works you use NSSM to install it as a service in less than a minute:

From an elevated (admin) command prompt you run nssm.exe install NameOfYourService and you fill-in these options:

  • path: (the path to python.exe e.g. C:\Python27\Python.exe)
  • Arguments: (the path to your python script, e.g. c:\path\to\program.py)

By the way, if your program prints useful messages that you want to keep in a log file NSSM can also handle this and a lot more for you.

Organza answered 10/11, 2018 at 15:52 Comment(2)
Yes, this is a duplicate of Adriano's answer. I upvoted that answer and tried to edit it but after the edits I was looking at a new answer.Organza
How to provide virtual environment?Skedaddle
S
-2

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

  1. Follow up the PySvc.py

  2. changing the dll folder

I know this is old but I was stuck on this forever. For me, this specific problem was solved by copying this file - pywintypes36.dll

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

To -> 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. changing the path to python folder by

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

  1. NET START PySvc
  2. NET STOP PySvc
Stockpile answered 20/3, 2020 at 14:15 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.