14
14
# KIND, either express or implied. See the License for the
15
15
# specific language governing permissions and limitations
16
16
# under the License.
17
+ import contextlib
17
18
import errno
18
19
import os
19
20
import subprocess
21
+ import typing
20
22
from platform import system
21
23
from subprocess import DEVNULL
22
24
from subprocess import PIPE
29
31
30
32
31
33
class Service :
32
-
33
- def __init__ (self , executable , port = 0 , log_file = DEVNULL , env = None , start_error_message = "" ):
34
+ def __init__ (
35
+ self ,
36
+ executable : str ,
37
+ port : int = 0 ,
38
+ log_file = DEVNULL ,
39
+ env : typing .Optional [typing .Dict [typing .Any , typing .Any ]] = None ,
40
+ start_error_message : str = "" ,
41
+ ) -> None :
34
42
self .path = executable
35
-
36
- self .port = port
37
- if self .port == 0 :
38
- self .port = utils .free_port ()
39
-
40
- if not _HAS_NATIVE_DEVNULL and log_file == DEVNULL :
41
- log_file = open (os .devnull , 'wb' )
42
-
43
+ self .port = port or utils .free_port ()
44
+ self .log_file = open (os .devnull , "wb" ) if not _HAS_NATIVE_DEVNULL and log_file == DEVNULL else log_file
43
45
self .start_error_message = start_error_message
44
- self .log_file = log_file
45
46
# Default value for every python subprocess: subprocess.Popen(..., creationflags=0)
46
- self .creationflags = 0
47
+ self .creation_flags = 0
47
48
self .env = env or os .environ
49
+ self .process = None
48
50
49
51
@property
50
52
def service_url (self ):
@@ -67,31 +69,37 @@ def start(self):
67
69
try :
68
70
cmd = [self .path ]
69
71
cmd .extend (self .command_line_args ())
70
- self .process = subprocess .Popen (cmd , env = self .env ,
71
- close_fds = system () != 'Windows' ,
72
- stdout = self .log_file ,
73
- stderr = self .log_file ,
74
- stdin = PIPE ,
75
- creationflags = self .creationflags )
72
+ self .process = subprocess .Popen (
73
+ cmd ,
74
+ env = self .env ,
75
+ close_fds = system () != "Windows" ,
76
+ stdout = self .log_file ,
77
+ stderr = self .log_file ,
78
+ stdin = PIPE ,
79
+ creationflags = self .creation_flags ,
80
+ )
76
81
except TypeError :
77
82
raise
78
83
except OSError as err :
79
84
if err .errno == errno .ENOENT :
80
85
raise WebDriverException (
81
86
"'{}' executable needs to be in PATH. {}" .format (
82
- os .path .basename (self .path ), self .start_error_message )
87
+ os .path .basename (self .path ), self .start_error_message
88
+ )
83
89
)
84
90
elif err .errno == errno .EACCES :
85
91
raise WebDriverException (
86
92
"'{}' executable may have wrong permissions. {}" .format (
87
- os .path .basename (self .path ), self .start_error_message )
93
+ os .path .basename (self .path ), self .start_error_message
94
+ )
88
95
)
89
96
else :
90
97
raise
91
98
except Exception as e :
92
99
raise WebDriverException (
93
- "The executable %s needs to be available in the path. %s\n %s" %
94
- (os .path .basename (self .path ), self .start_error_message , str (e )))
100
+ "The executable %s needs to be available in the path. %s\n %s"
101
+ % (os .path .basename (self .path ), self .start_error_message , str (e ))
102
+ )
95
103
count = 0
96
104
while True :
97
105
self .assert_process_still_running ()
@@ -106,19 +114,17 @@ def start(self):
106
114
def assert_process_still_running (self ):
107
115
return_code = self .process .poll ()
108
116
if return_code :
109
- raise WebDriverException (
110
- 'Service %s unexpectedly exited. Status code was: %s'
111
- % (self .path , return_code )
112
- )
117
+ raise WebDriverException (f"Service { self .path } unexpectedly exited. Status code was: { return_code } " )
113
118
114
119
def is_connectable (self ):
115
120
return utils .is_connectable (self .port )
116
121
117
122
def send_remote_shutdown_command (self ):
118
123
from urllib import request
119
124
from urllib .error import URLError
125
+
120
126
try :
121
- request .urlopen ("%s/shutdown" % self .service_url )
127
+ request .urlopen (f" { self .service_url } /shutdown" )
122
128
except URLError :
123
129
return
124
130
@@ -127,15 +133,14 @@ def send_remote_shutdown_command(self):
127
133
break
128
134
sleep (1 )
129
135
130
- def stop (self ):
136
+ def stop (self ) -> None :
131
137
"""
132
138
Stops the service.
133
139
"""
134
140
if self .log_file != PIPE and not (self .log_file == DEVNULL and _HAS_NATIVE_DEVNULL ):
135
- try :
141
+ with contextlib .suppress (Exception ):
142
+ # Todo: Be explicit in what we are catching here.
136
143
self .log_file .close ()
137
- except Exception :
138
- pass
139
144
140
145
if not self .process :
141
146
return
@@ -147,9 +152,7 @@ def stop(self):
147
152
148
153
try :
149
154
if self .process :
150
- for stream in [self .process .stdin ,
151
- self .process .stdout ,
152
- self .process .stderr ]:
155
+ for stream in [self .process .stdin , self .process .stdout , self .process .stderr ]:
153
156
try :
154
157
stream .close ()
155
158
except AttributeError :
@@ -161,11 +164,9 @@ def stop(self):
161
164
except OSError :
162
165
pass
163
166
164
- def __del__ (self ):
167
+ def __del__ (self ) -> None :
165
168
# `subprocess.Popen` doesn't send signal on `__del__`;
166
169
# so we attempt to close the launched process when `__del__`
167
170
# is triggered.
168
- try :
171
+ with contextlib . suppress ( Exception ) :
169
172
self .stop ()
170
- except Exception :
171
- pass
0 commit comments