Marco Islas

Create win32 services using Python and py2exe

Hi, in this case I'm going to show you how to create simple services for the win32 platform using the powerfull Python programming language and the py2exe utility.

Writing Python applications under windows is quite simple, maybe you didn't use windows to develop your application (as I do most times), the code, what you want to do with your application is up to you, and I'm not goint to touch the programming techniques for developing windows applications, Just, create a simple -and useles- win32 service that will help you to uderstand how to create your very own windows service.

First, every single program that you create with python and compile with py2exe and does run well, may be a win32 service, it may be a program that fetches the RSS xml feed for islascruz.org, maybe is a more complex program to generate some files and then send them to another server, maybe is a simple program to remind you to sand up an walk 10 minutes.

Said that we don't really need to create a program thinking on it as a service, we will create a simple program and test it using the python interpreter, who needs to compile?, well, we need it, but just when we are sure that our program performs well.



 
#!/usr/bin/env python
import time
import thread
import os
import sys
 
class service_test:
        def __init__(self):
                thread.start_new(self.do_something, tuple())
                while True:
                        if getattr(sys,'stopservice', False):
                                sys.exit()
                        time.sleep(0.3)
       
        def do_something(self):
                '''
                Do something
                '
''
                while True:
                        fname ='C:\\\\test.txt'
                        f = open(fname , 'a')
                        f.write(time.time())
                        f.close()
                        time.sleep(1)
               
               
if __name__ == "__main__":
        tst = service_test()
 
 
Now that we have this program running in our windows environment, we can start thinking in converting into a service. First, I don't know you, but I haven't make Windows to load a python script and run it as a service. This is because Win32 use it's own method to start/stop the services. Then, we will need to create a wrapper for our program, this wrapper will interface with the windows service mananger. Doing it is quite simple, lets see it.

 
 
import win32serviceutil
import win32service
import win32event
import os
import sys
import time
 
sys.stopservice = "false"
 
def main():
        '''
        Modulo principal para windows
        '
''
        sys.path.insert(0,os.getcwd())
        import service_module
        a = service_module.service_test()
 
class ServiceLauncher(win32serviceutil.ServiceFramework):
        _svc_name_ = 'ServiceTest'
        _scv_display_name_ ='Servicio de pruebas'
        def __init__(self, args):
                win32serviceutil.ServiceFramework.__init__(self, args)
                self.hWaitStop = win32event.CreateEvent(None, 0, 0, None)
       
        def SvcStop(self):
                self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
                sys.stopservice = "true"
                win32event.SetEvent(self.hWaitStop)
 
        def SvcDoRun(self):
                main()
 
As you can see in this piece of code, we have a class called ServiceLauncher, actually it doesn't really matter if you name it as you want, but, we need some variables that starts with "_svc_" and the important is _svn_name, this variable should have the name of our service, the very same name of the executable that we are going to compile. The __init__ method initialize our service.

Then, we have two methods. one SvcStop, this method will be called when we want to stop the service in the service mananger. As our program is not running here, is safe to communicate with it by some method, I use a variable stored in "sys" which is "pooled" on the main program to know when it's time to stop the program.

THe method SvcDoRun is quite explicit, it starts the program. Now, you guess that when you start your service must be doing something, even sleeping, because, if it stops, then an error message will be saved in the Windows logs.

Ok, we have our program and our wrapper class, but, did I said that Windows cannot load a python scritp as a service?. Well, we need to compile it. here is when py2exe comes in.

Py2Exe will help us to convert those scripts in a binary for windows, a simple binary for execution or a Windows service :-D. I use this script to create my windows services, py2exe makes use of the distutils and hence a setup.py script must be used to compile the our scripts. check the next code that will help you to create console programs and win32 services.

 
import sys
buildservice = True
if '--no-service' in sys.argv[1:]:
        buildservice = False
        sys.argv = [k for k in sys.argv if k != '--no-service']
        print sys.argv
       
from distutils.core import setup
import os
import py2exe
import glob
import shutil
 
sys.path.insert(0,os.getcwd())
 
def getFiles(dir):
        """
        Retorna una tupla de tuplas del directorio
        """

        # dig looking for files
        a= os.walk(dir)
        b = True
        filenames = []
 
        while (b):
                try:
                        (dirpath, dirnames, files) = a.next()
                        filenames.append([dirpath, tuple(files)])
                except:
                        b = False
        return filenames
 
DESCRIPTION = 'Servicio de pruebas'
NAME = 'servicetest'
 
 
class Target:
        def __init__(self,**kw):
                        self.__dict__.update(kw)
                        self.version        = "1.00.00"
                        self.compay_name    = "islascruz.org"
                        self.copyright      = "(c) 2009, Marco Islas"
                        self.name           = NAME
                        self.description    = DESCRIPTION
 
my_com_server_target = Target(
                description    = DESCRIPTION,
                service = ["service_module"],
                modules = ["service_module"],
                create_exe = True,
                create_dll = True)
 
if not buildservice:
        print 'Compilando como ejecutable de windows...'
        setup(
            name = NAME ,
            description = DESCRIPTION,
            version = '1.00.00',
            console = ['service_module.py'],
                zipfile=None,
                options = {
                                "py2exe":{"packages":"encodings",
                                        "includes":"win32com,win32service,win32serviceutil,win32event",
                                        "optimize": '2'
                                        },
                                },
        )
else:
        print 'Compilando como servicio de windows...'
        setup(
            name = NAME,
            description = DESCRIPTION,
            version = '1.00.00',
                service = [{'modules':["ServiceLauncher"], 'cmdline':'pywin32'}],
                zipfile=None,
                options = {
                                "py2exe":{"packages":"encodings",
                                        "includes":"win32com,win32service,win32serviceutil,win32event",
                                        "optimize": '2'
                                        },
                                },
        )
 
Maybe you want to read the distutils documentation to understand it. Once you have your binary, you can use it to install it on the win32 services list, just run your program with the "-install" parameter and you are done!.

I hope this article and the code above help you, If you have a question or some comment with this, plese use the comment form in the post associated to this article.