[Checkins] SVN: z3c.recipe.winservice/trunk/ New feature: ``runscript`` and ``parameters``.
Adam Groszer
agroszer at gmail.com
Tue Oct 19 08:26:53 EDT 2010
Log message for revision 117754:
New feature: ``runscript`` and ``parameters``.
More docs, tests. Some fixes. See CHANGES.txt
Changed:
U z3c.recipe.winservice/trunk/CHANGES.txt
U z3c.recipe.winservice/trunk/README.txt
U z3c.recipe.winservice/trunk/src/z3c/recipe/winservice/README.txt
U z3c.recipe.winservice/trunk/src/z3c/recipe/winservice/service.py
U z3c.recipe.winservice/trunk/src/z3c/recipe/winservice/tests.py
U z3c.recipe.winservice/trunk/src/z3c/recipe/winservice/winservice.in
-=-
Modified: z3c.recipe.winservice/trunk/CHANGES.txt
===================================================================
--- z3c.recipe.winservice/trunk/CHANGES.txt 2010-10-19 08:53:56 UTC (rev 117753)
+++ z3c.recipe.winservice/trunk/CHANGES.txt 2010-10-19 12:26:52 UTC (rev 117754)
@@ -5,9 +5,20 @@
Version 0.7.0 (unreleased)
--------------------------
+- New feature: ``runscript`` and ``parameters``.
+ With those, this recipe is now capable of making any executable or
+ python script a service.
+
+- Added more docs and tests.
+
+- Remove compiled winservice.pyc and pyo if they exist.
+
+- Most folder calculation moved to the recipe installer.
+ winservice.py gets constants, that makes it easier to see what happens.
+
- Updated tests to be more tolerant to different Python versions.
-- Using Python's ``doctest`` module instead of depreacted
+- Using Python's ``doctest`` module instead of deprecated
``zope.testing.doctest``.
Version 0.6.3 (2009-11-02)
Modified: z3c.recipe.winservice/trunk/README.txt
===================================================================
--- z3c.recipe.winservice/trunk/README.txt 2010-10-19 08:53:56 UTC (rev 117753)
+++ z3c.recipe.winservice/trunk/README.txt 2010-10-19 12:26:52 UTC (rev 117754)
@@ -1,2 +1,49 @@
-The ``z3c.recipe.winservice:service`` generates a service scripts and
-configuration files for starting a egg based Zope 3 setup as a windows service.
+=====================
+z3c.recipe.winservice
+=====================
+
+This recipe offers windows service installation support.
+
+The 'service' recipe installes the required scripts and files which can be
+used to install a windows service.
+
+Using the ``runscript`` option it is able to make any executable a service.
+
+
+Options
+*******
+
+The 'service' recipe accepts the following options:
+
+name
+ The windows service name option.
+
+description
+ The windows service description option.
+
+runzope
+ The script name which gets run by the winservice.
+ If the script name contains any path, it's taken as it is, otherwise the
+ buildout bin folder is prepended. winservice will check on install if this
+ script exists.
+ The install takes care of adding ``-script.py`` if necessary.
+ This script can get setup for exmaple with the z3c.recipe.dev.app recipe.
+
+runscript
+ The script (.py) or executable (.exe) name to be run by the winservice.
+ The value will get NO treatment, you need to pass an exact specification.
+ winservice will check on install if this script/exe exists.
+ Use this option OR runzope, but never both.
+
+parameters
+ This value will get passed to the script (runzope or runscript) as a
+ parameter. The value will get NO treatment, you need to take care of adding
+ any quotes if necessary.
+
+debug
+ Adding this option to the recipe wraps the whole script to run into
+ a catch all except that logs the exception to the windows event log.
+ This is good for debugging weird exceptions that occur before the Zope
+ logging system is in place.
+ This does not work if runscript is an executable.
+
Modified: z3c.recipe.winservice/trunk/src/z3c/recipe/winservice/README.txt
===================================================================
--- z3c.recipe.winservice/trunk/src/z3c/recipe/winservice/README.txt 2010-10-19 08:53:56 UTC (rev 117753)
+++ z3c.recipe.winservice/trunk/src/z3c/recipe/winservice/README.txt 2010-10-19 12:26:52 UTC (rev 117754)
@@ -1,420 +1,659 @@
=====================
-Z3 development recipe
+z3c.recipe.winservice
=====================
-z3c.recipe.winservice
----------------------
+This recipe offers windows service installation support.
-This Zope 3 recipes offers windows service installation support.
-The 'service' recipe installes the required scripts and files whihc can be
-used for install a windwos service.
-
-
-Options
-*******
-
-The 'service' recipe accepts the following options:
-
-name
- The windows service name option.
-
-description
- The windows service description option.
-
-runzope
- The script name which get used by the winservice. This script must exist in
- the bin folder and before we run this recipe exist. This script can get setup
- with the z3c.recipe.dev.app recipe.
-
-
Test
****
Lets define some (bogus) eggs that we can use in our application:
- >>> mkdir('demo1')
- >>> write('demo1', 'setup.py',
- ... '''
- ... from setuptools import setup
- ... setup(name = 'demo1')
- ... ''')
+ >>> mkdir('demo1')
+ >>> write('demo1', 'setup.py',
+ ... '''
+ ... from setuptools import setup
+ ... setup(name = 'demo1')
+ ... ''')
- >>> mkdir('demo2')
- >>> write('demo2', 'setup.py',
- ... '''
- ... from setuptools import setup
- ... setup(name = 'demo2', install_requires='demo1')
- ... ''')
+ >>> mkdir('demo2')
+ >>> write('demo2', 'setup.py',
+ ... '''
+ ... from setuptools import setup
+ ... setup(name = 'demo2', install_requires='demo1')
+ ... ''')
We also need to setup an application. This is normaly done with the app recipe
defined in z3c.recipe.dev:
- >>> write('bin', 'app-script.py',
- ... '''
- ... dummy start script
- ... ''')
+ >>> write('bin', 'app-script.py',
+ ... '''
+ ... dummy start script
+ ... ''')
Now check if the setup was correct. This is true if we have already the
buildout files and our fake app-script.py installed:
- >>> ls('bin')
- - app-script.py
- - buildout-script.py
- - buildout.exe
+ >>> ls('bin')
+ - app-script.py
+ - buildout-script.py
+ - buildout.exe
We'll create a `buildout.cfg` file that defines our winservice configuration:
- >>> write('buildout.cfg',
- ... '''
- ... [buildout]
- ... develop = demo1 demo2
- ... parts = winservice
- ...
- ... [winservice]
- ... recipe = z3c.recipe.winservice:service
- ... name = Zope 3 Windows Service
- ... description = Zope 3 Windows Service description
- ... runzope = app
- ...
- ... ''' % globals())
+ >>> write('buildout.cfg',
+ ... '''
+ ... [buildout]
+ ... develop = demo1 demo2
+ ... parts = winservice
+ ...
+ ... [winservice]
+ ... recipe = z3c.recipe.winservice:service
+ ... name = Zope 3 Windows Service
+ ... description = Zope 3 Windows Service description
+ ... runzope = app
+ ...
+ ... ''' % globals())
Now, Let's run the buildout and see what we get:
- >>> print system(join('bin', 'buildout')),
- Develop: '/sample-buildout/demo1'
- Develop: '/sample-buildout/demo2'
- Installing winservice.
+ >>> print system(join('bin', 'buildout')),
+ Develop: '/sample-buildout/demo1'
+ Develop: '/sample-buildout/demo2'
+ Installing winservice.
The bin folder contains the windows service installer script:
- >>> ls('bin')
- - app-script.py
- - buildout-script.py
- - buildout.exe
- - winservice.py
+ >>> ls('bin')
+ - app-script.py
+ - buildout-script.py
+ - buildout.exe
+ - winservice.py
The winservice.py contains the service setup for our zope windows service:
- >>> cat('bin', 'winservice.py')
- ##############################################################################
- #
- # Copyright (c) 2008 Zope Foundation and Contributors.
- # All Rights Reserved.
- #
- # This software is subject to the provisions of the Zope Public License,
- # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
- # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
- # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
- # FOR A PARTICULAR PURPOSE.
- #
- ##############################################################################
- """A Zope Windows NT service frontend.
- <BLANKLINE>
- Usage:
- <BLANKLINE>
- Installation
- <BLANKLINE>
- You can manually install, uninstall the service from the commandline.
- <BLANKLINE>
- python bin\winservice.py [options] install|update|remove|start [...]
- |stop|restart [...]|debug [...]
- <BLANKLINE>
- Options for 'install' and 'update' commands only:
- <BLANKLINE>
- --username domain\username : The Username the service is to run
- under
- <BLANKLINE>
- --password password : The password for the username
- <BLANKLINE>
- --startup [manual|auto|disabled] : How the service starts,
- default = manual
- <BLANKLINE>
- Commands
- <BLANKLINE>
- install : Installs the service
- <BLANKLINE>
- update : Updates the service, use this when you change
- the service class implementation
- <BLANKLINE>
- remove : Removes the service
- <BLANKLINE>
- start : Starts the service, this can also be done from the
- services control panel
- <BLANKLINE>
- stop : Stops the service, this can also be done from the
- services control panel
- <BLANKLINE>
- restart : Restarts the service
- <BLANKLINE>
- debug : Runs the service in debug mode
- <BLANKLINE>
- You can view the usage options by running ntservice.py without any
- arguments.
- <BLANKLINE>
- Note: you may have to register the Python service program first,
- <BLANKLINE>
- win32\PythonService.exe /register
- <BLANKLINE>
- Starting Zope
- <BLANKLINE>
- Start Zope by clicking the 'start' button in the services control
- panel. You can set Zope to automatically start at boot time by
- choosing 'Auto' startup by clicking the 'statup' button.
- <BLANKLINE>
- Stopping Zope
- <BLANKLINE>
- Stop Zope by clicking the 'stop' button in the services control
- panel. You can also stop Zope through the web by going to the
- Zope control panel and by clicking 'Shutdown'.
- <BLANKLINE>
- Event logging
- <BLANKLINE>
- Zope events are logged to the NT application event log. Use the
- event viewer to keep track of Zope events.
- <BLANKLINE>
- """
- <BLANKLINE>
- import sys, os, time
- import pywintypes
- import win32serviceutil
- import win32service
- import win32event
- import win32process
- <BLANKLINE>
- # these are replacements from winservice recipe
- PYTHON = r'...'
- PYTHONDIR = os.path.split(PYTHON)[0]
- PYTHONSERVICE_EXE = r'%s\Lib\site-packages\win32\pythonservice.exe' % PYTHONDIR
- TOSTART = r'/sample-buildout/bin/app-script.py'
- SERVICE_NAME = '...sample_buildout_bin_app_script_py'
- SERVICE_DISPLAY_NAME = r'Zope 3 Windows Service'
- SERVICE_DESCRIPTION = r'Zope 3 Windows Service description'
- INSTANCE_HOME = os.path.dirname(os.path.dirname(TOSTART))
- <BLANKLINE>
- <BLANKLINE>
- # the max seconds we're allowed to spend backing off
- BACKOFF_MAX = 300
- # if the process runs successfully for more than BACKOFF_CLEAR_TIME
- # seconds, we reset the backoff stats to their initial values
- BACKOFF_CLEAR_TIME = 30
- # the initial backoff interval (the amount of time we wait to restart
- # a dead process)
- BACKOFF_INITIAL_INTERVAL = 5
- <BLANKLINE>
- <BLANKLINE>
- class NullOutput:
- """A stdout / stderr replacement that discards everything."""
- <BLANKLINE>
- def noop(self, *args, **kw):
- pass
- <BLANKLINE>
- write = writelines = close = seek = flush = truncate = noop
- <BLANKLINE>
- def __iter__(self):
- return self
- <BLANKLINE>
- def next(self):
- raise StopIteration
- <BLANKLINE>
- def isatty(self):
- return False
- <BLANKLINE>
- def tell(self):
- return 0
- <BLANKLINE>
- def read(self, *args, **kw):
- return ''
- <BLANKLINE>
- readline = read
- <BLANKLINE>
- def readlines(self, *args, **kw):
- return []
- <BLANKLINE>
- <BLANKLINE>
- class Zope3Service(win32serviceutil.ServiceFramework):
- """ A class representing a Windows NT service that can manage an
- instance-home-based Zope/ZEO/ZRS processes """
- <BLANKLINE>
- # The PythonService model requires that an actual on-disk class declaration
- # represent a single service. Thus, the below definition of start_cmd,
- # must be overridden in a subclass in a file within the instance home for
- # each instance. The below-defined start_cmd (and _svc_display_name_
- # and _svc_name_) are just examples.
- <BLANKLINE>
- _svc_name_ = SERVICE_NAME
- _svc_display_name_ = SERVICE_DISPLAY_NAME
- _svc_description_ = SERVICE_DESCRIPTION
- <BLANKLINE>
- _exe_name_ = PYTHONSERVICE_EXE
- start_cmd = ''
- <BLANKLINE>
- def __init__(self, args):
- if not os.path.exists(PYTHON):
- raise OSError("%s does not exist" % PYTHON)
- if not os.path.exists(TOSTART):
- raise OSError("%s does not exist" % TOSTART)
- <BLANKLINE>
- self.start_cmd = '"%s" "%s"' % (PYTHON, TOSTART)
- <BLANKLINE>
- win32serviceutil.ServiceFramework.__init__(self, args)
- # Create an event which we will use to wait on.
- # The "service stop" request will set this event.
- self.hWaitStop = win32event.CreateEvent(None, 0, 0, None)
- self.redirectOutput()
- <BLANKLINE>
- def redirectOutput(self):
- sys.stdout.close()
- sys.stderr.close()
- sys.stdout = NullOutput()
- sys.stderr = NullOutput()
- <BLANKLINE>
- def SvcStop(self):
- # Before we do anything, tell the SCM we are starting the stop process.
- self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
- <BLANKLINE>
- # TODO: This TerminateProcess call doesn't make much sense: it's
- # doing a hard kill _first_, never giving the process a chance to
- # shut down cleanly. Compare to current Zope2 service code, which
- # uses Windows events to give the process a chance to shut down
- # cleanly, doing a hard kill only if that doesn't succeed.
- <BLANKLINE>
- # stop the process if necessary
- try:
- win32process.TerminateProcess(self.hZope, 0)
- except pywintypes.error:
- # the process may already have been terminated
- pass
- # And set my event.
- win32event.SetEvent(self.hWaitStop)
- <BLANKLINE>
- # SvcStop only gets triggered when the user explictly stops (or restarts)
- # the service. To shut the service down cleanly when Windows is shutting
- # down, we also need to hook SvcShutdown.
- SvcShutdown = SvcStop
- <BLANKLINE>
- def createProcess(self, cmd):
- #need to set current dir to INSTANCE_HOME otherwise pkg_resources will
- #be pissed (in a combination with paster)
- return win32process.CreateProcess(
- None, cmd, None, None, 0, 0, None,
- INSTANCE_HOME,
- win32process.STARTUPINFO())
- <BLANKLINE>
- def SvcDoRun(self):
- # indicate to Zope that the process is daemon managed (restartable)
- os.environ['ZMANAGED'] = '1'
- <BLANKLINE>
- # daemon behavior: we want to to restart the process if it
- # dies, but if it dies too many times, we need to give up.
- <BLANKLINE>
- # we use a simple backoff algorithm to determine whether
- # we should try to restart a dead process: for each
- # time the process dies unexpectedly, we wait some number of
- # seconds to restart it, as determined by the backoff interval,
- # which doubles each time the process dies. if we exceed
- # BACKOFF_MAX seconds in cumulative backoff time, we give up.
- # at any time if we successfully run the process for more thab
- # BACKOFF_CLEAR_TIME seconds, the backoff stats are reset.
- <BLANKLINE>
- # the initial number of seconds between process start attempts
- backoff_interval = BACKOFF_INITIAL_INTERVAL
- # the cumulative backoff seconds counter
- backoff_cumulative = 0
- <BLANKLINE>
- import servicemanager
- <BLANKLINE>
- # log a service started message
- servicemanager.LogMsg(
- servicemanager.EVENTLOG_INFORMATION_TYPE,
- servicemanager.PYS_SERVICE_STARTED,
- (self._svc_name_, ' (%s)' % self._svc_display_name_))
- <BLANKLINE>
- while 1:
- start_time = time.time()
- info = self.createProcess(self.start_cmd)
- self.hZope = info[0] # the pid
- if backoff_interval > BACKOFF_INITIAL_INTERVAL:
- # if we're in a backoff state, log a message about
- # starting a new process
- servicemanager.LogInfoMsg(
- '%s (%s): recovering from died process, new process '
- 'started' % (self._svc_name_, self._svc_display_name_)
- )
- rc = win32event.WaitForMultipleObjects(
- (self.hWaitStop, self.hZope), 0, win32event.INFINITE)
- if rc == win32event.WAIT_OBJECT_0:
- # user sent a stop service request
- self.SvcStop()
- break
- else:
- # user did not send a service stop request, but
- # the process died; this may be an error condition
- status = win32process.GetExitCodeProcess(self.hZope)
- if status == 0:
- # the user shut the process down from the web
- # interface (or it otherwise exited cleanly)
- break
- else:
- # this was an abormal shutdown. if we can, we want to
- # restart the process but if it seems hopeless,
- # don't restart an infinite number of times.
- if backoff_cumulative > BACKOFF_MAX:
- # it's hopeless
- servicemanager.LogErrorMsg(
- '%s (%s): process could not be restarted due to max '
- 'restart attempts exceeded' % (
- self._svc_display_name_, self._svc_name_
- ))
- self.SvcStop()
- break
- servicemanager.LogWarningMsg(
- '%s (%s): process died unexpectedly. Will attempt '
- 'restart after %s seconds.' % (
- self._svc_name_, self._svc_display_name_,
- backoff_interval
- )
- )
- # if BACKOFF_CLEAR_TIME seconds have elapsed since we last
- # started the process, reset the backoff interval
- # and the cumulative backoff time to their original
- # states
- if time.time() - start_time > BACKOFF_CLEAR_TIME:
- backoff_interval = BACKOFF_INITIAL_INTERVAL
- backoff_cumulative = 0
- # we sleep for the backoff interval. since this is async
- # code, it would be better done by sending and
- # catching a timed event (a service
- # stop request will need to wait for us to stop sleeping),
- # but this works well enough for me.
- time.sleep(backoff_interval)
- # update backoff_cumulative with the time we spent
- # backing off.
- backoff_cumulative = backoff_cumulative + backoff_interval
- # bump the backoff interval up by 2* the last interval
- backoff_interval = backoff_interval * 2
- <BLANKLINE>
- # loop and try to restart the process
- <BLANKLINE>
- # log a service stopped message
- servicemanager.LogMsg(
- servicemanager.EVENTLOG_INFORMATION_TYPE,
- servicemanager.PYS_SERVICE_STOPPED,
- (self._svc_name_, ' (%s) ' % self._svc_display_name_))
- <BLANKLINE>
- <BLANKLINE>
- if __name__ == '__main__':
- import win32serviceutil
- if os.path.exists(PYTHONSERVICE_EXE):
- # This ensures that pythonservice.exe is registered...
- os.system('"%s" -register' % PYTHONSERVICE_EXE)
- win32serviceutil.HandleCommandLine(Zope3Service)
+ >>> cat('bin', 'winservice.py')
+ ##############################################################################
+ #
+ # Copyright (c) 2008 Zope Foundation and Contributors.
+ # All Rights Reserved.
+ #
+ # This software is subject to the provisions of the Zope Public License,
+ # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+ # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+ # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+ # FOR A PARTICULAR PURPOSE.
+ #
+ ##############################################################################
+ """A Zope Windows NT service frontend.
+ <BLANKLINE>
+ Usage:
+ <BLANKLINE>
+ Installation
+ <BLANKLINE>
+ You can manually install, uninstall the service from the commandline.
+ <BLANKLINE>
+ python bin\winservice.py [options] install|update|remove|start [...]
+ |stop|restart [...]|debug [...]
+ <BLANKLINE>
+ Options for 'install' and 'update' commands only:
+ <BLANKLINE>
+ --username domain\username : The Username the service is to run
+ under
+ <BLANKLINE>
+ --password password : The password for the username
+ <BLANKLINE>
+ --startup [manual|auto|disabled] : How the service starts,
+ default = manual
+ <BLANKLINE>
+ Commands
+ <BLANKLINE>
+ install : Installs the service
+ <BLANKLINE>
+ update : Updates the service, use this when you change
+ the service class implementation
+ <BLANKLINE>
+ remove : Removes the service
+ <BLANKLINE>
+ start : Starts the service, this can also be done from the
+ services control panel
+ <BLANKLINE>
+ stop : Stops the service, this can also be done from the
+ services control panel
+ <BLANKLINE>
+ restart : Restarts the service
+ <BLANKLINE>
+ debug : Runs the service in debug mode
+ <BLANKLINE>
+ You can view the usage options by running ntservice.py without any
+ arguments.
+ <BLANKLINE>
+ Note: you may have to register the Python service program first,
+ <BLANKLINE>
+ win32\PythonService.exe /register
+ <BLANKLINE>
+ Starting Zope
+ <BLANKLINE>
+ Start Zope by clicking the 'start' button in the services control
+ panel. You can set Zope to automatically start at boot time by
+ choosing 'Auto' startup by clicking the 'statup' button.
+ <BLANKLINE>
+ Stopping Zope
+ <BLANKLINE>
+ Stop Zope by clicking the 'stop' button in the services control
+ panel. You can also stop Zope through the web by going to the
+ Zope control panel and by clicking 'Shutdown'.
+ <BLANKLINE>
+ Event logging
+ <BLANKLINE>
+ Zope events are logged to the NT application event log. Use the
+ event viewer to keep track of Zope events.
+ <BLANKLINE>
+ """
+ <BLANKLINE>
+ import sys, os, time
+ import pywintypes
+ import win32serviceutil
+ import win32service
+ import win32event
+ import win32process
+ <BLANKLINE>
+ # these are replacements from winservice recipe
+ PYTHONSERVICE_EXE = r'...\Lib\site-packages\win32\pythonservice.exe'
+ PYTHON = r'...\python.exe'
+ TOSTART = r'/sample-buildout/bin/app-script.py'
+ PARAMETERS = r''
+ SERVICE_NAME = '...sample_buildout_bin_app_script_py'
+ SERVICE_DISPLAY_NAME = r'Zope 3 Windows Service'
+ SERVICE_DESCRIPTION = r'Zope 3 Windows Service description'
+ INSTANCE_HOME = r'/sample-buildout'
+ <BLANKLINE>
+ <BLANKLINE>
+ # the max seconds we're allowed to spend backing off
+ BACKOFF_MAX = 300
+ # if the process runs successfully for more than BACKOFF_CLEAR_TIME
+ # seconds, we reset the backoff stats to their initial values
+ BACKOFF_CLEAR_TIME = 30
+ # the initial backoff interval (the amount of time we wait to restart
+ # a dead process)
+ BACKOFF_INITIAL_INTERVAL = 5
+ <BLANKLINE>
+ <BLANKLINE>
+ class NullOutput:
+ """A stdout / stderr replacement that discards everything."""
+ <BLANKLINE>
+ def noop(self, *args, **kw):
+ pass
+ <BLANKLINE>
+ write = writelines = close = seek = flush = truncate = noop
+ <BLANKLINE>
+ def __iter__(self):
+ return self
+ <BLANKLINE>
+ def next(self):
+ raise StopIteration
+ <BLANKLINE>
+ def isatty(self):
+ return False
+ <BLANKLINE>
+ def tell(self):
+ return 0
+ <BLANKLINE>
+ def read(self, *args, **kw):
+ return ''
+ <BLANKLINE>
+ readline = read
+ <BLANKLINE>
+ def readlines(self, *args, **kw):
+ return []
+ <BLANKLINE>
+ <BLANKLINE>
+ class Zope3Service(win32serviceutil.ServiceFramework):
+ """ A class representing a Windows NT service that can manage an
+ instance-home-based Zope/ZEO/ZRS processes """
+ <BLANKLINE>
+ # The PythonService model requires that an actual on-disk class declaration
+ # represent a single service. Thus, the below definition of start_cmd,
+ # must be overridden in a subclass in a file within the instance home for
+ # each instance. The below-defined start_cmd (and _svc_display_name_
+ # and _svc_name_) are just examples.
+ <BLANKLINE>
+ _svc_name_ = SERVICE_NAME
+ _svc_display_name_ = SERVICE_DISPLAY_NAME
+ _svc_description_ = SERVICE_DESCRIPTION
+ <BLANKLINE>
+ _exe_name_ = PYTHONSERVICE_EXE
+ start_cmd = ''
+ <BLANKLINE>
+ def __init__(self, args):
+ cmdLine = ''
+ if PYTHON:
+ if not os.path.exists(PYTHON):
+ raise OSError("%s does not exist" % PYTHON)
+ cmdLine += '"%s" ' % PYTHON
+ if not os.path.exists(TOSTART):
+ raise OSError("%s does not exist" % TOSTART)
+ cmdLine += '"%s" ' % TOSTART
+ <BLANKLINE>
+ if PARAMETERS:
+ cmdLine += PARAMETERS
+ <BLANKLINE>
+ self.start_cmd = cmdLine
+ <BLANKLINE>
+ win32serviceutil.ServiceFramework.__init__(self, args)
+ # Create an event which we will use to wait on.
+ # The "service stop" request will set this event.
+ self.hWaitStop = win32event.CreateEvent(None, 0, 0, None)
+ self.redirectOutput()
+ <BLANKLINE>
+ def redirectOutput(self):
+ sys.stdout.close()
+ sys.stderr.close()
+ sys.stdout = NullOutput()
+ sys.stderr = NullOutput()
+ <BLANKLINE>
+ def SvcStop(self):
+ # Before we do anything, tell the SCM we are starting the stop process.
+ self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
+ <BLANKLINE>
+ # TODO: This TerminateProcess call doesn't make much sense: it's
+ # doing a hard kill _first_, never giving the process a chance to
+ # shut down cleanly. Compare to current Zope2 service code, which
+ # uses Windows events to give the process a chance to shut down
+ # cleanly, doing a hard kill only if that doesn't succeed.
+ <BLANKLINE>
+ # stop the process if necessary
+ try:
+ win32process.TerminateProcess(self.hZope, 0)
+ except pywintypes.error:
+ # the process may already have been terminated
+ pass
+ # And set my event.
+ win32event.SetEvent(self.hWaitStop)
+ <BLANKLINE>
+ # SvcStop only gets triggered when the user explictly stops (or restarts)
+ # the service. To shut the service down cleanly when Windows is shutting
+ # down, we also need to hook SvcShutdown.
+ SvcShutdown = SvcStop
+ <BLANKLINE>
+ def createProcess(self, cmd):
+ #need to set current dir to INSTANCE_HOME otherwise pkg_resources will
+ #be pissed (in a combination with paster)
+ return win32process.CreateProcess(
+ None, cmd, None, None, 0, 0, None,
+ INSTANCE_HOME,
+ win32process.STARTUPINFO())
+ <BLANKLINE>
+ def SvcDoRun(self):
+ # indicate to Zope that the process is daemon managed (restartable)
+ os.environ['ZMANAGED'] = '1'
+ <BLANKLINE>
+ # daemon behavior: we want to to restart the process if it
+ # dies, but if it dies too many times, we need to give up.
+ <BLANKLINE>
+ # we use a simple backoff algorithm to determine whether
+ # we should try to restart a dead process: for each
+ # time the process dies unexpectedly, we wait some number of
+ # seconds to restart it, as determined by the backoff interval,
+ # which doubles each time the process dies. if we exceed
+ # BACKOFF_MAX seconds in cumulative backoff time, we give up.
+ # at any time if we successfully run the process for more thab
+ # BACKOFF_CLEAR_TIME seconds, the backoff stats are reset.
+ <BLANKLINE>
+ # the initial number of seconds between process start attempts
+ backoff_interval = BACKOFF_INITIAL_INTERVAL
+ # the cumulative backoff seconds counter
+ backoff_cumulative = 0
+ <BLANKLINE>
+ import servicemanager
+ <BLANKLINE>
+ # log a service started message
+ servicemanager.LogMsg(
+ servicemanager.EVENTLOG_INFORMATION_TYPE,
+ servicemanager.PYS_SERVICE_STARTED,
+ (self._svc_name_, ' (%s)' % self._svc_display_name_))
+ <BLANKLINE>
+ while 1:
+ start_time = time.time()
+ info = self.createProcess(self.start_cmd)
+ self.hZope = info[0] # the pid
+ if backoff_interval > BACKOFF_INITIAL_INTERVAL:
+ # if we're in a backoff state, log a message about
+ # starting a new process
+ servicemanager.LogInfoMsg(
+ '%s (%s): recovering from died process, new process '
+ 'started' % (self._svc_name_, self._svc_display_name_)
+ )
+ rc = win32event.WaitForMultipleObjects(
+ (self.hWaitStop, self.hZope), 0, win32event.INFINITE)
+ if rc == win32event.WAIT_OBJECT_0:
+ # user sent a stop service request
+ self.SvcStop()
+ break
+ else:
+ # user did not send a service stop request, but
+ # the process died; this may be an error condition
+ status = win32process.GetExitCodeProcess(self.hZope)
+ if status == 0:
+ # the user shut the process down from the web
+ # interface (or it otherwise exited cleanly)
+ break
+ else:
+ # this was an abormal shutdown. if we can, we want to
+ # restart the process but if it seems hopeless,
+ # don't restart an infinite number of times.
+ if backoff_cumulative > BACKOFF_MAX:
+ # it's hopeless
+ servicemanager.LogErrorMsg(
+ '%s (%s): process could not be restarted due to max '
+ 'restart attempts exceeded' % (
+ self._svc_display_name_, self._svc_name_
+ ))
+ self.SvcStop()
+ break
+ servicemanager.LogWarningMsg(
+ '%s (%s): process died unexpectedly. Will attempt '
+ 'restart after %s seconds.' % (
+ self._svc_name_, self._svc_display_name_,
+ backoff_interval
+ )
+ )
+ # if BACKOFF_CLEAR_TIME seconds have elapsed since we last
+ # started the process, reset the backoff interval
+ # and the cumulative backoff time to their original
+ # states
+ if time.time() - start_time > BACKOFF_CLEAR_TIME:
+ backoff_interval = BACKOFF_INITIAL_INTERVAL
+ backoff_cumulative = 0
+ # we sleep for the backoff interval. since this is async
+ # code, it would be better done by sending and
+ # catching a timed event (a service
+ # stop request will need to wait for us to stop sleeping),
+ # but this works well enough for me.
+ time.sleep(backoff_interval)
+ # update backoff_cumulative with the time we spent
+ # backing off.
+ backoff_cumulative = backoff_cumulative + backoff_interval
+ # bump the backoff interval up by 2* the last interval
+ backoff_interval = backoff_interval * 2
+ <BLANKLINE>
+ # loop and try to restart the process
+ <BLANKLINE>
+ # log a service stopped message
+ servicemanager.LogMsg(
+ servicemanager.EVENTLOG_INFORMATION_TYPE,
+ servicemanager.PYS_SERVICE_STOPPED,
+ (self._svc_name_, ' (%s) ' % self._svc_display_name_))
+ <BLANKLINE>
+ <BLANKLINE>
+ if __name__ == '__main__':
+ import win32serviceutil
+ if os.path.exists(PYTHONSERVICE_EXE):
+ # This ensures that pythonservice.exe is registered...
+ os.system('"%s" -register' % PYTHONSERVICE_EXE)
+ win32serviceutil.HandleCommandLine(Zope3Service)
+Parameters for the script
+-------------------------
+Let's add now parameters to the script:
+ >>> write('buildout.cfg',
+ ... '''
+ ... [buildout]
+ ... develop = demo1 demo2
+ ... parts = winservice
+ ...
+ ... [winservice]
+ ... recipe = z3c.recipe.winservice:service
+ ... name = Zope 3 Windows Service
+ ... description = Zope 3 Windows Service description
+ ... runzope = app
+ ... parameters = -C zope.conf
+ ...
+ ... ''' % globals())
+
+Let's buildout:
+
+ >>> print system(join('bin', 'buildout')),
+ Develop: '/sample-buildout/demo1'
+ Develop: '/sample-buildout/demo2'
+ Uninstalling winservice.
+ Installing winservice.
+
+The bin folder contains the windows service installer script:
+
+ >>> ls('bin')
+ - app-script.py
+ - buildout-script.py
+ - buildout.exe
+ - winservice.py
+
+PARAMETERS gets filled in:
+
+ >>> cat('bin', 'winservice.py')
+ ##############################################################################
+ ...
+ # these are replacements from winservice recipe
+ PYTHONSERVICE_EXE = r'...\Lib\site-packages\win32\pythonservice.exe'
+ PYTHON = r'...\python.exe'
+ TOSTART = r'/sample-buildout/bin/app-script.py'
+ PARAMETERS = r'-C zope.conf'
+ SERVICE_NAME = '...sample_buildout_bin_app_script_py'
+ SERVICE_DISPLAY_NAME = r'Zope 3 Windows Service'
+ SERVICE_DESCRIPTION = r'Zope 3 Windows Service description'
+ INSTANCE_HOME = r'/sample-buildout'
+ ...
+
+A script not in ``bin``
+-----------------------
+
+We can have the script in a different folder than ``bin``:
+
+ >>> write('buildout.cfg',
+ ... '''
+ ... [buildout]
+ ... develop = demo1 demo2
+ ... parts = winservice
+ ...
+ ... [winservice]
+ ... recipe = z3c.recipe.winservice:service
+ ... name = Zope 3 Windows Service
+ ... description = Zope 3 Windows Service description
+ ... runzope = parts\zope-app\zope-dev
+ ...
+ ... ''' % globals())
+
+Let's buildout:
+
+ >>> print system(join('bin', 'buildout')),
+ Develop: '/sample-buildout/demo1'
+ Develop: '/sample-buildout/demo2'
+ Uninstalling winservice.
+ Installing winservice.
+ While:
+ Installing winservice.
+ Error: App start script parts\zope-app\zope-dev-script.py does not exist.
+
+Oops, the script does not exist yet.
+
+ >>> import os
+ >>> os.mkdir('parts/zope-app')
+
+ >>> write('parts', 'zope-app', 'zope-dev-script.py',
+ ... '''
+ ... other dummy app script
+ ... ''')
+
+Buildout again:
+
+ >>> print system(join('bin', 'buildout')),
+ Develop: '/sample-buildout/demo1'
+ Develop: '/sample-buildout/demo2'
+ Installing winservice.
+
+The bin folder contains the windows service installer script:
+
+ >>> ls('bin')
+ - app-script.py
+ - buildout-script.py
+ - buildout.exe
+ - winservice.py
+
+TOSTART respects the folder given in runzope:
+
+ >>> cat('bin', 'winservice.py')
+ ##############################################################################
+ ...
+ PYTHONSERVICE_EXE = r'...\Lib\site-packages\win32\pythonservice.exe'
+ PYTHON = r'...\python.exe'
+ TOSTART = r'parts\zope-app\zope-dev-script.py'
+ PARAMETERS = r''
+ SERVICE_NAME = 'parts_zope_app_zope_dev_script_py'
+ SERVICE_DISPLAY_NAME = r'Zope 3 Windows Service'
+ SERVICE_DESCRIPTION = r'Zope 3 Windows Service description'
+ INSTANCE_HOME = r'/sample-buildout'
+ ...
+
+
+``runscript`` executable
+-------------------------
+
+Let's use now the ``runscript`` option:
+
+ >>> write('buildout.cfg',
+ ... '''
+ ... [buildout]
+ ... develop = demo1 demo2
+ ... parts = winservice
+ ...
+ ... [winservice]
+ ... recipe = z3c.recipe.winservice:service
+ ... name = Zope 3 Windows Service
+ ... description = Zope 3 Windows Service description
+ ... runscript = parts\exe\some.exe
+ ... parameters = -C zope.conf
+ ...
+ ... ''' % globals())
+
+Let's buildout:
+
+ >>> print system(join('bin', 'buildout')),
+ Develop: '/sample-buildout/demo1'
+ Develop: '/sample-buildout/demo2'
+ Uninstalling winservice.
+ Installing winservice.
+ While:
+ Installing winservice.
+ Error: App start script parts\exe\some.exe does not exist.
+
+Darn, the executable must exist.
+Try again:
+
+ >>> import os
+ >>> os.mkdir('parts/exe')
+
+ >>> write('parts', 'exe', 'some.exe',
+ ... '''
+ ... dummy executable
+ ... ''')
+
+Buildout again:
+
+ >>> print system(join('bin', 'buildout')),
+ Develop: '/sample-buildout/demo1'
+ Develop: '/sample-buildout/demo2'
+ Installing winservice.
+
+The bin folder contains the windows service installer script:
+
+ >>> ls('bin')
+ - app-script.py
+ - buildout-script.py
+ - buildout.exe
+ - winservice.py
+
+PYTHON gets blanked, because this is an executable,
+TOSTART gets filled with the right executable,
+PARAMETERS is still as before.
+
+ >>> cat('bin', 'winservice.py')
+ ##############################################################################
+ ...
+ # these are replacements from winservice recipe
+ PYTHONSERVICE_EXE = r'...\Lib\site-packages\win32\pythonservice.exe'
+ PYTHON = r''
+ TOSTART = r'parts\exe\some.exe'
+ PARAMETERS = r'-C zope.conf'
+ SERVICE_NAME = 'parts_exe_some_exe'
+ SERVICE_DISPLAY_NAME = r'Zope 3 Windows Service'
+ SERVICE_DESCRIPTION = r'Zope 3 Windows Service description'
+ INSTANCE_HOME = r'/sample-buildout'
+ ...
+
+``runscript`` python script
+----------------------------
+
+Let's use now the ``runscript`` option:
+
+ >>> write('buildout.cfg',
+ ... '''
+ ... [buildout]
+ ... develop = demo1 demo2
+ ... parts = winservice
+ ...
+ ... [winservice]
+ ... recipe = z3c.recipe.winservice:service
+ ... name = Zope 3 Windows Service
+ ... description = Zope 3 Windows Service description
+ ... runscript = parts\exe\some.py
+ ... parameters = -C zope.conf
+ ...
+ ... ''' % globals())
+
+ >>> write('parts', 'exe', 'some.py',
+ ... '''
+ ... dummy python script
+ ... ''')
+
+Let's buildout:
+
+ >>> print system(join('bin', 'buildout')),
+ Develop: '/sample-buildout/demo1'
+ Develop: '/sample-buildout/demo2'
+ Uninstalling winservice.
+ Installing winservice.
+
+The bin folder contains the windows service installer script:
+
+ >>> ls('bin')
+ - app-script.py
+ - buildout-script.py
+ - buildout.exe
+ - winservice.py
+
+PYTHON is back, because this is a script,
+TOSTART gets filled with the right python script,
+PARAMETERS is still as before.
+
+ >>> cat('bin', 'winservice.py')
+ ##############################################################################
+ ...
+ # these are replacements from winservice recipe
+ PYTHONSERVICE_EXE = r'...\Lib\site-packages\win32\pythonservice.exe'
+ PYTHON = r'...\python.exe'
+ TOSTART = r'parts\exe\some.py'
+ PARAMETERS = r'-C zope.conf'
+ SERVICE_NAME = 'parts_exe_some_py'
+ SERVICE_DISPLAY_NAME = r'Zope 3 Windows Service'
+ SERVICE_DESCRIPTION = r'Zope 3 Windows Service description'
+ INSTANCE_HOME = r'/sample-buildout'
+ ...
+
Debug
-----
@@ -430,60 +669,60 @@
We can enable the ``debug`` option:
- >>> write('buildout.cfg',
- ... '''
- ... [buildout]
- ... develop = demo1 demo2
- ... parts = winservice
- ...
- ... [winservice]
- ... recipe = z3c.recipe.winservice:service
- ... name = Zope 3 Windows Service
- ... description = Zope 3 Windows Service description
- ... runzope = app
- ... debug = true
- ...
- ... ''' % globals())
+ >>> write('buildout.cfg',
+ ... '''
+ ... [buildout]
+ ... develop = demo1 demo2
+ ... parts = winservice
+ ...
+ ... [winservice]
+ ... recipe = z3c.recipe.winservice:service
+ ... name = Zope 3 Windows Service
+ ... description = Zope 3 Windows Service description
+ ... runzope = app
+ ... debug = true
+ ...
+ ... ''' % globals())
Now, Let's run the buildout and see what we get:
- >>> print system(join('bin', 'buildout')),
- Develop: '/sample-buildout/demo1'
- Develop: '/sample-buildout/demo2'
- Uninstalling winservice.
- Installing winservice.
+ >>> print system(join('bin', 'buildout')),
+ Develop: '/sample-buildout/demo1'
+ Develop: '/sample-buildout/demo2'
+ Uninstalling winservice.
+ Installing winservice.
The bin folder contains the windows service installer script:
- >>> ls('bin')
- - app-script.py
- - app-servicedebug.py
- - buildout-script.py
- - buildout.exe
- - winservice.py
+ >>> ls('bin')
+ - app-script.py
+ - app-servicedebug.py
+ - buildout-script.py
+ - buildout.exe
+ - winservice.py
The winservice.py file gets changed according to the new script name:
- >>> cat('bin', 'winservice.py')
- ##############################################################################
- ...TOSTART = r'/sample-buildout/bin/app-servicedebug.py'...
- ...SERVICE_NAME = '...bin_app_script_py'...
+ >>> cat('bin', 'winservice.py')
+ ##############################################################################
+ ...TOSTART = r'/sample-buildout/bin/app-servicedebug.py'...
+ ...SERVICE_NAME = '...bin_app_script_py'...
The debug script contains a bare catch-all and a logger:
- >>> cat('bin', 'app-servicedebug.py')
- <BLANKLINE>
- def exceptionlogger():
- import servicemanager
- import traceback
- servicemanager.LogErrorMsg("Script %s had an exception: %s" % (
- __file__, traceback.format_exc()
- ))
- <BLANKLINE>
- try:
- <BLANKLINE>
- dummy start script
- <BLANKLINE>
- except Exception, e:
- exceptionlogger()
+ >>> cat('bin', 'app-servicedebug.py')
+ <BLANKLINE>
+ def exceptionlogger():
+ import servicemanager
+ import traceback
+ servicemanager.LogErrorMsg("Script %s had an exception: %s" % (
+ __file__, traceback.format_exc()
+ ))
+ <BLANKLINE>
+ try:
+ <BLANKLINE>
+ dummy start script
+ <BLANKLINE>
+ except Exception, e:
+ exceptionlogger()
Modified: z3c.recipe.winservice/trunk/src/z3c/recipe/winservice/service.py
===================================================================
--- z3c.recipe.winservice/trunk/src/z3c/recipe/winservice/service.py 2010-10-19 08:53:56 UTC (rev 117753)
+++ z3c.recipe.winservice/trunk/src/z3c/recipe/winservice/service.py 2010-10-19 12:26:52 UTC (rev 117754)
@@ -54,6 +54,16 @@
shutil.copymode(srcFile, script)
shutil.copystat(srcFile, script)
+ #get rid of compiled versions
+ try:
+ os.unlink(os.path.splitext(script)[0]+'.pyc')
+ except OSError:
+ pass
+ try:
+ os.unlink(os.path.splitext(script)[0]+'.pyo')
+ except OSError:
+ pass
+
generated.append(script)
return generated
@@ -75,6 +85,17 @@
shutil.copymode(srcFile, dstFile)
shutil.copystat(srcFile, dstFile)
+ #get rid of compiled versions
+ try:
+ os.unlink(os.path.splitext(dstFile)[0]+'.pyc')
+ except OSError:
+ pass
+
+ try:
+ os.unlink(os.path.splitext(dstFile)[0]+'.pyo')
+ except OSError:
+ pass
+
generated.append(dstFile)
return generated
@@ -107,17 +128,33 @@
# setup start script
binDir = self.buildout['buildout']['bin-directory']
runzope = options.get('runzope')
+ runscript = options.get('runscript')
# fix script name
- if not runzope:
+ if not runzope and not runscript:
raise zc.buildout.UserError(
- 'Missing runzope option in winservice recipe.')
- if not runzope.endswith('-script.py'):
- if runzope.endswith('.py'):
- runzope = runzope[:3]
- runzope = '%s-script.py' % runzope
+ 'Missing `runzope` or `runscript` option in the recipe.')
- self.runScript = os.path.join(binDir, runzope)
+ if runzope and runscript:
+ raise zc.buildout.UserError(
+ 'Only one of `runzope` or `runscript` allowed in the recipe.')
+
+ if runzope:
+ #old-ish way
+ if not runzope.endswith('-script.py'):
+ if runzope.endswith('.py'):
+ runzope = runzope[:3]
+ runzope = '%s-script.py' % runzope
+
+ if '/' in runzope or '\\' in runzope:
+ #don't add the bin folder if there's already a folder
+ self.runScript = runzope
+ else:
+ self.runScript = os.path.join(binDir, runzope)
+ else:
+ #new-ish way, just don't touch runscript
+ self.runScript = runscript
+
options['serviceName'] = self.getServiceName()
def getServiceName(self):
@@ -151,28 +188,47 @@
displayName = options.get('name', defaultName)
serviceName = options['serviceName']
description = options.get('description', defaultDescription)
+ parameters = options.get('parameters', '')
+ python = self.executable
+ pythondir = os.path.split(python)[0]
+ #this is dumb... but stays unless someone figures something better
+ pythonservice_exe = r'%s\Lib\site-packages\win32\pythonservice.exe' % pythondir
+ instance_home = self.buildout['buildout']['directory']
+ # raise exeption if the service exe is not here now
+ if not os.path.exists(pythonservice_exe):
+ raise zc.buildout.UserError(
+ 'Python service %s does not exist.' %
+ pythonservice_exe)
+
generated = []
- if options.get('debug'):
+ if options.get('debug') and self.runScript.lower().endswith('.py'):
serviceScript = self.runScript.replace(
'-script.py', '-servicedebug.py')
generated += patchScript(self.runScript, serviceScript)
else:
serviceScript = self.runScript
+ if serviceScript.lower().endswith('.exe'):
+ #if the script is an exe, no need to run it with python
+ python = ''
+
self.winServiceVars = [
- ("<<PYTHON>>", self.executable),
+ ("<<PYTHON>>", python),
("<<RUNZOPE>>", serviceScript),
+ ("<<PYTHONSERVICE_EXE>>", pythonservice_exe),
("<<SERVICE_NAME>>", serviceName),
("<<SERVICE_DISPLAY_NAME>>", displayName),
("<<SERVICE_DESCRIPTION>>", description),
+ ("<<PARAMETERS>>", parameters),
+ ("<<INSTANCE_HOME>>", instance_home),
]
# raise exeption if the app script is not here now
if not os.path.exists(serviceScript):
raise zc.buildout.UserError(
- 'App start script %s does not exist in bin folder.' %
+ 'App start script %s does not exist.' %
self.runScript)
# get templates
Modified: z3c.recipe.winservice/trunk/src/z3c/recipe/winservice/tests.py
===================================================================
--- z3c.recipe.winservice/trunk/src/z3c/recipe/winservice/tests.py 2010-10-19 08:53:56 UTC (rev 117753)
+++ z3c.recipe.winservice/trunk/src/z3c/recipe/winservice/tests.py 2010-10-19 12:26:52 UTC (rev 117754)
@@ -66,8 +66,7 @@
setUp=setUp, tearDown=zc.buildout.testing.buildoutTearDown,
optionflags=(
doctest.NORMALIZE_WHITESPACE|
- doctest.ELLIPSIS|
- doctest.REPORT_NDIFF),
+ doctest.ELLIPSIS),
checker=checker),
)
Modified: z3c.recipe.winservice/trunk/src/z3c/recipe/winservice/winservice.in
===================================================================
--- z3c.recipe.winservice/trunk/src/z3c/recipe/winservice/winservice.in 2010-10-19 08:53:56 UTC (rev 117753)
+++ z3c.recipe.winservice/trunk/src/z3c/recipe/winservice/winservice.in 2010-10-19 12:26:52 UTC (rev 117754)
@@ -85,14 +85,14 @@
import win32process
# these are replacements from winservice recipe
+PYTHONSERVICE_EXE = r'<<PYTHONSERVICE_EXE>>'
PYTHON = r'<<PYTHON>>'
-PYTHONDIR = os.path.split(PYTHON)[0]
-PYTHONSERVICE_EXE = r'%s\Lib\site-packages\win32\pythonservice.exe' % PYTHONDIR
TOSTART = r'<<RUNZOPE>>'
+PARAMETERS = r'<<PARAMETERS>>'
SERVICE_NAME = '<<SERVICE_NAME>>'
SERVICE_DISPLAY_NAME = r'<<SERVICE_DISPLAY_NAME>>'
SERVICE_DESCRIPTION = r'<<SERVICE_DESCRIPTION>>'
-INSTANCE_HOME = os.path.dirname(os.path.dirname(TOSTART))
+INSTANCE_HOME = r'<<INSTANCE_HOME>>'
# the max seconds we're allowed to spend backing off
@@ -152,13 +152,20 @@
start_cmd = ''
def __init__(self, args):
- if not os.path.exists(PYTHON):
- raise OSError("%s does not exist" % PYTHON)
+ cmdLine = ''
+ if PYTHON:
+ if not os.path.exists(PYTHON):
+ raise OSError("%s does not exist" % PYTHON)
+ cmdLine += '"%s" ' % PYTHON
if not os.path.exists(TOSTART):
raise OSError("%s does not exist" % TOSTART)
+ cmdLine += '"%s" ' % TOSTART
- self.start_cmd = '"%s" "%s"' % (PYTHON, TOSTART)
+ if PARAMETERS:
+ cmdLine += PARAMETERS
+ self.start_cmd = cmdLine
+
win32serviceutil.ServiceFramework.__init__(self, args)
# Create an event which we will use to wait on.
# The "service stop" request will set this event.
More information about the checkins
mailing list