|
|
"""System requirements checking utilities.""" |
|
|
|
|
|
import os |
|
|
import sys |
|
|
import shutil |
|
|
import logging |
|
|
import subprocess |
|
|
from typing import Dict, Any, Optional |
|
|
from pathlib import Path |
|
|
|
|
|
logger = logging.getLogger(__name__) |
|
|
|
|
|
class SystemChecker: |
|
|
"""Checks system requirements and dependencies.""" |
|
|
|
|
|
|
|
|
REQUIRED_COMMANDS = { |
|
|
"python": { |
|
|
"min_version": (3, 8), |
|
|
"command": "python --version", |
|
|
"description": "Python interpreter" |
|
|
}, |
|
|
"manim": { |
|
|
"min_version": (0, 17, 0), |
|
|
"command": "manim --version", |
|
|
"description": "Manim library" |
|
|
}, |
|
|
"ffmpeg": { |
|
|
"min_version": (4, 0, 0), |
|
|
"command": "ffmpeg -version", |
|
|
"description": "FFmpeg for video processing" |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
REQUIRED_PACKAGES = { |
|
|
"manim": "0.17.0", |
|
|
"streamlit": "1.0.0", |
|
|
"groq": "0.3.0", |
|
|
"numpy": "1.20.0", |
|
|
"pillow": "8.0.0" |
|
|
} |
|
|
|
|
|
|
|
|
MIN_REQUIREMENTS = { |
|
|
"cpu_cores": 2, |
|
|
"ram_gb": 4, |
|
|
"disk_space_gb": 1, |
|
|
"gpu_memory_gb": 2 |
|
|
} |
|
|
|
|
|
def __init__(self): |
|
|
"""Initialize the system checker.""" |
|
|
self.system_info = {} |
|
|
self.missing_requirements = [] |
|
|
self.warnings = [] |
|
|
|
|
|
@classmethod |
|
|
def get_system_info(cls) -> Dict[str, Any]: |
|
|
"""Get system information and requirements status. |
|
|
|
|
|
Returns: |
|
|
Dictionary containing system information and requirements status |
|
|
""" |
|
|
checker = cls() |
|
|
checker._check_python_version() |
|
|
checker._check_commands() |
|
|
checker._check_packages() |
|
|
checker._check_system_resources() |
|
|
|
|
|
return { |
|
|
"python_version": checker.system_info.get("python_version", "Unknown"), |
|
|
"manim_installed": checker.system_info.get("manim_installed", False), |
|
|
"ffmpeg_installed": checker.system_info.get("ffmpeg_installed", False), |
|
|
"required_packages": checker.system_info.get("required_packages", {}), |
|
|
"system_resources": checker.system_info.get("system_resources", {}), |
|
|
"missing_requirements": checker.missing_requirements, |
|
|
"warnings": checker.warnings |
|
|
} |
|
|
|
|
|
def _check_python_version(self) -> None: |
|
|
"""Check Python version.""" |
|
|
version = sys.version_info |
|
|
self.system_info["python_version"] = f"{version.major}.{version.minor}.{version.micro}" |
|
|
|
|
|
min_version = self.REQUIRED_COMMANDS["python"]["min_version"] |
|
|
if version < min_version: |
|
|
self.missing_requirements.append( |
|
|
f"Python {min_version[0]}.{min_version[1]} or higher required" |
|
|
) |
|
|
|
|
|
def _check_commands(self) -> None: |
|
|
"""Check required command-line tools.""" |
|
|
for cmd, info in self.REQUIRED_COMMANDS.items(): |
|
|
if cmd == "python": |
|
|
continue |
|
|
|
|
|
try: |
|
|
result = subprocess.run( |
|
|
info["command"].split(), |
|
|
capture_output=True, |
|
|
text=True |
|
|
) |
|
|
|
|
|
if result.returncode == 0: |
|
|
version = self._parse_version(result.stdout) |
|
|
min_version = info["min_version"] |
|
|
|
|
|
if version >= min_version: |
|
|
self.system_info[f"{cmd}_installed"] = True |
|
|
self.system_info[f"{cmd}_version"] = ".".join(map(str, version)) |
|
|
else: |
|
|
self.missing_requirements.append( |
|
|
f"{info['description']} version {'.'.join(map(str, min_version))} " |
|
|
f"or higher required" |
|
|
) |
|
|
else: |
|
|
self.missing_requirements.append( |
|
|
f"{info['description']} not found" |
|
|
) |
|
|
|
|
|
except Exception as e: |
|
|
logger.error(f"Error checking {cmd}: {e}") |
|
|
self.missing_requirements.append( |
|
|
f"Error checking {info['description']}" |
|
|
) |
|
|
|
|
|
def _check_packages(self) -> None: |
|
|
"""Check required Python packages.""" |
|
|
import pkg_resources |
|
|
|
|
|
self.system_info["required_packages"] = {} |
|
|
|
|
|
for package, min_version in self.REQUIRED_PACKAGES.items(): |
|
|
try: |
|
|
installed = pkg_resources.get_distribution(package) |
|
|
self.system_info["required_packages"][package] = { |
|
|
"installed": True, |
|
|
"version": installed.version |
|
|
} |
|
|
|
|
|
if installed.version < min_version: |
|
|
self.missing_requirements.append( |
|
|
f"{package} version {min_version} or higher required" |
|
|
) |
|
|
|
|
|
except pkg_resources.DistributionNotFound: |
|
|
self.system_info["required_packages"][package] = { |
|
|
"installed": False, |
|
|
"version": None |
|
|
} |
|
|
self.missing_requirements.append(f"{package} not installed") |
|
|
|
|
|
def _check_system_resources(self) -> None: |
|
|
"""Check system resources.""" |
|
|
import psutil |
|
|
|
|
|
self.system_info["system_resources"] = {} |
|
|
|
|
|
|
|
|
cpu_count = psutil.cpu_count(logical=False) |
|
|
self.system_info["system_resources"]["cpu_cores"] = cpu_count |
|
|
if cpu_count < self.MIN_REQUIREMENTS["cpu_cores"]: |
|
|
self.warnings.append( |
|
|
f"Low CPU cores: {cpu_count} (recommended: {self.MIN_REQUIREMENTS['cpu_cores']}+)" |
|
|
) |
|
|
|
|
|
|
|
|
ram_gb = psutil.virtual_memory().total / (1024 ** 3) |
|
|
self.system_info["system_resources"]["ram_gb"] = round(ram_gb, 1) |
|
|
if ram_gb < self.MIN_REQUIREMENTS["ram_gb"]: |
|
|
self.warnings.append( |
|
|
f"Low RAM: {round(ram_gb, 1)}GB (recommended: {self.MIN_REQUIREMENTS['ram_gb']}+GB)" |
|
|
) |
|
|
|
|
|
|
|
|
disk_gb = psutil.disk_usage("/").free / (1024 ** 3) |
|
|
self.system_info["system_resources"]["disk_space_gb"] = round(disk_gb, 1) |
|
|
if disk_gb < self.MIN_REQUIREMENTS["disk_space_gb"]: |
|
|
self.warnings.append( |
|
|
f"Low disk space: {round(disk_gb, 1)}GB " |
|
|
f"(recommended: {self.MIN_REQUIREMENTS['disk_space_gb']}+GB)" |
|
|
) |
|
|
|
|
|
|
|
|
try: |
|
|
import torch |
|
|
if torch.cuda.is_available(): |
|
|
gpu_memory = torch.cuda.get_device_properties(0).total_memory / (1024 ** 3) |
|
|
self.system_info["system_resources"]["gpu_memory_gb"] = round(gpu_memory, 1) |
|
|
if gpu_memory < self.MIN_REQUIREMENTS["gpu_memory_gb"]: |
|
|
self.warnings.append( |
|
|
f"Low GPU memory: {round(gpu_memory, 1)}GB " |
|
|
f"(recommended: {self.MIN_REQUIREMENTS['gpu_memory_gb']}+GB)" |
|
|
) |
|
|
except ImportError: |
|
|
self.system_info["system_resources"]["gpu_memory_gb"] = None |
|
|
|
|
|
@staticmethod |
|
|
def _parse_version(version_str: str) -> tuple: |
|
|
"""Parse version string into tuple of integers.""" |
|
|
import re |
|
|
version_match = re.search(r'(\d+\.\d+(?:\.\d+)?)', version_str) |
|
|
if version_match: |
|
|
return tuple(map(int, version_match.group(1).split('.'))) |
|
|
return (0, 0, 0) |
|
|
|
|
|
@classmethod |
|
|
def check_requirements(cls) -> bool: |
|
|
"""Check if all system requirements are met. |
|
|
|
|
|
Returns: |
|
|
True if all requirements are met, False otherwise |
|
|
""" |
|
|
system_info = cls.get_system_info() |
|
|
return len(system_info["missing_requirements"]) == 0 |
|
|
|
|
|
@classmethod |
|
|
def get_installation_guide(cls) -> str: |
|
|
"""Get installation guide for missing requirements. |
|
|
|
|
|
Returns: |
|
|
Installation guide as a formatted string |
|
|
""" |
|
|
system_info = cls.get_system_info() |
|
|
|
|
|
if not system_info["missing_requirements"]: |
|
|
return "All requirements are satisfied." |
|
|
|
|
|
guide = ["Installation Guide:"] |
|
|
|
|
|
|
|
|
if "Python" in str(system_info["missing_requirements"]): |
|
|
guide.extend([ |
|
|
"\n1. Install Python:", |
|
|
" - Download from https://www.python.org/downloads/", |
|
|
" - Make sure to check 'Add Python to PATH' during installation" |
|
|
]) |
|
|
|
|
|
|
|
|
if not system_info["manim_installed"]: |
|
|
guide.extend([ |
|
|
"\n2. Install Manim:", |
|
|
" pip install manim", |
|
|
" - For detailed installation, visit: https://docs.manim.community/", |
|
|
" - Make sure to install system dependencies first" |
|
|
]) |
|
|
|
|
|
|
|
|
if not system_info["ffmpeg_installed"]: |
|
|
guide.extend([ |
|
|
"\n3. Install FFmpeg:", |
|
|
" - Windows: Download from https://ffmpeg.org/download.html", |
|
|
" - Linux: sudo apt-get install ffmpeg", |
|
|
" - macOS: brew install ffmpeg" |
|
|
]) |
|
|
|
|
|
|
|
|
missing_packages = [ |
|
|
pkg for pkg, info in system_info["required_packages"].items() |
|
|
if not info["installed"] |
|
|
] |
|
|
if missing_packages: |
|
|
guide.extend([ |
|
|
"\n4. Install required Python packages:", |
|
|
f" pip install {' '.join(missing_packages)}" |
|
|
]) |
|
|
|
|
|
return "\n".join(guide) |