Python client library for Appium mobile automation framework extending Selenium WebDriver with iOS and Android testing capabilities
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Appium server lifecycle management including startup, shutdown, and connectivity validation for local and remote testing scenarios. These capabilities enable automated server management and testing infrastructure setup.
Service class for managing Appium server processes with startup, shutdown, and monitoring capabilities.
class AppiumService:
def __init__(self):
"""Initialize Appium service instance."""
def start(self, **kwargs):
"""
Start Appium server with specified options.
Args:
**kwargs: Server startup options
timeout (int): Startup timeout in seconds
args (list): Additional command line arguments
env (dict): Environment variables
stdout (file): Stdout redirection
stderr (file): Stderr redirection
Raises:
AppiumServiceError: If service fails to start
"""
def stop(self, timeout: int = None):
"""
Stop running Appium server.
Args:
timeout (int, optional): Shutdown timeout in seconds
Raises:
AppiumServiceError: If service fails to stop cleanly
"""
@property
def is_running(self) -> bool:
"""
Check if Appium service is currently running.
Returns:
bool: True if service is running, False otherwise
"""
@property
def is_listening(self) -> bool:
"""
Check if service is listening on configured port.
Returns:
bool: True if service is accepting connections, False otherwise
"""Standalone utility functions for service discovery, validation, and executable location.
def is_service_listening(url: str, timeout: int = None, custom_validator = None) -> bool:
"""
Check if service is listening at specified URL.
Args:
url (str): Service URL to check
timeout (int, optional): Connection timeout in seconds
custom_validator (callable, optional): Custom validation function
Returns:
bool: True if service is listening and responsive
"""
def find_executable(executable: str) -> str:
"""
Find executable in system PATH.
Args:
executable (str): Executable name to find
Returns:
str: Full path to executable
Raises:
FileNotFoundError: If executable not found in PATH
"""
def get_node() -> str:
"""
Get Node.js executable path.
Returns:
str: Path to node executable
Raises:
FileNotFoundError: If Node.js not found
"""
def get_npm() -> str:
"""
Get NPM executable path.
Returns:
str: Path to npm executable
Raises:
FileNotFoundError: If NPM not found
"""Exception classes for service-related errors and diagnostics.
class AppiumServiceError(Exception):
"""General Appium service error."""
pass
class AppiumStartupError(AppiumServiceError):
"""Exception raised when Appium service fails to start."""
passfrom appium.webdriver.appium_service import AppiumService
from appium import webdriver
from appium.options.android import UiAutomator2Options
import time
# Create and configure service
service = AppiumService()
try:
# Start Appium server
print("Starting Appium service...")
service.start()
# Wait for service to be ready
timeout = 30
start_time = time.time()
while not service.is_listening and (time.time() - start_time) < timeout:
time.sleep(1)
if service.is_listening:
print("Appium service is ready")
# Create WebDriver session
options = UiAutomator2Options()
options.platform_name = "Android"
options.device_name = "Android Emulator"
driver = webdriver.Remote("http://localhost:4723", options=options)
# Run tests
perform_tests(driver)
# Clean up
driver.quit()
else:
print("Service failed to start within timeout")
except Exception as e:
print(f"Error during testing: {e}")
finally:
# Always stop service
if service.is_running:
print("Stopping Appium service...")
service.stop()
def perform_tests(driver):
"""Placeholder for test logic."""
passimport subprocess
import os
# Start service with custom configuration
def start_appium_with_config():
service = AppiumService()
# Custom environment variables
env = os.environ.copy()
env['APPIUM_LOG_LEVEL'] = 'debug'
env['ANDROID_HOME'] = '/path/to/android/sdk'
# Custom command line arguments
args = [
'--port', '4724',
'--session-override',
'--relaxed-security',
'--log-timestamp',
'--log', '/tmp/appium.log'
]
# Start with custom configuration
service.start(
args=args,
env=env,
timeout=60
)
return service
# Usage
service = start_appium_with_config()
if service.is_listening:
print("Custom Appium service started successfully")
# Use service with WebDriver at http://localhost:4724
else:
print("Failed to start custom service")from appium.webdriver.appium_service import is_service_listening, find_executable
# Check if Appium server is already running
def check_existing_service():
urls_to_check = [
"http://localhost:4723",
"http://localhost:4724",
"http://127.0.0.1:4723"
]
for url in urls_to_check:
if is_service_listening(url, timeout=5):
print(f"Found running Appium service at {url}")
return url
print("No running Appium service found")
return None
# Find required executables
def check_prerequisites():
"""Check if required tools are available."""
try:
node_path = find_executable("node")
print(f"Node.js found at: {node_path}")
npm_path = find_executable("npm")
print(f"NPM found at: {npm_path}")
appium_path = find_executable("appium")
print(f"Appium found at: {appium_path}")
return True
except FileNotFoundError as e:
print(f"Missing prerequisite: {e}")
return False
# Custom service validator
def custom_validator(response):
"""Custom validation for service health."""
if response.status_code == 200:
data = response.json()
return data.get('status') == 0 # Appium returns status 0 for success
return False
# Check service with custom validation
def validate_service_health(url):
return is_service_listening(
url,
timeout=10,
custom_validator=custom_validator
)
# Usage
if check_prerequisites():
existing_service = check_existing_service()
if existing_service:
if validate_service_health(existing_service):
print("Service is healthy and ready")
else:
print("Service is running but not healthy")class MultiServiceManager:
"""Manage multiple Appium services for parallel testing."""
def __init__(self):
self.services = {}
def start_service(self, name, port, **kwargs):
"""Start named service on specific port."""
service = AppiumService()
args = kwargs.get('args', [])
args.extend(['--port', str(port)])
kwargs['args'] = args
try:
service.start(**kwargs)
# Wait for service to be ready
service_url = f"http://localhost:{port}"
if self._wait_for_service(service_url):
self.services[name] = {
'service': service,
'port': port,
'url': service_url
}
print(f"Service '{name}' started on port {port}")
return True
else:
service.stop()
return False
except Exception as e:
print(f"Failed to start service '{name}': {e}")
return False
def stop_service(self, name):
"""Stop named service."""
if name in self.services:
service_info = self.services[name]
service_info['service'].stop()
del self.services[name]
print(f"Service '{name}' stopped")
def stop_all(self):
"""Stop all managed services."""
for name in list(self.services.keys()):
self.stop_service(name)
def get_service_url(self, name):
"""Get URL for named service."""
return self.services.get(name, {}).get('url')
def _wait_for_service(self, url, timeout=30):
"""Wait for service to be ready."""
import time
start_time = time.time()
while (time.time() - start_time) < timeout:
if is_service_listening(url, timeout=2):
return True
time.sleep(1)
return False
# Usage example
manager = MultiServiceManager()
try:
# Start services for parallel testing
manager.start_service("android_service", 4723)
manager.start_service("ios_service", 4724)
# Run parallel tests
android_url = manager.get_service_url("android_service")
ios_url = manager.get_service_url("ios_service")
if android_url and ios_url:
# Create drivers for both platforms
android_driver = create_android_driver(android_url)
ios_driver = create_ios_driver(ios_url)
# Run tests in parallel
run_parallel_tests(android_driver, ios_driver)
# Clean up drivers
android_driver.quit()
ios_driver.quit()
finally:
# Stop all services
manager.stop_all()from appium.webdriver.appium_service import AppiumServiceError, AppiumStartupError
import time
def robust_service_start(max_retries=3, base_port=4723):
"""Start Appium service with retry logic and port selection."""
for attempt in range(max_retries):
port = base_port + attempt
service = AppiumService()
try:
print(f"Attempt {attempt + 1}: Starting service on port {port}")
args = ['--port', str(port), '--session-override']
service.start(args=args, timeout=60)
# Verify service is responding
service_url = f"http://localhost:{port}"
if wait_for_service_ready(service_url, timeout=30):
print(f"Service successfully started on port {port}")
return service, service_url
else:
print(f"Service started but not responding on port {port}")
service.stop()
except AppiumStartupError as e:
print(f"Startup failed on port {port}: {e}")
try:
service.stop()
except:
pass
except Exception as e:
print(f"Unexpected error on port {port}: {e}")
try:
service.stop()
except:
pass
# Wait before next attempt
if attempt < max_retries - 1:
time.sleep(5)
raise AppiumServiceError(f"Failed to start service after {max_retries} attempts")
def wait_for_service_ready(url, timeout=30):
"""Wait for service to be ready with health check."""
start_time = time.time()
while (time.time() - start_time) < timeout:
try:
if is_service_listening(url, timeout=5):
# Additional health check
response = requests.get(f"{url}/status", timeout=5)
if response.status_code == 200:
return True
except:
pass
time.sleep(2)
return False
# Usage with error handling
try:
service, service_url = robust_service_start()
# Use service for testing
options = UiAutomator2Options()
driver = webdriver.Remote(service_url, options=options)
# Run tests
run_tests(driver)
except AppiumServiceError as e:
print(f"Service management error: {e}")
# Handle service error (e.g., use remote service)
except Exception as e:
print(f"Test execution error: {e}")
finally:
# Clean up
if 'driver' in locals():
driver.quit()
if 'service' in locals() and service.is_running:
service.stop()# Service types
ServiceURL = str # Format: "http://hostname:port"
Port = int # Port number (typically 4723)
ExecutablePath = str # Full path to executable
Timeout = int # Timeout in seconds
# Service configuration
ServiceArgs = List[str] # Command line arguments
ServiceEnv = Dict[str, str] # Environment variables
ServiceOptions = Dict[str, Union[int, List[str], Dict[str, str]]]
# Service state
ServiceStatus = bool # Running/stopped state
ConnectionStatus = bool # Listening/not listening
# Exception types
ServiceError = AppiumServiceError
StartupError = AppiumStartupError
# Validator function type
ValidatorFunction = Callable[[Any], bool]Install with Tessl CLI
npx tessl i tessl/pypi-appium-python-client