17 August 2010 12 Comments

Running Python script(s) as a Windows Service – Keep your Python Mojo Engines Running while you Sleep!

Now any Python duct-taper integrate-anything junkie like me has a need to schedule their things (in production) every once in awhile. Usually this is not a problem - Unix / Linux cron jobs handle this nicely - but for a client or job that runs on a Windows server - the built-in "Scheduled Tasks" just never really cut it for me - in fact, I've always though that it was pretty Mickey-Mouse and not super reliable (just my opinion - don't flame me, you wily Windows Dudes - I know that there are 1,000 ways to skin a cat, here's mine!)...

It honestly gave me the creeps to put production stuff on there that NEEDED to be run every X minutes, hours, etc. Besides, making a batch file to fire off a python scripts feels so... well, unpolished - and pretty much of a lazy hack that made it into prod...

Disclaimer: Hey, I love hacks - they make the IT world go around, but I don't like delivering shit to clients that have loose feeling triggers like that. Especially, if some smart ass IT dickface (who COULDN'T pull off what I did, or else I wouldn't have been paid to do it) is going to dig into them one day and bad mouth me about it (rightfully so).

Besides, I'll find you! :)

Enough blather - here is the whole script. Modify and distribute where you see fit - parts of it were written by others that I can't recall (so if its you - please accept my apology and an expired gift card).

### Run Python scripts as a service example (ryrobes.com)
### Usage : python aservice.py install (or / then start, stop, remove)

import win32service
import win32serviceutil
import win32api
import win32con
import win32event
import win32evtlogutil
import os, sys, string, time

class aservice(win32serviceutil.ServiceFramework):
   
   _svc_name_ = "MyServiceShortName"
   _svc_display_name_ = "My Serivce Long Fancy Name!"
   _svc_description_ = "THis is what my crazy little service does - aka a DESCRIPTION! WHoa!"
         
   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)
           win32event.SetEvent(self.hWaitStop)                    
         
   def SvcDoRun(self):
      import servicemanager      
      servicemanager.LogMsg(servicemanager.EVENTLOG_INFORMATION_TYPE,servicemanager.PYS_SERVICE_STARTED,(self._svc_name_, ''))
     
      #self.timeout = 640000    #640 seconds / 10 minutes (value is in milliseconds)
      self.timeout = 120000     #120 seconds / 2 minutes
      # This is how long the service will wait to run / refresh itself (see script below)

      while 1:
         # Wait for service stop signal, if I timeout, loop again
         rc = win32event.WaitForSingleObject(self.hWaitStop, self.timeout)
         # Check to see if self.hWaitStop happened
         if rc == win32event.WAIT_OBJECT_0:
            # Stop signal encountered
            servicemanager.LogInfoMsg("SomeShortNameVersion - STOPPED!")  #For Event Log
            break
         else:

                 #Ok, here's the real money shot right here.
                 #[actual service code between rests]
                 try:
                     file_path = "C:\whereever\my_REAL_py_work_to_be_done.py"
                     execfile(file_path)             #Execute the script

                     inc_file_path2 = "C:\whereever\MORE_REAL_py_work_to_be_done.py"
                     execfile(inc_file_path2)        #Execute the script
                 except:
                     pass
                 #[actual service code between rests]


def ctrlHandler(ctrlType):
   return True
                 
if __name__ == '__main__':  
   win32api.SetConsoleCtrlHandler(ctrlHandler, True)  
   win32serviceutil.HandleCommandLine(aservice)

# Done! Lets go out and get some dinner, bitches!

As you can see above between the [actual service code between rests] text - THAT'S the real meat of this script - since that is the ACTION that will be executed within the service with each iteration (regardless of the timing).

So it doesn't matter if you're:

  • Running some Python ETL processes
  • Scraping some data off the web
  • Sending emails and reading IMAP accounts
  • Updating a Data warehouse
  • EMail your Grandmother the latest news from Blabbermouth.net

Hell, it can be and do virtually anything! Hey, its YOUR service after all.

Don't want to Cut-N-Paste? I don't blame you - download the whole she-bang here.

Questions, Comments, Corrections? Comment me below!

(especially corrections - sometimes things get mangled in cut-n-paste)

  • priti

    Not able to execute this code.
    When i run this code like:
    python aservice.py install –> It show following messsage on command prompt

    Usage: ‘aservice.py [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] : 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.

    Please help me to execute this code(create window service)

  • Pingback: Need to run a Python program as a Windows Service? | Baggers on the Web

  • Pingback: Simple web service in python – pywin32 : Note the Code

  • Pingback: Some Metallica Setlist Analysis using Tableau Public Visualization Software and some Python Hacking | Ryan Robitaille

  • http://www.facebook.com/people/Meester-Taco/100000072839431 Meester Taco

     Good shit. If I was a man of questionable morals (and I am!) I would jack your website’s tag line and use it as my own – but I won’t since I like the cut of your jib.

  • Constient

    Thanks for the info :)
    for more info : visit http://www.constient.com

  • hvalot

    Hello,

    I tried your script and installation is OK but when i try to start the service I get the following error :

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

    Running on Windows 7 Pro X64
    Python 2.6 pywin32 214

    Can you help me

    • http://ryrobes.com/ Ryan Robitaille

      Seems like it might be a service problem. Does the service start well on its own when you do it manually?

  • http://ryrobes.com/ Ryan Robitaille

    Note: Since this is a popular post (and it’s damn old – like 3 years) I’m going to check that it works on Win 7 and Win 8, and update the script where needed.

  • cappy

    Should execfile be startfile ? couldn’t find execfile in any imported module.

  • Edward Chou

    I ran it well, but I need to change execfile into os.system. Subprocess didn’t work either.

    My Env:
    Win 7 32bit
    Portable Python 2.7.5

  • The Big Grey Sasquatch

    I had a problem where when I launched my “service” using “execfile”, then my “service” would spawn sub processes (restarting things), but everything would exit… so no happy ending. I worked around this by replacing the “execfile” line with one like this:

    os.spawnl(os.P_NOWAIT, “C:\Python27\python.exe”,
    “C:\Python27\python.exe”, file_path)