16
16
from __future__ import print_function
17
17
18
18
from collections import Mapping
19
- import errno
20
19
import inspect
20
+ import os
21
21
import re
22
22
import signal
23
- import select
24
23
import sys
24
+ import threading
25
25
import traceback
26
26
27
27
if sys .version_info < (3 ,):
@@ -62,13 +62,11 @@ def __init__(self, library, host='127.0.0.1', port=8270, port_file=None,
62
62
:param allow_stop: Allow/disallow stopping the server using
63
63
``Stop Remote Server`` keyword.
64
64
"""
65
- self ._server = StoppableXMLRPCServer (host , int (port ))
65
+ self ._server = StoppableXMLRPCServer (host , int (port ), port_file )
66
66
self ._library = RemoteLibraryFactory (library )
67
67
self ._allow_stop = allow_stop
68
68
self ._register_functions (self ._server )
69
- self ._register_signal_handlers ()
70
- self ._announce_start (port_file )
71
- self ._server .start ()
69
+ self ._server .serve ()
72
70
73
71
@property
74
72
def server_address (self ):
@@ -81,29 +79,13 @@ def _register_functions(self, server):
81
79
server .register_function (self .get_keyword_documentation )
82
80
server .register_function (self .stop_remote_server )
83
81
84
- def _register_signal_handlers (self ):
85
- def stop_with_signal (signum , frame ):
86
- self ._allow_stop = True
87
- self .stop_remote_server ()
88
- for name in 'SIGINT' , 'SIGTERM' , 'SIGHUP' :
89
- if hasattr (signal , name ):
90
- signal .signal (getattr (signal , name ), stop_with_signal )
91
-
92
- def _announce_start (self , port_file = None ):
93
- host , port = self .server_address
94
- self ._log ('Robot Framework remote server at %s:%s starting.'
95
- % (host , port ))
96
- if port_file :
97
- with open (port_file , 'w' ) as pf :
98
- pf .write (str (port ))
99
-
100
82
def stop_remote_server (self ):
101
- prefix = 'Robot Framework remote server at %s:%s ' % self .server_address
102
83
if self ._allow_stop :
103
- self ._log (prefix + 'stopping.' )
104
- self ._server .stop ()
84
+ self ._server .stop_serve ()
105
85
return True
106
- self ._log (prefix + 'does not allow stopping.' , 'WARN' )
86
+ # TODO: Log to __stdout__? WARN?
87
+ print ('Robot Framework remote server at %s:%s does not allow stopping.'
88
+ % self .server_address )
107
89
return False
108
90
109
91
def _log (self , msg , level = None ):
@@ -140,24 +122,59 @@ def get_keyword_documentation(self, name):
140
122
class StoppableXMLRPCServer (SimpleXMLRPCServer ):
141
123
allow_reuse_address = True
142
124
143
- def __init__ (self , host , port ):
144
- SimpleXMLRPCServer .__init__ (self , (host , port ), logRequests = False )
145
- self ._shutdown = False
125
+ def __init__ (self , host , port , port_file = None ):
126
+ SimpleXMLRPCServer .__init__ (self , (host , port ), logRequests = False ,
127
+ bind_and_activate = False )
128
+ self ._port_file = port_file
129
+ self ._thread = None
130
+ self ._stop_server = threading .Event ()
131
+
132
+ def start (self , log = False ):
133
+ self .server_bind ()
134
+ self .server_activate ()
135
+ self ._thread = threading .Thread (target = self .serve_forever )
136
+ self ._thread .start ()
137
+ self ._announce_start (log , self ._port_file )
138
+
139
+ def _announce_start (self , log_start , port_file ):
140
+ # TODO: starting -> started
141
+ if log_start :
142
+ print ('Robot Framework remote server at %s:%s starting.'
143
+ % self .server_address )
144
+ if port_file :
145
+ with open (port_file , 'w' ) as pf :
146
+ pf .write (str (self .server_address [1 ]))
147
+
148
+ def stop (self , log = False ):
149
+ self .shutdown ()
150
+ self .server_close ()
151
+ self ._thread .join ()
152
+ self ._announce_end (log , self ._port_file )
153
+
154
+ def _announce_end (self , log_end , port_file ):
155
+ # TODO: stopping -> stopped
156
+ if log_end :
157
+ print ('Robot Framework remote server at %s:%s stopping.'
158
+ % self .server_address )
159
+ if port_file and os .path .exists (port_file ):
160
+ os .remove (port_file ) # TODO: Document that port file is removed
161
+
162
+ def serve (self , log = True ):
163
+ self ._stop_server .clear ()
164
+ self ._register_signal_handlers () # TODO: use as context manager!
165
+ self .start (log )
166
+ while not self ._stop_server .is_set ():
167
+ self ._stop_server .wait (1 )
168
+ self .stop (log )
146
169
147
- def start (self ):
148
- if hasattr (self , 'timeout' ):
149
- self .timeout = 0.5
150
- elif sys .platform .startswith ('java' ):
151
- self .socket .settimeout (0.5 )
152
- while not self ._shutdown :
153
- try :
154
- self .handle_request ()
155
- except (OSError , select .error ) as err :
156
- if err .args [0 ] != errno .EINTR :
157
- raise
170
+ def _register_signal_handlers (self ):
171
+ for name in 'SIGINT' , 'SIGTERM' , 'SIGHUP' :
172
+ if hasattr (signal , name ):
173
+ signal .signal (getattr (signal , name ),
174
+ lambda signum , frame : self .stop_serve ())
158
175
159
- def stop (self ):
160
- self ._shutdown = True
176
+ def stop_serve (self ):
177
+ self ._stop_server . set ()
161
178
162
179
163
180
def RemoteLibraryFactory (library ):
0 commit comments