Search code examples
pythonwindowsservicecherrypy

CherryPy as a Windows Service - the example


I've seen quite a few people struggle with it - I'm having issues too. It would be a great help if someone could help to make the CherryPy Service example work. Explaining the issues would be greatly appreciated.

There is a CherryPy as a Windows Service example available at: CherryPy Wiki. I'm trying to make it work. Here's my code:

"""
The most basic (working) CherryPy 3.0 Windows service possible.
Requires Mark Hammond's pywin32 package.
"""

import cherrypy
import win32serviceutil
import win32service
import win32event
import servicemanager

class HelloWorld:
    """ Sample request handler class. """

    def index(self):
        return "Hello world!"
    index.exposed = True


class MyService(win32serviceutil.ServiceFramework):
    """NT Service."""

    _svc_name_ = "CherryPyService"
    _svc_display_name_ = "CherryPy Service"

    def __init__(self, args):
        win32serviceutil.ServiceFramework.__init__(self, args)
        # create an event that SvcDoRun can wait on and SvcStop
        # can set.
        self.stop_event = win32event.CreateEvent(None, 0, 0, None)

    def SvcDoRun(self):
        self.ReportServiceStatus(win32service.SERVICE_START_PENDING)
        servicemanager.LogMsg(servicemanager.EVENTLOG_INFORMATION_TYPE,
                              servicemanager.PYS_SERVICE_STARTED,
                              (self._svc_name_,''))

        cherrypy.tree.mount(HelloWorld(), '/')

        # in practice, you will want to specify a value for
        # log.error_file below or in your config file.  If you
        # use a config file, be sure to use an absolute path to
        # it, as you can't be assured what path your service
        # will run in.
        cherrypy.config.update({
            'global':{
                'engine.autoreload.on': False,
                'log.screen': False,
                'log.error_file': 'c:\\MG\\temp\\CherryPy_Sample_Service.log',
                'engine.SIGHUP': None,
                'engine.SIGTERM': None
                }
            })
        # set blocking=False so that start() does not block
        cherrypy.server.quickstart()
        cherrypy.engine.start(blocking=False)
        # now, block until our event is set...
        win32event.WaitForSingleObject(self.stop_event, win32event.INFINITE)
        self.ReportServiceStatus(win32service.SERVICE_RUNNING)

    def SvcStop(self):
        self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
        cherrypy.server.stop()
        win32event.SetEvent(self.stop_event)

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

The above differs from the linked example. I've added

  • self.ReportServiceStatus(win32service.SERVICE_START_PENDING) and
  • self.ReportServiceStatus(win32service.SERVICE_RUNNING) as first and last statement of SvcDoRun
  • 'log.error_file': 'c:\\MG\\temp\\CherryPy_Sample_Service.log', as the wiki instructs

Important remark: while it is possible to use the console python cherrypy_sample_service.py install to install the service, it is not possible to start it using python cherrypy_sample_service.py start command. The reason for this is, that service created this way will refer to python executable, which is not designed as a service.

Therefore for my further testing I've compiled the code using the following:

from cx_Freeze import setup, Executable

exe = Executable(
    script='cherrypy_sample_service.py'
)


build_options = {'includes': ['cherrypy', 'win32serviceutil', 'win32service', 'win32event', 'servicemanager']}

setup(
        name = "CherryPy Sample Service",
        version = "0.1",
        service = ["cherrypy_sample_service.py"],
        options = {"build_exe" : build_options},
        executables = [exe])

During the build I get the following warning:

Python27\App\lib\distutils\dist.py:267: UserWarning: Unknown distribution option: 'service'
warnings.warn(msg)

I've added this option as per the following stack problem answer.

Now I'm able to call cherrypy_sample_service install, cherrypy_sample_service remove and cherrypy_sample_service update. However trying to run the service (either from services or via cherrypy_sample_service start) results in the following error:

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

And I'm stuck. Log file does not even get created. Would you please help me to get the example running? I guess this would be also useful for other people struggling with related issues if we could have an example explained and running. Thanks!


Solution

  • Finally I've found the solution - I've downloaded and reinstalled pywin32... Everything works perfectly fine! :)

    Some final notes:

    • Service starts fine from Editor - compilation is not needed.
    • Adding 'log.error_file': 'c:\\somedir\cherypy_sample_service.log', to cherrypy.config.update helps to verify that service is up&running

    While trying to debug the problem I've found out that once win32serviceutil.HandleCommandLine calls the StartService and gets to this part of the code:

        try:
            win32service.StartService(hs, args)
        finally:
            win32service.CloseServiceHandle(hs)
    

    I was not able to step into win32service.StartService. I was not able to locate the file and that is why I've reinstalled pywin32.

    I could only wihs there was some error message - hope this will be helpful for others.