61 lines
2.4 KiB
Python
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("\\", "/")
|