AutoPercenty3/check_dependencies.py

273 lines
11 KiB
Python

# -*- coding: utf-8 -*-
"""
DirectML 기반 ONNX Runtime 호환성을 위한 의존성 버전 체크 및 교체 스크립트
"""
import subprocess
import sys
import logging
import importlib.util
# 설정: DirectML 기반 ONNX Runtime 호환성을 위한 고정 버전
REQUIRED_VERSIONS = {
'numpy': '1.26.4',
'scipy': '1.11.4', # 안정 버전
'onnxruntime-directml': '1.22.0', # DirectML 지원 ONNX Runtime
'opencv-python': '4.6.0.66', # OpenCV 호환 버전
}
# 프로세스가 사용 중일 때 건너뛸 패키지들
SKIP_IF_IN_USE = ['onnxruntime-directml']
def check_package_version(package_name):
"""패키지의 현재 설치된 버전을 확인합니다."""
try:
if package_name == 'numpy':
import numpy
return numpy.__version__
elif package_name == 'scipy':
# scipy는 직접 import해서 버전 확인
import scipy
return scipy.__version__
elif package_name == 'opencv-python':
# opencv-contrib-python과 충돌을 피하기 위해 pip show로 직접 확인
try:
result = subprocess.run([sys.executable, '-m', 'pip', 'show', package_name],
capture_output=True, text=True, timeout=30)
if result.returncode == 0:
for line in result.stdout.split('\n'):
if line.startswith('Version:'):
return line.split('Version:')[1].strip()
return None
except:
# fallback to cv2 version
import cv2
return cv2.__version__
elif package_name == 'onnxruntime-directml':
# onnxruntime-directml은 onnxruntime 모듈로 import됨
import onnxruntime
return onnxruntime.__version__
else:
# 기본 방법
spec = importlib.util.find_spec(package_name)
if spec is None:
return None
module = importlib.import_module(package_name)
return getattr(module, '__version__', None)
except ImportError:
return None
except Exception as e:
# import 오류가 발생하면 패키지가 깨진 것으로 간주
print(f"[경고] {package_name} import 오류 (깨진 설치): {str(e)[:100]}...")
return "BROKEN"
def parse_version(version_str):
"""버전 문자열을 파싱하여 비교 가능한 튜플로 변환합니다."""
try:
# 버전 문자열에서 숫자와 점만 추출
import re
clean_version = re.sub(r'[^\d\.]', '', version_str)
parts = clean_version.split('.')
# 각 부분을 정수로 변환 (빈 문자열은 0으로 처리)
return tuple(int(part) if part.isdigit() else 0 for part in parts)
except Exception:
return (0,)
def is_version_compatible(current_version, required_version):
"""현재 버전이 요구 버전과 호환되는지 확인합니다."""
if current_version is None:
return False
try:
# 정확한 버전 매치 확인
current_tuple = parse_version(current_version)
required_tuple = parse_version(required_version)
return current_tuple == required_tuple
except Exception as e:
print(f"버전 비교 중 오류: {e}")
return False
def install_package(package_name, package_version, force=False):
"""특정 버전의 패키지를 설치합니다."""
try:
package_spec = f"{package_name}=={package_version}"
if force:
# 강제 재설치
print(f"[새로고침] {package_spec} 강제 재설치 중...")
cmd = [sys.executable, '-m', 'pip', 'install', '--force-reinstall', '--no-deps', package_spec]
else:
# 일반 설치
print(f"[패키지] {package_spec} 설치 중...")
cmd = [sys.executable, '-m', 'pip', 'install', package_spec]
result = subprocess.run(cmd, capture_output=True, text=True, timeout=300)
if result.returncode == 0:
print(f"[성공] {package_spec} 설치 완료")
return True
else:
# 권한 문제 또는 파일 사용 중 오류 체크
error_output = result.stderr.lower()
if any(err in error_output for err in ['winError 5', '액세스가 거부', 'permission denied', 'access is denied']):
print(f"[경고] {package_spec} 권한 문제로 설치 실패")
print("[팁] 해결 방법:")
print(" 1. 관리자 권한으로 명령 프롬프트를 실행 후 재시도")
print(" 2. 또는 현재 Python 프로세스를 종료 후 재시도")
return False
elif 'already satisfied' in result.stdout.lower():
print(f"[성공] {package_spec} 이미 설치됨")
return True
else:
print(f"[실패] {package_spec} 설치 실패:")
print(f"stderr: {result.stderr[:500]}...") # 에러 메시지 길이 제한
return False
except subprocess.TimeoutExpired:
print(f"[시간초과] {package_spec} 설치 시간 초과 (5분)")
return False
except Exception as e:
print(f"[오류] {package_spec} 설치 중 예외: {e}")
return False
def uninstall_package(package_name):
"""패키지를 제거합니다."""
try:
print(f"[삭제] {package_name} 제거 중...")
cmd = [sys.executable, '-m', 'pip', 'uninstall', '-y', package_name]
result = subprocess.run(cmd, capture_output=True, text=True, timeout=120)
if result.returncode == 0:
print(f"[성공] {package_name} 제거 완료")
return True
else:
print(f"[경고] {package_name} 제거 실패 (이미 제거되었을 수 있음)")
return True # 제거 실패는 큰 문제가 아님
except subprocess.TimeoutExpired:
print(f"[시간초과] {package_name} 제거 시간 초과")
return False
except Exception as e:
print(f"[오류] {package_name} 제거 중 예외: {e}")
return False
def check_and_fix_dependencies():
"""의존성을 체크하고 필요시 교체합니다."""
print("[검색] DirectML 기반 ONNX Runtime 호환성을 위한 의존성 체크 시작")
print("=" * 60)
needs_restart = False
for package_name, required_version in REQUIRED_VERSIONS.items():
print(f"\n[체크] {package_name} 버전 체크 중...")
current_version = check_package_version(package_name)
if current_version is None:
print(f"[실패] {package_name}이 설치되지 않음")
if install_package(package_name, required_version):
needs_restart = True
else:
print(f"[오류] {package_name} 설치 실패")
return False
elif current_version == "BROKEN":
print(f"[오류] {package_name}이 깨진 상태로 설치됨 - 재설치 필요")
# 프로세스가 사용 중일 수 있는 패키지는 경고만 출력
if package_name in SKIP_IF_IN_USE:
print(f"[경고] {package_name}는 현재 프로세스에서 사용 중일 수 있어 교체를 건너뜁니다.")
print("[팁] 해결 방법:")
print(" 1. 현재 Python 프로세스를 완전히 종료")
print(" 2. fix_deps_admin.bat 실행 (관리자 권한)")
print(f" 3. 또는 수동으로: pip uninstall {package_name} && pip install {package_name}=={required_version}")
continue
# 깨진 패키지 강제 재설치
if uninstall_package(package_name):
if install_package(package_name, required_version):
needs_restart = True
else:
print(f"[경고] {package_name} 재설치 실패 - 수동 설치 필요")
else:
print(f"[경고] {package_name} 제거 실패 - 수동 정리 필요")
elif not is_version_compatible(current_version, required_version):
print(f"[경고] {package_name} 버전 불일치:")
print(f" 현재: {current_version}")
print(f" 필요: {required_version}")
# 프로세스가 사용 중일 수 있는 패키지는 경고만 출력
if package_name in SKIP_IF_IN_USE:
print(f"[경고] {package_name}는 현재 프로세스에서 사용 중일 수 있어 교체를 건너뜁니다.")
print("[팁] 해결 방법:")
print(" 1. 현재 Python 프로세스를 완전히 종료")
print(" 2. 관리자 권한으로 새 명령 프롬프트 실행")
print(f" 3. pip install {package_name}=={required_version} --force-reinstall")
print(" 4. 빌드 다시 실행")
continue
# 기존 버전 제거 후 재설치
if uninstall_package(package_name):
if install_package(package_name, required_version):
needs_restart = True
else:
print(f"[경고] {package_name} 재설치 실패 - 빌드 계속 진행")
# return False # 실패해도 빌드는 계속 진행
else:
print(f"[경고] {package_name} 제거 실패 - 빌드 계속 진행")
# return False # 실패해도 빌드는 계속 진행
else:
print(f"[성공] {package_name} {current_version} (호환됨)")
if needs_restart:
print("\n[새로고침] 패키지 변경으로 인해 Python 재시작이 권장됩니다.")
print(" cx_Freeze 빌드를 다시 실행해주세요.")
print("\n[완료] 모든 의존성 체크 완료!")
return True
def get_package_info():
"""현재 설치된 패키지 정보를 출력합니다."""
print("\n[정보] 현재 패키지 정보:")
print("-" * 40)
for package_name, required_version in REQUIRED_VERSIONS.items():
current_version = check_package_version(package_name)
status = "[성공]" if current_version and is_version_compatible(current_version, required_version) else "[실패]"
print(f"{status} {package_name:15} {current_version or 'Not installed':15} (요구: {required_version})")
def main():
"""메인 함수"""
print("[도구] DirectML 기반 ONNX Runtime 호환성 체커")
print("=" * 60)
try:
# 1. 현재 상태 출력
get_package_info()
# 2. 의존성 체크 및 수정
success = check_and_fix_dependencies()
if success:
print("\n[성공] 의존성 체크 완료 - 빌드 진행 가능")
# 3. 최종 상태 출력
get_package_info()
return 0
else:
print("\n[실패] 의존성 체크 실패 - 빌드 중단")
return 1
except KeyboardInterrupt:
print("\n[경고] 사용자에 의해 중단됨")
return 1
except Exception as e:
print(f"\n[오류] 예상치 못한 오류: {e}")
import traceback
traceback.print_exc()
return 1
if __name__ == "__main__":
exit_code = main()
sys.exit(exit_code)