Files
ant-vision-inspector/paths.py
2026-06-10 16:18:41 +09:00

61 lines
2.4 KiB
Python

# 프로젝트 경로 유틸리티 — 개발/EXE 환경 모두에서 상대↔절대 경로 일관 처리
#
# 두 종류의 루트가 있음:
# PROJECT_ROOT : 사용자가 보는 실제 폴더. 로그/설정/모델 등 쓰기 가능한 위치.
# - dev 모드: 이 파일이 있는 폴더 (E:\ANT)
# - EXE(--onefile) 모드: EXE가 놓인 폴더 (예: E:\ANT\dist)
# BUNDLE_ROOT : 번들에 포함된 읽기 전용 자원 위치.
# - dev 모드: PROJECT_ROOT 와 동일
# - EXE 모드: PyInstaller 임시 추출 폴더 (sys._MEIPASS)
import os
import sys
_FROZEN = getattr(sys, "frozen", False)
# 번들된 자원(읽기 전용): config.json, ai/models, assets 등
BUNDLE_ROOT = getattr(sys, "_MEIPASS", os.path.dirname(os.path.abspath(__file__)))
# 사용자 데이터(읽기/쓰기): logs, 변경된 config, 새로 저장된 모델 등
if _FROZEN:
PROJECT_ROOT = os.path.dirname(os.path.abspath(sys.executable))
else:
PROJECT_ROOT = os.path.dirname(os.path.abspath(__file__))
def resolve_path(path: str) -> str:
"""상대경로 → 절대경로 변환.
1) 절대경로/빈 값은 그대로 반환
2) PROJECT_ROOT 기준 경로가 존재하면 사용 (사용자가 변경한 사본 우선)
3) BUNDLE_ROOT 기준 경로가 존재하면 폴백 (번들 기본 사본)
4) 둘 다 없으면 PROJECT_ROOT 기준 경로 반환 (신규 생성 시 사용자 영역에 만들도록)
"""
if not path:
return path
if os.path.isabs(path):
return os.path.normpath(path)
primary = os.path.normpath(os.path.join(PROJECT_ROOT, path))
if os.path.exists(primary):
return primary
if BUNDLE_ROOT and BUNDLE_ROOT != PROJECT_ROOT:
fallback = os.path.normpath(os.path.join(BUNDLE_ROOT, path))
if os.path.exists(fallback):
return fallback
return primary
def to_project_relative(path: str) -> str:
"""PROJECT_ROOT 또는 BUNDLE_ROOT 하위면 슬래시 구분 상대경로로, 외부면 슬래시 구분 절대경로로."""
if not path:
return path
abs_path = os.path.abspath(path)
for root in (PROJECT_ROOT, BUNDLE_ROOT):
if not root:
continue
try:
rel = os.path.relpath(abs_path, root)
except ValueError:
continue
if not rel.startswith(".."):
return rel.replace("\\", "/")
return abs_path.replace("\\", "/")