1 """Windows service. Requires pywin32."""
8 import win32serviceutil
10 from cherrypy
.process
import wspbus
, plugins
13 class ConsoleCtrlHandler(plugins
.SimplePlugin
):
14 """A WSPBus plugin for handling Win32 console events (like Ctrl-C)."""
16 def __init__(self
, bus
):
18 plugins
.SimplePlugin
.__init
__(self
, bus
)
22 self
.bus
.log('Handler for console events already set.', level
=40)
25 result
= win32api
.SetConsoleCtrlHandler(self
.handle
, 1)
27 self
.bus
.log('Could not SetConsoleCtrlHandler (error %r)' %
28 win32api
.GetLastError(), level
=40)
30 self
.bus
.log('Set handler for console events.', level
=40)
35 self
.bus
.log('Handler for console events already off.', level
=40)
39 result
= win32api
.SetConsoleCtrlHandler(self
.handle
, 0)
41 # "ValueError: The object has not been registered"
45 self
.bus
.log('Could not remove SetConsoleCtrlHandler (error %r)' %
46 win32api
.GetLastError(), level
=40)
48 self
.bus
.log('Removed handler for console events.', level
=40)
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
)
58 # Remove self immediately so repeated Ctrl-C doesn't re-call it.
65 # 'First to return True stops the calls'
70 class Win32Bus(wspbus
.Bus
):
71 """A Web Site Process Bus implementation for Win32.
73 Instead of time.sleep, this bus blocks using native win32event objects.
78 wspbus
.Bus
.__init
__(self
)
80 def _get_state_event(self
, state
):
81 """Return a win32event for the given state (creating it if needed)."""
83 return self
.events
[state
]
85 event
= win32event
.CreateEvent(None, 0, 0,
86 "WSPBus %s Event (pid=%r)" %
87 (state
.name
, os
.getpid()))
88 self
.events
[state
] = event
93 def _set_state(self
, value
):
95 event
= self
._get
_state
_event
(value
)
96 win32event
.PulseEvent(event
)
97 state
= property(_get_state
, _set_state
)
99 def wait(self
, state
, interval
=0.1, channel
=None):
100 """Wait for the given state(s), KeyboardInterrupt or SystemExit.
102 Since this class uses native win32event objects, the interval
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
)
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
)
117 class _ControlCodes(dict):
118 """Control codes used to "signal" a service via ControlService.
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:
125 control_codes['graceful'] = 128 + 10
128 def key_for(self
, obj
):
129 """For the given value, return its corresponding key."""
130 for key
, val
in self
.items():
133 raise ValueError("The given object could not be found: %r" % obj
)
135 control_codes
= _ControlCodes({'graceful': 138})
138 def signal_child(service
, command
):
139 if command
== 'stop':
140 win32serviceutil
.StopService(service
)
141 elif command
== 'restart':
142 win32serviceutil
.RestartService(service
)
144 win32serviceutil
.ControlService(service
, control_codes
[command
])
147 class PyWebService(win32serviceutil
.ServiceFramework
):
148 """Python Web Service."""
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
156 # Only exists on Windows 2000 or later, ignored on windows NT
157 _svc_description_
= "Python Web Service"
160 from cherrypy
import process
165 from cherrypy
import process
166 self
.ReportServiceStatus(win32service
.SERVICE_STOP_PENDING
)
169 def SvcOther(self
, control
):
170 process
.bus
.publish(control_codes
.key_for(control
))
173 if __name__
== '__main__':
174 win32serviceutil
.HandleCommandLine(PyWebService
)