@@ -60,8 +60,7 @@ def __init__(self, library, host='127.0.0.1', port=8270, port_file=None,
60
60
:param port_file: File to write port that is used. ``None`` means
61
61
no such file is written.
62
62
:param allow_stop: Allow/disallow stopping the server using ``Stop
63
- Remote Server`` keyword and :meth:`stop_serve`
64
- method.
63
+ Remote Server`` keyword.
65
64
:param serve: When ``True`` starts the server automatically.
66
65
When ``False``, server can be started with
67
66
:meth:`serve` or :meth:`start` methods.
@@ -88,43 +87,27 @@ def _register_functions(self, server):
88
87
server .register_function (self .run_keyword )
89
88
server .register_function (self .get_keyword_arguments )
90
89
server .register_function (self .get_keyword_documentation )
91
- server .register_function (self .stop_serve , 'stop_remote_server' )
90
+ server .register_function (self ._stop_serve , 'stop_remote_server' )
92
91
93
92
def serve (self , log = True ):
94
93
"""Start the server and wait for it to finish.
95
94
96
95
:param log: Log message about startup or not.
97
96
98
- Using this requires using ``serve=False`` when creating initializing
99
- the server. Using ``serve=True`` is equal to first using ``serve=False``
97
+ If this method is called in the main thread, automatically registers
98
+ signals INT, TERM and HUP to stop the server.
99
+
100
+ Using this method requires using ``serve=False`` when initializing the
101
+ server. Using ``serve=True`` is equal to first using ``serve=False``
100
102
and then calling this method. Alternatively :meth:`start` can be used
101
103
to start the server on background.
102
104
103
105
In addition to signals, the server can be stopped with ``Stop Remote
104
- Server`` keyword or by calling :meth:`stop_serve` method, but both
105
- of these can be disabled with ``allow_stop=False`` when the server
106
- is initialized. Calling :meth:`force_stop_serve` stops the server
107
- unconditionally.
106
+ Server`` keyword. Using :meth:`stop` method is possible too, but
107
+ requires running this method in a thread.
108
108
"""
109
109
self ._server .serve (log = log )
110
110
111
- def stop_serve (self , log = True ):
112
- """Stop the server started by :meth:`serve`.
113
-
114
- :param log: Log message about stopping or not.
115
-
116
- May be disabled with ``allow_stop=False`` when initializing the server.
117
- Use :meth:`force_stop_serve` if that is a problem.
118
- """
119
- return self ._server .stop_serve (log = log )
120
-
121
- def force_stop_serve (self , log = True ):
122
- """Stop the server started by :meth:`serve` unconditionally.
123
-
124
- :param log: Log message about stopping or not.
125
- """
126
- return self ._server .stop_serve (force = True , log = log )
127
-
128
111
def start (self , log = False ):
129
112
"""Start the server on background.
130
113
@@ -142,6 +125,9 @@ def stop(self, log=False):
142
125
"""
143
126
self ._server .stop (log = log )
144
127
128
+ def _stop_serve (self , log = True ):
129
+ return self ._server .stop_serve (remote = True , log = log )
130
+
145
131
def _log (self , msg , level = None ):
146
132
if level :
147
133
msg = '*%s* %s' % (level .upper (), msg )
@@ -158,7 +144,7 @@ def get_keyword_names(self):
158
144
159
145
def run_keyword (self , name , args , kwargs = None ):
160
146
if name == 'stop_remote_server' :
161
- return KeywordRunner (self .stop_serve ).run_keyword (args , kwargs )
147
+ return KeywordRunner (self ._stop_serve ).run_keyword (args , kwargs )
162
148
return self ._library .run_keyword (name , args , kwargs )
163
149
164
150
def get_keyword_arguments (self , name ):
@@ -176,41 +162,14 @@ def get_keyword_documentation(self, name):
176
162
class StoppableXMLRPCServer (SimpleXMLRPCServer ):
177
163
allow_reuse_address = True
178
164
179
- def __init__ (self , host , port , port_file = None , allow_stop_serve = True ):
165
+ def __init__ (self , host , port , port_file = None , allow_remote_stop = True ):
180
166
SimpleXMLRPCServer .__init__ (self , (host , port ), logRequests = False ,
181
167
bind_and_activate = False )
182
168
self ._port_file = port_file
183
169
self ._thread = None
184
- self ._allow_stop_serve = allow_stop_serve
170
+ self ._allow_remote_stop = allow_remote_stop
185
171
self ._stop_serve = None
186
-
187
- def start (self , log = False ):
188
- self .server_bind ()
189
- self .server_activate ()
190
- self ._thread = threading .Thread (target = self .serve_forever )
191
- self ._thread .start ()
192
- self ._announce_start (log , self ._port_file )
193
-
194
- def _announce_start (self , log_start , port_file ):
195
- self ._log ('started' , log_start )
196
- if port_file :
197
- with open (port_file , 'w' ) as pf :
198
- pf .write (str (self .server_address [1 ]))
199
-
200
- def stop (self , log = False ):
201
- if self ._stop_serve :
202
- self .stop_serve (log = log )
203
- return
204
- self .shutdown ()
205
- self .server_close ()
206
- self ._thread .join ()
207
- self ._thread = None
208
- self ._announce_end (log , self ._port_file )
209
-
210
- def _announce_end (self , log_end , port_file ):
211
- self ._log ('stopped' , log_end )
212
- if port_file and os .path .exists (port_file ):
213
- os .remove (port_file ) # TODO: Document that port file is removed
172
+ self ._stop_lock = threading .Lock ()
214
173
215
174
def serve (self , log = True ):
216
175
self ._stop_serve = threading .Event ()
@@ -224,7 +183,7 @@ def serve(self, log=True):
224
183
@contextmanager
225
184
def _stop_signals (self ):
226
185
original = {}
227
- stop = lambda signum , frame : self .stop_serve ()
186
+ stop = lambda signum , frame : self .stop_serve (remote = False )
228
187
try :
229
188
for name in 'SIGINT' , 'SIGTERM' , 'SIGHUP' :
230
189
if hasattr (signal , name ):
@@ -237,17 +196,45 @@ def _stop_signals(self):
237
196
for name in original :
238
197
signal .signal (getattr (signal , name ), original [name ])
239
198
240
- def stop_serve (self , force = False , log = True ):
241
- if not self ._thread :
242
- self ._log ('is not running' , log )
243
- return True
244
- if (self ._allow_stop_serve or force ) and self ._stop_serve :
199
+ def stop_serve (self , remote = True , log = True ):
200
+ if (self ._allow_remote_stop or not remote ) and self ._stop_serve :
245
201
self ._stop_serve .set ()
246
202
return True
247
203
# TODO: Log to __stdout__? WARN?
248
204
self ._log ('does not allow stopping' , log )
249
205
return False
250
206
207
+ def start (self , log = False ):
208
+ self .server_bind ()
209
+ self .server_activate ()
210
+ self ._thread = threading .Thread (target = self .serve_forever )
211
+ self ._thread .daemon = True
212
+ self ._thread .start ()
213
+ self ._announce_start (log , self ._port_file )
214
+
215
+ def _announce_start (self , log_start , port_file ):
216
+ self ._log ('started' , log_start )
217
+ if port_file :
218
+ with open (port_file , 'w' ) as pf :
219
+ pf .write (str (self .server_address [1 ]))
220
+
221
+ def stop (self , log = False ):
222
+ if self ._stop_serve :
223
+ return self .stop_serve (log = log )
224
+ with self ._stop_lock :
225
+ if not self ._thread : # already stopped
226
+ return
227
+ self .shutdown ()
228
+ self .server_close ()
229
+ self ._thread .join ()
230
+ self ._thread = None
231
+ self ._announce_stop (log , self ._port_file )
232
+
233
+ def _announce_stop (self , log_end , port_file ):
234
+ self ._log ('stopped' , log_end )
235
+ if port_file and os .path .exists (port_file ):
236
+ os .remove (port_file ) # TODO: Document that port file is removed
237
+
251
238
def _log (self , action , log = True ):
252
239
if log :
253
240
host , port = self .server_address
@@ -370,6 +357,7 @@ def __init__(self, keyword):
370
357
self ._keyword = keyword
371
358
372
359
def run_keyword (self , args , kwargs = None ):
360
+ # TODO: Handle binary in lists/dicts in args/kwargs
373
361
args , kwargs = self ._handle_binary_args (args , kwargs or {})
374
362
result = KeywordResult ()
375
363
with StandardStreamInterceptor () as interceptor :
@@ -400,18 +388,20 @@ class StandardStreamInterceptor(object):
400
388
401
389
def __init__ (self ):
402
390
self .output = ''
403
-
404
- def __enter__ ( self ):
391
+ self . origout = sys . stdout
392
+ self . origerr = sys . stderr
405
393
sys .stdout = StringIO ()
406
394
sys .stderr = StringIO ()
395
+
396
+ def __enter__ (self ):
407
397
return self
408
398
409
399
def __exit__ (self , * exc_info ):
410
400
stdout = sys .stdout .getvalue ()
411
401
stderr = sys .stderr .getvalue ()
412
402
close = [sys .stdout , sys .stderr ]
413
- sys .stdout = sys . __stdout__
414
- sys .stderr = sys . __stderr__
403
+ sys .stdout = self . origout
404
+ sys .stderr = self . origerr
415
405
for stream in close :
416
406
stream .close ()
417
407
if stdout and stderr :
@@ -528,9 +518,8 @@ def test_remote_server(uri, log=True):
528
518
:param log: Log status message or not.
529
519
:return ``True`` if server is running, ``False`` otherwise.
530
520
"""
531
- server = ServerProxy (uri )
532
521
try :
533
- server .get_keyword_names ()
522
+ ServerProxy ( uri ) .get_keyword_names ()
534
523
except Exception :
535
524
if log :
536
525
print ('No remote server running at %s.' % uri )
@@ -545,18 +534,17 @@ def stop_remote_server(uri, log=True):
545
534
546
535
:param uri: Server address.
547
536
:param log: Log status message or not.
548
- :return ``True`` if server was stopped or it was not running,
549
- ``False`` otherwise.
537
+ :return ``True`` if server was stopped or it was not running in
538
+ the first place, ``False`` otherwise.
550
539
"""
551
540
if not test_remote_server (uri , log = False ):
552
541
if log :
553
542
print ('No remote server running at %s.' % uri )
554
543
return True
555
- server = ServerProxy (uri )
556
544
if log :
557
545
print ('Stopping remote server at %s.' % uri )
558
546
args = [] if log else [False ]
559
- return server .stop_remote_server (* args )
547
+ return ServerProxy ( uri ) .stop_remote_server (* args )
560
548
561
549
562
550
if __name__ == '__main__' :
0 commit comments