EU-Utility/plugins/integration_tests/scripts/platform_detector.py

300 lines
10 KiB
Python

#!/usr/bin/env python3
"""
EU-Utility Platform Detection Utility
======================================
Detects platform capabilities and available features.
Can be run standalone to check system compatibility.
Usage:
python platform_detector.py
python platform_detector.py --json
python platform_detector.py --markdown
"""
import sys
import os
import platform
import subprocess
import json
from typing import Dict, Any, List
from dataclasses import dataclass, asdict
@dataclass
class PlatformCapabilities:
"""Container for platform capabilities."""
platform: str
system: str
release: str
version: str
machine: str
processor: str
python_version: str
# Feature availability
has_window_manager: bool
has_native_hotkeys: bool
has_global_hotkeys: bool
has_fcntl: bool
has_portalocker: bool
has_requests: bool
has_paho_mqtt: bool
has_easyocr: bool
has_pytesseract: bool
has_paddleocr: bool
has_psutil: bool
has_aiohttp: bool
has_websockets: bool
# System capabilities
supports_long_paths: bool
supports_unicode_paths: bool
has_wsl: bool
class PlatformDetector:
"""Detects platform capabilities."""
def __init__(self):
self.is_windows = sys.platform == 'win32'
self.is_linux = sys.platform == 'linux'
self.is_mac = sys.platform == 'darwin'
def detect(self) -> PlatformCapabilities:
"""Detect all platform capabilities."""
return PlatformCapabilities(
platform=platform.platform(),
system=platform.system(),
release=platform.release(),
version=platform.version(),
machine=platform.machine(),
processor=platform.processor() or 'Unknown',
python_version=platform.python_version(),
has_window_manager=self._check_window_manager(),
has_native_hotkeys=self._check_native_hotkeys(),
has_global_hotkeys=self._check_global_hotkeys(),
has_fcntl=self._check_module('fcntl'),
has_portalocker=self._check_module('portalocker'),
has_requests=self._check_module('requests'),
has_paho_mqtt=self._check_module('paho.mqtt.client'),
has_easyocr=self._check_module('easyocr'),
has_pytesseract=self._check_module('pytesseract'),
has_paddleocr=self._check_module('paddleocr'),
has_psutil=self._check_module('psutil'),
has_aiohttp=self._check_module('aiohttp'),
has_websockets=self._check_module('websockets'),
supports_long_paths=self._check_long_paths(),
supports_unicode_paths=self._check_unicode_paths(),
has_wsl=self._check_wsl(),
)
def _check_module(self, name: str) -> bool:
"""Check if a module is available."""
try:
__import__(name)
return True
except ImportError:
return False
def _check_window_manager(self) -> bool:
"""Check if window manager features are available."""
if self.is_windows:
try:
import ctypes
return True
except ImportError:
return False
elif self.is_linux:
# Check for X11 or Wayland
return 'DISPLAY' in os.environ or 'WAYLAND_DISPLAY' in os.environ
elif self.is_mac:
# Limited window manager support on Mac
return False
return False
def _check_native_hotkeys(self) -> bool:
"""Check if native hotkeys are available."""
if self.is_windows:
try:
import ctypes
return True
except ImportError:
return False
elif self.is_linux:
# Check for xbindkeys or similar
return subprocess.run(['which', 'xbindkeys'], capture_output=True).returncode == 0
return False
def _check_global_hotkeys(self) -> bool:
"""Check if global hotkeys are available."""
# PyQt6 global hotkeys work on all platforms
return True
def _check_long_paths(self) -> bool:
"""Check if long paths are supported."""
if self.is_windows:
# Windows 10 1607+ supports long paths
try:
version = tuple(map(int, platform.version().split('.')))
return version >= (10, 0, 14393)
except:
return False
return True # Linux/Mac always support long paths
def _check_unicode_paths(self) -> bool:
"""Check if Unicode paths are supported."""
import tempfile
from pathlib import Path
try:
test_path = Path(tempfile.gettempdir()) / "测试_unicode_🎮"
test_path.mkdir(exist_ok=True)
test_path.rmdir()
return True
except:
return False
def _check_wsl(self) -> bool:
"""Check if running in WSL."""
if self.is_linux:
try:
with open('/proc/version', 'r') as f:
return 'microsoft' in f.read().lower()
except:
pass
return False
def print_report(self, caps: PlatformCapabilities):
"""Print human-readable report."""
print("=" * 60)
print("EU-Utility Platform Detection Report")
print("=" * 60)
print("\n📋 Platform Information:")
print(f" System: {caps.system}")
print(f" Release: {caps.release}")
print(f" Version: {caps.version}")
print(f" Machine: {caps.machine}")
print(f" Processor: {caps.processor}")
print(f" Python: {caps.python_version}")
if caps.has_wsl:
print(" ⚠️ Running in WSL")
print("\n🔧 Core Features:")
print(f" Window Manager: {'' if caps.has_window_manager else ''}")
print(f" Native Hotkeys: {'' if caps.has_native_hotkeys else ''}")
print(f" Global Hotkeys: {'' if caps.has_global_hotkeys else ''}")
print("\n🔒 File Locking:")
print(f" fcntl: {'' if caps.has_fcntl else ''}")
print(f" portalocker: {'' if caps.has_portalocker else ''}")
print("\n🌐 Network & Communication:")
print(f" requests: {'' if caps.has_requests else ''}")
print(f" paho-mqtt: {'' if caps.has_paho_mqtt else ''}")
print(f" aiohttp: {'' if caps.has_aiohttp else ''}")
print(f" websockets: {'' if caps.has_websockets else ''}")
print("\n📷 OCR Engines:")
print(f" EasyOCR: {'' if caps.has_easyocr else ''}")
print(f" Tesseract: {'' if caps.has_pytesseract else ''}")
print(f" PaddleOCR: {'' if caps.has_paddleocr else ''}")
print("\n🛠️ Utilities:")
print(f" psutil: {'' if caps.has_psutil else ''}")
print("\n📁 Path Support:")
print(f" Long Paths: {'' if caps.supports_long_paths else ''}")
print(f" Unicode Paths: {'' if caps.supports_unicode_paths else ''}")
# Recommendations
print("\n💡 Recommendations:")
recommendations = self._get_recommendations(caps)
if recommendations:
for rec in recommendations:
print(f"{rec}")
else:
print(" All core dependencies satisfied!")
print("\n" + "=" * 60)
def _get_recommendations(self, caps: PlatformCapabilities) -> List[str]:
"""Get installation recommendations."""
recs = []
if not caps.has_requests:
recs.append("Install requests: pip install requests")
if not caps.has_psutil:
recs.append("Install psutil for system info: pip install psutil")
if not any([caps.has_easyocr, caps.has_pytesseract, caps.has_paddleocr]):
recs.append("Install an OCR engine: pip install easyocr (or pytesseract, paddleocr)")
if self.is_windows and not caps.has_portalocker:
recs.append("Install portalocker for file locking: pip install portalocker")
if not caps.has_aiohttp:
recs.append("Install aiohttp for async HTTP: pip install aiohttp")
return recs
def print_json(self, caps: PlatformCapabilities):
"""Print JSON report."""
print(json.dumps(asdict(caps), indent=2))
def print_markdown(self, caps: PlatformCapabilities):
"""Print Markdown report."""
print("# EU-Utility Platform Report")
print()
print("## Platform Information")
print()
print(f"| Property | Value |")
print(f"|----------|-------|")
print(f"| System | {caps.system} |")
print(f"| Release | {caps.release} |")
print(f"| Version | {caps.version} |")
print(f"| Machine | {caps.machine} |")
print(f"| Processor | {caps.processor} |")
print(f"| Python | {caps.python_version} |")
print()
print("## Feature Support")
print()
print(f"| Feature | Status |")
print(f"|---------|--------|")
print(f"| Window Manager | {'' if caps.has_window_manager else ''} |")
print(f"| Native Hotkeys | {'' if caps.has_native_hotkeys else ''} |")
print(f"| Global Hotkeys | {'' if caps.has_global_hotkeys else ''} |")
print(f"| File Locking | {'' if (caps.has_fcntl or caps.has_portalocker) else ''} |")
print(f"| Network (requests) | {'' if caps.has_requests else ''} |")
print(f"| MQTT | {'' if caps.has_paho_mqtt else ''} |")
print(f"| OCR | {'' if any([caps.has_easyocr, caps.has_pytesseract, caps.has_paddleocr]) else ''} |")
print()
def main():
import argparse
parser = argparse.ArgumentParser(description="EU-Utility Platform Detector")
parser.add_argument("--json", action="store_true", help="Output JSON format")
parser.add_argument("--markdown", action="store_true", help="Output Markdown format")
args = parser.parse_args()
detector = PlatformDetector()
caps = detector.detect()
if args.json:
detector.print_json(caps)
elif args.markdown:
detector.print_markdown(caps)
else:
detector.print_report(caps)
if __name__ == "__main__":
main()