본문 바로가기
Software/Python

Pyinstaller로 변환한 exe 파일의 실행 경로 찾기

by lovey25 2021. 7. 8.
반응형

Pyinstaller로 파이선 스크립트를 실행파일로 변환했을 때 겪을 수 있는 경로 문제에 대한 이야기입니다. 프로그램 동작중에 데이터 파일을 가져오거나 저장해야 할 일이 있을 때 파이썬 스크립트 상태에서 실행할 때는 아무런 문제가 없었는데 Pyinstaller로 패키징을 한 후에 실행을 하면 똑같은 위치에 멀쩡히 잘 있는 데이터 파일을 찾지 못한다고 에러가 뜨거나 파일을 저장했다는데 엉뚱한 곳에 저장을 해서 난감한 경험을 격으셨다면 끝까지 읽어보시고 해결해보세요.

본 포스팅은 프로그램의 runtime과 관련된 내용으로 pyinstaller의 기술문서인 아래 링크를 참고하였습니다. 자세히 공부를 하고 싶다 하시는 분은 링크의 문서를 다 읽어보시면 좋을 것 같고요. 난 결론만 있으면 돼 하시면 아래 글을 읽어주세요.

 

Run-time Information — PyInstaller 4.3 documentation

When your program is not bundled, the Python variable __file__ refers to the current path of the module it is contained in. When importing a module from a bundled script, the PyInstaller bootloader will set the module’s __file__ attribute to the correct

pyinstaller.readthedocs.io

앞에서 언급한 문제가 발생하는 원인은 스크립트 상태일 때와 ".exe" 파일로 패키징이 된 상태일 때 runtime에 차이가 있기 때문입니다.

예를 들면, 파이썬 코드의 현재 위치를 나타내는 예약어 "__file__"를 살펴보면 파이썬 코드를 스크립트 상태에서 실행을 하게 되면 __file__에는 현재 경로를 잘 나타냅니다. 그러나 스크립트를 pyinstaller로 exe파일 1개로 패키징을 하게 되면 상황이 달라집니다. 1개 파일로 패키징 된 프로그램을 실행시켜서 __file__의 경로를 확인해 보면 완전히 엉뚱한 임시 폴더를 가리킨다는 걸 알 수 있습니다.

아래 코드는 프로그램 번들의 경로를 나타내는 여러 가지 방법이 상황에 따라 어떤 결과가 나오는지 확인할 수 있게 해주는 예제 코드입니다.

#!/usr/bin/python3
import sys, os
frozen = 'not'
if getattr(sys, 'frozen', False):
        # we are running in a bundle
        frozen = 'ever so'
        bundle_dir = sys._MEIPASS
else:
        # we are running in a normal Python environment
        bundle_dir = os.path.dirname(os.path.abspath(__file__))
print( 'we are',frozen,'frozen')
print( 'bundle dir is', bundle_dir )
print( 'sys.argv[0] is', sys.argv[0] )
print( 'sys.executable is', sys.executable )
print( 'os.getcwd is', os.getcwd() )

4행의 getattr() 함수는 pyinstaller로 패키징이 된 상태인지 아닌지를 판별할 수 있는 함수인데 알아두면 나중에 유용하게 쓸 수 있을 것 같네요.

이 코드를 스크립트 상태로 실행했을 때와 하나의 폴더로 패키징 해서 실행했을 때 마지막으로 하나의 파일로 패키징 해서 실행했을 때 결과를 비교해보면 다음과 같습니다.

test.py 스크립트 상태로 실행했을 때 결과

스크립트 상태일 때는 "sys.argv[0]"에 현재 스크립트의 상대 경로가 들어가 있네요. 그리고 "sys.executable"은 인터프리터인 python.exe를 가리키고 있습니다.

하나의 폴더로 패키징 하여 실행한 결과

폴더로 패키징을 한 결과에서는 "sys.argv[0]"이 실행 프로그램의 절대 경로를 나타냅니다. 그리고 모든 경로는 실행파일인 "test.exe"의 경로로 모여있습니다.

".exe" 파일 1개로 패키징하여 실행한 결과

마지막으로 exe파일 1개로 패키징(-F or --onefile 옵션 사용)을 한 결과입니다. 여기서는 특이한 점이 "__file__"의 경로가 생뚱맞은 임시 폴더입니다. 파일 하나로 패키징 되어 있으니까 압축 파일을 임시 폴더에 잠시 풀고 실행하는 그런 개념처럼 생각하면 될까요? 암튼 이것도 다릅니다.

이렇게 결과를 보니 상황에 따라서 리소스 혹은 데이터 파일 경로를 지정할 때 어떤 옵션을 사용하면 될지 감이 오는 것 같습니다.

 

끝!

반응형

댓글