@@ -60,8 +60,7 @@ def __init__(self, library, host='127.0.0.1', port=8270, port_file=None,
6060 :param port_file: File to write port that is used. ``None`` means
6161 no such file is written.
6262 :param allow_stop: Allow/disallow stopping the server using ``Stop
63- Remote Server`` keyword and :meth:`stop_serve`
64- method.
63+ Remote Server`` keyword.
6564 :param serve: When ``True`` starts the server automatically.
6665 When ``False``, server can be started with
6766 :meth:`serve` or :meth:`start` methods.
@@ -88,43 +87,27 @@ def _register_functions(self, server):
8887 server .register_function (self .run_keyword )
8988 server .register_function (self .get_keyword_arguments )
9089 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' )
9291
9392 def serve (self , log = True ):
9493 """Start the server and wait for it to finish.
9594
9695 :param log: Log message about startup or not.
9796
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``
100102 and then calling this method. Alternatively :meth:`start` can be used
101103 to start the server on background.
102104
103105 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.
108108 """
109109 self ._server .serve (log = log )
110110
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-
128111 def start (self , log = False ):
129112 """Start the server on background.
130113
@@ -142,6 +125,9 @@ def stop(self, log=False):
142125 """
143126 self ._server .stop (log = log )
144127
128+ def _stop_serve (self , log = True ):
129+ return self ._server .stop_serve (remote = True , log = log )
130+
145131 def _log (self , msg , level = None ):
146132 if level :
147133 msg = '*%s* %s' % (level .upper (), msg )
@@ -158,7 +144,7 @@ def get_keyword_names(self):
158144
159145 def run_keyword (self , name , args , kwargs = None ):
160146 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 )
162148 return self ._library .run_keyword (name , args , kwargs )
163149
164150 def get_keyword_arguments (self , name ):
@@ -176,41 +162,14 @@ def get_keyword_documentation(self, name):
176162class StoppableXMLRPCServer (SimpleXMLRPCServer ):
177163 allow_reuse_address = True
178164
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 ):
180166 SimpleXMLRPCServer .__init__ (self , (host , port ), logRequests = False ,
181167 bind_and_activate = False )
182168 self ._port_file = port_file
183169 self ._thread = None
184- self ._allow_stop_serve = allow_stop_serve
170+ self ._allow_remote_stop = allow_remote_stop
185171 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 ()
214173
215174 def serve (self , log = True ):
216175 self ._stop_serve = threading .Event ()
@@ -224,7 +183,7 @@ def serve(self, log=True):
224183 @contextmanager
225184 def _stop_signals (self ):
226185 original = {}
227- stop = lambda signum , frame : self .stop_serve ()
186+ stop = lambda signum , frame : self .stop_serve (remote = False )
228187 try :
229188 for name in 'SIGINT' , 'SIGTERM' , 'SIGHUP' :
230189 if hasattr (signal , name ):
@@ -237,17 +196,45 @@ def _stop_signals(self):
237196 for name in original :
238197 signal .signal (getattr (signal , name ), original [name ])
239198
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 :
245201 self ._stop_serve .set ()
246202 return True
247203 # TODO: Log to __stdout__? WARN?
248204 self ._log ('does not allow stopping' , log )
249205 return False
250206
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+
251238 def _log (self , action , log = True ):
252239 if log :
253240 host , port = self .server_address
@@ -370,6 +357,7 @@ def __init__(self, keyword):
370357 self ._keyword = keyword
371358
372359 def run_keyword (self , args , kwargs = None ):
360+ # TODO: Handle binary in lists/dicts in args/kwargs
373361 args , kwargs = self ._handle_binary_args (args , kwargs or {})
374362 result = KeywordResult ()
375363 with StandardStreamInterceptor () as interceptor :
@@ -400,18 +388,20 @@ class StandardStreamInterceptor(object):
400388
401389 def __init__ (self ):
402390 self .output = ''
403-
404- def __enter__ ( self ):
391+ self . origout = sys . stdout
392+ self . origerr = sys . stderr
405393 sys .stdout = StringIO ()
406394 sys .stderr = StringIO ()
395+
396+ def __enter__ (self ):
407397 return self
408398
409399 def __exit__ (self , * exc_info ):
410400 stdout = sys .stdout .getvalue ()
411401 stderr = sys .stderr .getvalue ()
412402 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
415405 for stream in close :
416406 stream .close ()
417407 if stdout and stderr :
@@ -528,9 +518,8 @@ def test_remote_server(uri, log=True):
528518 :param log: Log status message or not.
529519 :return ``True`` if server is running, ``False`` otherwise.
530520 """
531- server = ServerProxy (uri )
532521 try :
533- server .get_keyword_names ()
522+ ServerProxy ( uri ) .get_keyword_names ()
534523 except Exception :
535524 if log :
536525 print ('No remote server running at %s.' % uri )
@@ -545,18 +534,17 @@ def stop_remote_server(uri, log=True):
545534
546535 :param uri: Server address.
547536 :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.
550539 """
551540 if not test_remote_server (uri , log = False ):
552541 if log :
553542 print ('No remote server running at %s.' % uri )
554543 return True
555- server = ServerProxy (uri )
556544 if log :
557545 print ('Stopping remote server at %s.' % uri )
558546 args = [] if log else [False ]
559- return server .stop_remote_server (* args )
547+ return ServerProxy ( uri ) .stop_remote_server (* args )
560548
561549
562550if __name__ == '__main__' :
0 commit comments