1212# See the License for the specific language governing permissions and
1313# limitations under the License.
1414
15-
1615import os
17- import subprocess
16+ import subprocess as sp
1817import sys
1918import time
19+ from typing import Any , List , Optional , TypeVar , Union
2020
2121import urllib3
2222
2727STATUS_URL = '/wd/hub/status'
2828
2929
30- def find_executable (executable ) :
30+ def find_executable (executable : str ) -> Optional [ str ] :
3131 path = os .environ ['PATH' ]
3232 paths = path .split (os .pathsep )
3333 base , ext = os .path .splitext (executable )
@@ -45,7 +45,7 @@ def find_executable(executable):
4545 return None
4646
4747
48- def poll_url (host , port , path , timeout_ms ) :
48+ def poll_url (host : str , port : int , path : str , timeout_ms : int ) -> bool :
4949 time_started_sec = time .time ()
5050 while time .time () < time_started_sec + timeout_ms / 1000.0 :
5151 try :
@@ -64,62 +64,65 @@ class AppiumServiceError(RuntimeError):
6464 pass
6565
6666
67+ T = TypeVar ('T' , bound = 'AppiumService' )
68+
69+
6770class AppiumService (object ):
68- def __init__ (self ):
69- self ._process = None
70- self ._cmd = None
71+ def __init__ (self ) -> None :
72+ self ._process : Optional [ sp . Popen ] = None
73+ self ._cmd : Optional [ List ] = None
7174
72- def _get_node (self ):
75+ def _get_node (self ) -> str :
7376 if not hasattr (self , '_node_executable' ):
7477 self ._node_executable = find_executable ('node' )
7578 if self ._node_executable is None :
7679 raise AppiumServiceError ('NodeJS main executable cannot be found. ' +
7780 'Make sure it is installed and present in PATH' )
7881 return self ._node_executable
7982
80- def _get_npm (self ):
83+ def _get_npm (self ) -> str :
8184 if not hasattr (self , '_npm_executable' ):
8285 self ._npm_executable = find_executable ('npm.cmd' if sys .platform == 'win32' else 'npm' )
8386 if self ._npm_executable is None :
8487 raise AppiumServiceError ('Node Package Manager executable cannot be found. ' +
8588 'Make sure it is installed and present in PATH' )
8689 return self ._npm_executable
8790
88- def _get_main_script (self ):
91+ def _get_main_script (self ) -> Union [ str , bytes ] :
8992 if not hasattr (self , '_main_script' ):
9093 for args in [['root' , '-g' ], ['root' ]]:
9194 try :
92- modules_root = subprocess .check_output ([self ._get_npm ()] + args ).strip ().decode ('utf-8' )
95+ modules_root = sp .check_output ([self ._get_npm ()] + args ).strip ().decode ('utf-8' )
9396 if os .path .exists (os .path .join (modules_root , MAIN_SCRIPT_PATH )):
94- self ._main_script = os .path .join (modules_root , MAIN_SCRIPT_PATH )
97+ self ._main_script : Union [ str , bytes ] = os .path .join (modules_root , MAIN_SCRIPT_PATH )
9598 break
96- except subprocess .CalledProcessError :
99+ except sp .CalledProcessError :
97100 continue
98101 if not hasattr (self , '_main_script' ):
99102 try :
100- self ._main_script = subprocess .check_output (
103+ self ._main_script = sp .check_output (
101104 [self ._get_node (),
102105 '-e' ,
103106 'console.log(require.resolve("{}"))' .format (MAIN_SCRIPT_PATH )]).strip ()
104- except subprocess .CalledProcessError as e :
107+ except sp .CalledProcessError as e :
105108 raise AppiumServiceError (e .output )
106109 return self ._main_script
107110
108111 @staticmethod
109- def _parse_port (args ) :
112+ def _parse_port (args : List [ str ]) -> int :
110113 for idx , arg in enumerate (args or []):
111114 if arg in ('--port' , '-p' ) and idx < len (args ) - 1 :
112115 return int (args [idx + 1 ])
113116 return DEFAULT_PORT
114117
115118 @staticmethod
116- def _parse_host (args ) :
119+ def _parse_host (args : List [ str ]) -> str :
117120 for idx , arg in enumerate (args or []):
118121 if arg in ('--address' , '-a' ) and idx < len (args ) - 1 :
119122 return args [idx + 1 ]
120123 return DEFAULT_HOST
121124
122- def start (self , ** kwargs ) :
125+ def start (self , ** kwargs : Any ) -> sp . Popen :
123126 """Starts Appium service with given arguments.
124127
125128 The service will be forcefully restarted if it is already running.
@@ -153,31 +156,31 @@ def start(self, **kwargs):
153156
154157 env = kwargs ['env' ] if 'env' in kwargs else None
155158 node = kwargs ['node' ] if 'node' in kwargs else self ._get_node ()
156- stdout = kwargs ['stdout' ] if 'stdout' in kwargs else subprocess .PIPE
157- stderr = kwargs ['stderr' ] if 'stderr' in kwargs else subprocess .PIPE
159+ stdout = kwargs ['stdout' ] if 'stdout' in kwargs else sp .PIPE
160+ stderr = kwargs ['stderr' ] if 'stderr' in kwargs else sp .PIPE
158161 timeout_ms = int (kwargs ['timeout_ms' ]) if 'timeout_ms' in kwargs else STARTUP_TIMEOUT_MS
159162 main_script = kwargs ['main_script' ] if 'main_script' in kwargs else self ._get_main_script ()
160163 args = [node , main_script ]
161164 if 'args' in kwargs :
162165 args .extend (kwargs ['args' ])
163166 self ._cmd = args
164- self ._process = subprocess .Popen (args = args , stdout = stdout , stderr = stderr , env = env )
167+ self ._process = sp .Popen (args = args , stdout = stdout , stderr = stderr , env = env )
165168 host = self ._parse_host (args )
166169 port = self ._parse_port (args )
167- error_msg = None
170+ error_msg : Optional [ str ] = None
168171 if not self .is_running or (timeout_ms > 0 and not poll_url (host , port , STATUS_URL , timeout_ms )):
169172 error_msg = 'Appium has failed to start on {}:{} within {}ms timeout' \
170173 .format (host , port , timeout_ms )
171174 if error_msg is not None :
172- if stderr == subprocess .PIPE :
175+ if stderr == sp .PIPE :
173176 err_output = self ._process .stderr .read ()
174177 if err_output :
175- error_msg += '\n Original error: {}' .format (err_output )
178+ error_msg += '\n Original error: {}' .format (str ( err_output ) )
176179 self .stop ()
177180 raise AppiumServiceError (error_msg )
178181 return self ._process
179182
180- def stop (self ):
183+ def stop (self ) -> bool :
181184 """Stops Appium service if it is running.
182185
183186 The call will be ignored if the service is not running
@@ -188,14 +191,14 @@ def stop(self):
188191 """
189192 is_terminated = False
190193 if self .is_running :
191- self ._process .terminate ()
194+ self ._process .terminate () # type: ignore
192195 is_terminated = True
193196 self ._process = None
194197 self ._cmd = None
195198 return is_terminated
196199
197200 @property
198- def is_running (self ):
201+ def is_running (self ) -> bool :
199202 """Check if the service is running.
200203
201204 Returns:
@@ -204,7 +207,7 @@ def is_running(self):
204207 return self ._process is not None and self ._process .poll () is None
205208
206209 @property
207- def is_listening (self ):
210+ def is_listening (self ) -> bool :
208211 """Check if the service is listening on the given/default host/port.
209212
210213 The fact, that the service is running, does not always mean it is listening.
0 commit comments