pyinstaller 사용시 리소스 폴더 경로얻기 주의사항
파일 경로 얻기
최근 Python으로 작성된 코드 중, 파일 경로를 얻는 코드부분에 문제가 있었습니다.
저는 평소 경로를 얻을때 코드를 아래와 같이 작성합니다.
import os curr_path = os.path.abspath(__file__) curr_dir = os.path.dirname(curr_path) file_path = os.path.join(curr_dir, 'Salary_dataset.csv')
현재 경로가 C:\ML 이라면, 출력은 아래와 같습니다.
c:\ML\Salary_dataset.csv
os모듈의 getcwd() 함수를 떠올렸다면 좋은 생각은 아닙니다.
만약 터미널에서 아래 경로의 파일을 실행하면,
cd C:\home python home\user\test.py
아래와 같이,
getcwd() 는 현재 터미널의 경로를,
__file__ 은 스크립트(*.py) 의 경로를 출력합니다.
C:\home (getcwd, 현재 작업경로) C:\home\user (__file__ *.py 파일이 있는 경로)
즉, getcwd() 는 현재 스크립트의 경로를 제대로 가져오지 못하는 경우가 있습니다.
따라서 __file__을 별 문제없이 사용해 왔는데 최근 Pyinstaller에서 문제가 발생하였습니다.
Pyinstaller에서 __file__
위 코드로 경로를 얻어와 이미지를 출력하는데 개발시에는 문제가 없다가 배포시에 문제가 발생하였습니다.
Pyinstaller로 만든 배포파일(*.exe)에서 이미지 경로를 얻어오지 못하네요.
__file__ 관련 문제원인은 아래와 같습니다.
- Python 스크립트를 실행할 때는 __file__ 이 "현재 .py 파일이 있는 경로"
- PyInstaller 로 실행파일(.exe) 을 만들면, .py 파일들이 전부 압축(*.exe 내부)
따라서 실행 시점에는 더 이상 원래 .py 파일이 물리적(*.exe로 압축)으로
존재하지 않으므로 __file__ 이 잘못된 경로나 아예 동작 안하는
현상입니다.
대처방법
원인은 찾았지만 개발시 *.py 스크립트 실행(컴파일타임)과 Pyinstaller 실행(런타임)을 따로 처리해야하는 과정이 남았습니다.
왜냐하면 __file__ 사용시 *.py파일이 배포판(*.exe 등) 에서는 압축되어 존재하지 않고, 평소 개발시에는 존재하기 때문입니다. (컴파일 타임 구분 불가)
위 문제는 경로찾기를 아래와 같이 구분해 처리합니다.
import os
import sys
if getattr(sys, 'frozen', False): # exe 실행 중
base_path = os.path.dirname(sys.executable) # 실행파일(.exe)이 있는 폴더
else: # 개발 환경 (py로 실행)
base_path = os.path.dirname(os.path.abspath(__file__))
# images 경로
img_path = os.path.join(base_path, "images", "my_image.png")
# CSV 경로
csv_path = os.path.join(base_path, "Salary_dataset.csv")
print(img_path)
print(csv_path)
getattr(sys, 'frozen', False) 함수는 sys 객체에 frozen 속성이 있으면 그 값을 반환하고, 없으면 False를 반환합니다.
여기서 'frozen' 은 PyInstaller 같은 번들러로 만든 ‘frozen’(묶여 실행되는) 앱을 감지하는 역할이므로 런타임에 실행파일 여부를 감지합니다.
실행파일인 경우 sys.executable 로 경로를 얻어처리하고 아니면 기존과 같이(__file__) 처리합니다.
이렇게 작성하니 개발, 배포과정의 경로 문제가 해결되었습니다. 😀
이상으로 설명을 마칩니다.
감사합니다.
댓글
댓글 쓰기