David,<br><br><div class="gmail_quote"><br><blockquote class="gmail_quote" style="border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;">While this process is certainly doable, I&#39;ll also point out that on<br>

Windows, the more &quot;natural&quot; approach to this is to implement the<br>
process as a service.  That also buys you some regular Windows<br>
approaches to management (net stop/start from command line, services<br>
UI from the control panel) and status inquiry (sc query from command<br>
line).  So while it may be a little platform-specific code you&#39;re<br>
final result will integrate more naturally into that environment for<br>
any system administrators.<br>
<br></blockquote><div><br>It is very possible I might have to go this route.  We have some odd requrements,<br>such as needing to run the server using the Windows Server 2008 HPC job scheduler.<br>the job scheduler does some funky things and I am finding that some of the &quot;usual&quot;<br>
assumptions you make on Windows don&#39;t hold.  So at this point, I need all the tricks<br>in my bad that I can get.  Thanks for the example!<br> </div><blockquote class="gmail_quote" style="border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;">

While I haven&#39;t done this with a twistd based Twisted service, I&#39;ve<br>
done it with a lot of Twisted code in general.  It&#39;s easiest if you have<br>
pywin32 installed and can use it&#39;s utility service wrappers, for which<br>
the pywin32 package has examples.<br></blockquote><div><br>OK, nice to know about these examples.<br> <br></div><blockquote class="gmail_quote" style="border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;">

I&#39;ve tended to offload the service definition to its own module, and<br>
have it just import and execute the main code (with the twisted<br>
reactor.run call) after the service gets started - that way you can<br>
also manually run the twisted code without involving any of the<br>
service support, or to support execution on non-Windows platforms.<br>
<br>
Here&#39;s one example of a service object that invoices Twisted-based<br>
code.  When stopping, you use callFromThread because the thread that<br>
handles the request from the Windows service manager is separate from<br>
the thread executing the main code (which is a thread created during<br>
the startup process).<br>
<br>
That&#39;s also why you start the reactor without signal initialization,<br>
since it&#39;ll be in a secondary thread.  In SvcDoRun is where you could<br>
import platform-generic modules to set up your twisted code, and then<br>
this code starts the reactor.<br></blockquote><div><br>Bummer, then I can&#39;t use this approach.  My &quot;server&quot; uses reactor.spawnProcess<br>which needs the signal handlers to be installed (SIGCHLD specifically) to work<br>
properly... do you know if it can be done without the dual thread trick.<br><br>Thanks,<br><br>Brian<br></div><div><br></div><blockquote class="gmail_quote" style="border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;">

Oh, and I left some code in that shows retrieving service-specific<br>
parameters from the registry as is typical for Windows services<br>
(stored in HKLM/System/CurrentControlSet/Services/&lt;svc_name&gt;/Parameters<br>
if I recall corrrectly):<br>
<br>
                              - - - - -<br>
<br>
class DataImportService(win32serviceutil.ServiceFramework):<br>
<br>
    __version__ = &#39;1.0.0&#39;<br>
<br>
    _svc_name_ = &#39;fdi&#39;<br>
    _svc_display_name_ = &#39;SomeCompany Data Integration&#39;<br>
<br>
    stopping = False<br>
    debug = False<br>
<br>
    def __init__(self, *args, **kwargs):<br>
        win32serviceutil.ServiceFramework.__init__(self, *args, **kwargs)<br>
        self.initialize()<br>
        self._stopped = threading.Event()<br>
        self.log = None<br>
<br>
    def initialize(self):<br>
        # This is separate from __init__ so during debugging the bootstrap<br>
        # code can override __init__ but still finish initialization.<br>
<br>
    def _getOption(self, option, default):<br>
        return win32serviceutil.GetServiceCustomOption(self._svc_name_,<br>
                                                       option,<br>
                                                       default)<br>
<br>
    def SvcStop(self):<br>
        self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)<br>
        print &#39;%s Service stopping&#39; % self._svc_display_name_<br>
        reactor.callFromThread(reactor.stop)<br>
        self._stopped.wait(5)<br>
        print &#39;%s Service stopped&#39; % self._svc_display_name_<br>
        if self.log:<br>
            self.log.close()<br>
<br>
    def SvcDoRun(self):<br>
        &quot;&quot;&quot;Main entry point for service execution&quot;&quot;&quot;<br>
<br>
        if hasattr(sys, &#39;frozen&#39;):<br>
            home_dir = os.path.dirname(sys.executable)<br>
        else:<br>
            home_dir = os.path.dirname(__file__)<br>
<br>
        # First, let&#39;s set up some logging<br>
        if self.debug:<br>
            dupstdout = True<br>
            logprefix = None<br>
        else:<br>
            dupstdout = False<br>
            logprefix = os.path.join(home_dir, &#39;logs&#39;, &#39;dataimport.log&#39;)<br>
<br>
        self.log = LogClass(logprefix, dupstdout)<br>
<br>
        # And then reroute stdout/stderr to that file object<br>
        sys.stdout = self.log<br>
        sys.stderr = self.log<br>
<br>
        print &#39;%s Service %s starting&#39; % (self._svc_display_name_,<br>
                                          self.__version__)<br>
<br>
        try:<br>
<br>
            # Process config file<br>
            config_file = self._getOption(&#39;config&#39;,<br>
                                          os.path.join(home_dir, &#39;data&#39;,<br>
                                                       &#39;dataimport.ini&#39;))<br>
<br>
            # ... other service-related initialization ...<br>
<br>
            # ... do any Twisted related initialization ...<br>
<br>
            # Start everything up<br>
            reactor.callLater(0, self.log.write,<br>
                              &#39;%s Service operational\n&#39; %<br>
                              self._svc_display_name_)<br>
            reactor.run(installSignalHandlers=False)<br>
<br>
            # We&#39;re shutting down.<br>
<br>
            # ... do any shutdown processing ...<br>
<br>
            # Flag that we&#39;re exiting so service thread can be more<br>
            # accurate in terms of declaring shutdown.<br>
            self._stopped.set()<br>
<br>
        except:<br>
<br>
            # For right now just log a traceback and abort<br>
            log.err()<br>
            # But try to gracefully close down log to let final traceback<br>
            # information make it out to the log file.<br>
            if self.log:<br>
                self.log.close()<br>
<br>
                              - - - - -<br>
<br>
Your main startup code in the service wrapper module can use helper<br>
functions from win32serviceutil for startup (see the pywin32 examples)<br>
which provides some automatic support for installing/uninstalling the<br>
service, or you can implement your own startup if you want to support<br>
different options and what not.  This all works under py2exe as well<br>
if you want to package things up and have a nice self-contained exe as<br>
a Windows service.<br>
<font color="#888888"><br>
-- David<br>
</font><div><div class="h5"><br>
<br>
_______________________________________________<br>
Twisted-Python mailing list<br>
<a href="mailto:Twisted-Python@twistedmatrix.com">Twisted-Python@twistedmatrix.com</a><br>
<a href="http://twistedmatrix.com/cgi-bin/mailman/listinfo/twisted-python" target="_blank">http://twistedmatrix.com/cgi-bin/mailman/listinfo/twisted-python</a><br>
</div></div></blockquote><br>
</div><br>