# ModuleNotFoundError
PyCharm같은 통합개발환경(IDE)에서 편하게 개발할때는 이슈가 없었는데, VS code로 docker에 띄울 배치 스크립트를 개발하던 중 문제가 생겼다. 가상환경(Virtual Environment)을 구성하여 활성화시키고, 실행시켰는데 모듈을 찾을 수 없다는 것이다.
python .\my_game\batch1.py
Traceback (most recent call last):
File "C:\Users\ASUS\PycharmProjects\RankingServer\batch\my_game\batch1.py", line 6, in <module>
from libs.task.TaskManager import TaskManager
ModuleNotFoundError: No module named 'libs.task'
프로젝트 구조는 다음과 같았다. 상위 폴더의 모듈을 어떻게 import할까...검색도 해봤지만 그건 어리석은 방법인것 같고PyCharm에서는되고 그냥 venv 활성화 시켜서 돌리려는데 안되는거니 자세히 찾아보았다.
batch/
├── libs/
│ └── tasks/
│ └── TaskManager.py
└── my_game/
└── batch1.py
# 원인은 PYTHONPATH
Python에서 모듈을 찾을때 PYTHONPATH를 기준으로 탐색하는데, PyCharm에서는 Configurations에 자세히보면 현재 프로젝트 루트를 PYTHONPATH에 자동으로 더해주고 있다. 그래서 libs/tasks를 찾을때 아무런 문제가 없었던 것이다.
# PYTHONPATH란 무엇일까?
PYTHONPATH는 환경변수다. Python 개발을 하면서 pip를 통해서 패키지를 다운받기도하지만 여러 모듈을 직접 만들기도한다. 하지만 직접 Installable하게 배포하지는 않고 프로젝트에 PythonPackage로 만들어서 추가하기만하는데, 이때 PYTHONPATH를 기준으로 모듈들을 찾을 수 있게 된다. python3 공식 문서의 표현은 모듈 파일의 기본 검색 경로를 늘린다(Augment the default search path for module files.)라고한다.
실행 환경에서 등록되어있는 PYTHONPATH를 출력하려면 sys.path를 이용하면된다.
import sys
print(sys.path)
위 사진처럼 venv 환경안에서 sys.path를 조회해보면 제일 앞에 RankingServer\batch라고 나오는데, 이건 최종적으로 추가 된 것이고 원래는 이게 존재하지 않아 libs/tasks를 찾을 수 없었다.
# PYTHONPATH를 추가해보자
■ 임시 해결책
StackOverFlow를 찾아보면 단기적인 해결책들이 눈에 보인다. 다음처럼 소스코드 가장 위에 sys.path.append로 PYTHONPATH를 추가시켜주는 방법이다. 하지만 추천하지 않는다. 환경변수 추가 한번만 해주면 될 걸 이런식으로 땜빵하려고하면 코드가 얼마나 더러워질까!
sys.path.append(os.path.join(os.getcwd(), ".."))
■ 진짜 해결책 : venv activate시에 환경변수 등록
docker 같은 독립적인 환경에서는 python을 오로지 그 프로젝트에만 쓸 수 있다. 그렇다면 컨테이너의 환경 변수에 PYTHONPATH를 등록해도 문제없다. 하지만 여러 python 프로젝트들을 개발하는 내 컴퓨터에...PYTHONPATH를 개발하는 프로젝트마다 추가할 수는 없을 것이다.
그래서 virtual environment를 사용하는 것이고, 그 속에서 PYTHONPATH를 추가해주면 깔끔해진다.
venv/
└── Scripts/
├── activate
├── activate.bat
├── Activate.ps1
└── ...
Windows 컴퓨터를 기준으로 venv 폴더안을 보면 activate 관련 파일들이 저렇게 있는데... activate.bat을 손봐주면 된다.
[activate.bat]
...
if defined _OLD_VIRTUAL_PATH set PATH=%_OLD_VIRTUAL_PATH%
if not defined _OLD_VIRTUAL_PATH set _OLD_VIRTUAL_PATH=%PATH%
set PATH=%VIRTUAL_ENV%\Scripts;%PATH%
# 이 라인에 내 프로젝트의 root path를 등록
set PYTHONPATH=C:\Users\ASUS\PycharmProjects\RankingServer\batch
:END
if defined _OLD_CODEPAGE (
"%SystemRoot%\System32\chcp.com" %_OLD_CODEPAGE% > nul
set _OLD_CODEPAGE=
)
...
중간에 환경변수를 설정하는 부분을 참고하면된다. Powershell을 사용하시는 분들이라면 이렇게 해도 적용이 안될거다. (바로 내가 그 Powershell 쓰는 사람이었기때문에...30분 더 헤맸다) 추가로 Powershell 사용자는 Activate.ps1 파일도 수정해주어야한다.
[Activate.ps1]
...
Write-Verbose "Prompt = '$Prompt'"
Write-Verbose "VenvDir='$VenvDir'"
# Deactivate any currently active virtual environment, but leave the
# deactivate function in place.
deactivate -nondestructive
$env:PYTHONPATH = "C:\Users\ASUS\PycharmProjects\RankingServer\batch"
# Now set the environment variable VIRTUAL_ENV, used by many tools to determine
# that there is an activated venv.
$env:VIRTUAL_ENV = $VenvDir
...
중간에 $env:PYTHONPATH 부분을 추가해주면된다. 그러고 venv 활성화 시켜주면 아마 될거다.
# Docker는요?
WORKDIR /batch
RUN pip3 install -r requirements.txt
COPY . .
ENV PYTHONPATH=/batch
WORKDIR로 설정한 부분을 ENV에 추가만 해주면된다.
참고자료
https://docs.python.org/3/using/cmdline.html#envvar-PYTHONPATH
https://bic-berkeley.github.io/psych-214-fall-2016/using_pythonpath.html
'프로그래밍 > Python' 카테고리의 다른 글
[Python] SlackBot 쉽게 만들기 (0) | 2023.12.10 |
---|---|
[Python] zstandard로 dictionary list 압축하기 (0) | 2022.06.26 |
[Windows] mysqlclient 설치하기 (pip install mysqlclient) (0) | 2022.05.28 |
[Windows] Python virtual envorinment 활성화 하기 (0) | 2022.05.28 |
Python 특정 키 값을 기준으로 Dictionary List에서 더하기 (0) | 2022.05.07 |