~j5-dev/+junk/cherrypy3-3.2.0rc1

« back to all changes in this revision

Viewing changes to cherrypy/process/win32.py

  • Committer: steveh at sjsoft
  • Date: 2010-07-01 13:07:15 UTC
  • Revision ID: steveh@sjsoft.com-20100701130715-w56oim8346qzqlka
New upstream release

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
"""Windows service. Requires pywin32."""
 
2
 
 
3
import os
 
4
import win32api
 
5
import win32con
 
6
import win32event
 
7
import win32service
 
8
import win32serviceutil
 
9
 
 
10
from cherrypy.process import wspbus, plugins
 
11
 
 
12
 
 
13
class ConsoleCtrlHandler(plugins.SimplePlugin):
 
14
    """A WSPBus plugin for handling Win32 console events (like Ctrl-C)."""
 
15
    
 
16
    def __init__(self, bus):
 
17
        self.is_set = False
 
18
        plugins.SimplePlugin.__init__(self, bus)
 
19
    
 
20
    def start(self):
 
21
        if self.is_set:
 
22
            self.bus.log('Handler for console events already set.', level=40)
 
23
            return
 
24
        
 
25
        result = win32api.SetConsoleCtrlHandler(self.handle, 1)
 
26
        if result == 0:
 
27
            self.bus.log('Could not SetConsoleCtrlHandler (error %r)' %
 
28
                         win32api.GetLastError(), level=40)
 
29
        else:
 
30
            self.bus.log('Set handler for console events.', level=40)
 
31
            self.is_set = True
 
32
    
 
33
    def stop(self):
 
34
        if not self.is_set:
 
35
            self.bus.log('Handler for console events already off.', level=40)
 
36
            return
 
37
        
 
38
        try:
 
39
            result = win32api.SetConsoleCtrlHandler(self.handle, 0)
 
40
        except ValueError:
 
41
            # "ValueError: The object has not been registered"
 
42
            result = 1
 
43
        
 
44
        if result == 0:
 
45
            self.bus.log('Could not remove SetConsoleCtrlHandler (error %r)' %
 
46
                         win32api.GetLastError(), level=40)
 
47
        else:
 
48
            self.bus.log('Removed handler for console events.', level=40)
 
49
            self.is_set = False
 
50
    
 
51
    def handle(self, event):
 
52
        """Handle console control events (like Ctrl-C)."""
 
53
        if event in (win32con.CTRL_C_EVENT, win32con.CTRL_LOGOFF_EVENT,
 
54
                     win32con.CTRL_BREAK_EVENT, win32con.CTRL_SHUTDOWN_EVENT,
 
55
                     win32con.CTRL_CLOSE_EVENT):
 
56
            self.bus.log('Console event %s: shutting down bus' % event)
 
57
            
 
58
            # Remove self immediately so repeated Ctrl-C doesn't re-call it.
 
59
            try:
 
60
                self.stop()
 
61
            except ValueError:
 
62
                pass
 
63
            
 
64
            self.bus.exit()
 
65
            # 'First to return True stops the calls'
 
66
            return 1
 
67
        return 0
 
68
 
 
69
 
 
70
class Win32Bus(wspbus.Bus):
 
71
    """A Web Site Process Bus implementation for Win32.
 
72
    
 
73
    Instead of time.sleep, this bus blocks using native win32event objects.
 
74
    """
 
75
    
 
76
    def __init__(self):
 
77
        self.events = {}
 
78
        wspbus.Bus.__init__(self)
 
79
    
 
80
    def _get_state_event(self, state):
 
81
        """Return a win32event for the given state (creating it if needed)."""
 
82
        try:
 
83
            return self.events[state]
 
84
        except KeyError:
 
85
            event = win32event.CreateEvent(None, 0, 0,
 
86
                                           "WSPBus %s Event (pid=%r)" %
 
87
                                           (state.name, os.getpid()))
 
88
            self.events[state] = event
 
89
            return event
 
90
    
 
91
    def _get_state(self):
 
92
        return self._state
 
93
    def _set_state(self, value):
 
94
        self._state = value
 
95
        event = self._get_state_event(value)
 
96
        win32event.PulseEvent(event)
 
97
    state = property(_get_state, _set_state)
 
98
    
 
99
    def wait(self, state, interval=0.1, channel=None):
 
100
        """Wait for the given state(s), KeyboardInterrupt or SystemExit.
 
101
        
 
102
        Since this class uses native win32event objects, the interval
 
103
        argument is ignored.
 
104
        """
 
105
        if isinstance(state, (tuple, list)):
 
106
            # Don't wait for an event that beat us to the punch ;)
 
107
            if self.state not in state:
 
108
                events = tuple([self._get_state_event(s) for s in state])
 
109
                win32event.WaitForMultipleObjects(events, 0, win32event.INFINITE)
 
110
        else:
 
111
            # Don't wait for an event that beat us to the punch ;)
 
112
            if self.state != state:
 
113
                event = self._get_state_event(state)
 
114
                win32event.WaitForSingleObject(event, win32event.INFINITE)
 
115
 
 
116
 
 
117
class _ControlCodes(dict):
 
118
    """Control codes used to "signal" a service via ControlService.
 
119
    
 
120
    User-defined control codes are in the range 128-255. We generally use
 
121
    the standard Python value for the Linux signal and add 128. Example:
 
122
    
 
123
        >>> signal.SIGUSR1
 
124
        10
 
125
        control_codes['graceful'] = 128 + 10
 
126
    """
 
127
    
 
128
    def key_for(self, obj):
 
129
        """For the given value, return its corresponding key."""
 
130
        for key, val in self.items():
 
131
            if val is obj:
 
132
                return key
 
133
        raise ValueError("The given object could not be found: %r" % obj)
 
134
 
 
135
control_codes = _ControlCodes({'graceful': 138})
 
136
 
 
137
 
 
138
def signal_child(service, command):
 
139
    if command == 'stop':
 
140
        win32serviceutil.StopService(service)
 
141
    elif command == 'restart':
 
142
        win32serviceutil.RestartService(service)
 
143
    else:
 
144
        win32serviceutil.ControlService(service, control_codes[command])
 
145
 
 
146
 
 
147
class PyWebService(win32serviceutil.ServiceFramework):
 
148
    """Python Web Service."""
 
149
    
 
150
    _svc_name_ = "Python Web Service"
 
151
    _svc_display_name_ = "Python Web Service"
 
152
    _svc_deps_ = None        # sequence of service names on which this depends
 
153
    _exe_name_ = "pywebsvc"
 
154
    _exe_args_ = None        # Default to no arguments
 
155
    
 
156
    # Only exists on Windows 2000 or later, ignored on windows NT
 
157
    _svc_description_ = "Python Web Service"
 
158
    
 
159
    def SvcDoRun(self):
 
160
        from cherrypy import process
 
161
        process.bus.start()
 
162
        process.bus.block()
 
163
    
 
164
    def SvcStop(self):
 
165
        from cherrypy import process
 
166
        self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
 
167
        process.bus.exit()
 
168
    
 
169
    def SvcOther(self, control):
 
170
        process.bus.publish(control_codes.key_for(control))
 
171
 
 
172
 
 
173
if __name__ == '__main__':
 
174
    win32serviceutil.HandleCommandLine(PyWebService)