300 lines
10 KiB
Python
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() |