최근에 비슷한 작업을 시도해 왔으며 이러한 답변이 내 사용 사례 (프로젝트 루트를 감지해야하는 분산 라이브러리)에 적합하지 않다는 것을 발견했습니다. 주로 저는 다양한 환경과 플랫폼과 싸우고 있지만 여전히 완벽하게 보편적 인 것을 찾지 못했습니다.
프로젝트에 로컬 코드
이 예제가 언급되고 Django 등 몇 군데에서 사용되는 것을 보았습니다.
import os
print(os.path.dirname(os.path.abspath(__file__)))
간단하게, 스 니펫이있는 파일이 실제로 프로젝트의 일부인 경우에만 작동합니다. 우리는 프로젝트 디렉토리를 검색하지 않고 대신 스 니펫의 디렉토리를 검색합니다.
마찬가지로, sys.modules에이 때 고장 접근 이라고 특별히 나는 '관련 뒤로없이이를 확인할 수없는 자식 스레드 관찰 한 응용 프로그램의 엔트리 포인트 밖에서을 주요 '모듈. 자식 스레드에서 가져 오기를 보여주기 위해 함수 내부에 가져 오기를 명시 적으로 넣었습니다. app.py의 최상위 수준으로 이동하면 문제가 해결됩니다.
app/
|-- config
| `-- __init__.py
| `-- settings.py
`-- app.py
app.py
#!/usr/bin/env python
import threading
def background_setup():
# Explicitly importing this from the context of the child thread
from config import settings
print(settings.ROOT_DIR)
# Spawn a thread to background preparation tasks
t = threading.Thread(target=background_setup)
t.start()
# Do other things during initialization
t.join()
# Ready to take traffic
settings.py
import os
import sys
ROOT_DIR = None
def setup():
global ROOT_DIR
ROOT_DIR = os.path.dirname(sys.modules['__main__'].__file__)
# Do something slow
이 프로그램을 실행하면 속성 오류가 발생합니다.
>>> import main
>>> Exception in thread Thread-1:
Traceback (most recent call last):
File "C:\Python2714\lib\threading.py", line 801, in __bootstrap_inner
self.run()
File "C:\Python2714\lib\threading.py", line 754, in run
self.__target(*self.__args, **self.__kwargs)
File "main.py", line 6, in background_setup
from config import settings
File "config\settings.py", line 34, in <module>
ROOT_DIR = get_root()
File "config\settings.py", line 31, in get_root
return os.path.dirname(sys.modules['__main__'].__file__)
AttributeError: 'module' object has no attribute '__file__'
... 따라서 스레딩 기반 솔루션
위치 독립적
이전과 동일한 애플리케이션 구조를 사용하지만 settings.py 수정
import os
import sys
import inspect
import platform
import threading
ROOT_DIR = None
def setup():
main_id = None
for t in threading.enumerate():
if t.name == 'MainThread':
main_id = t.ident
break
if not main_id:
raise RuntimeError("Main thread exited before execution")
current_main_frame = sys._current_frames()[main_id]
base_frame = inspect.getouterframes(current_main_frame)[-1]
if platform.system() == 'Windows':
filename = base_frame.filename
else:
filename = base_frame[0].f_code.co_filename
global ROOT_DIR
ROOT_DIR = os.path.dirname(os.path.abspath(filename))
이를 분석 : 먼저 메인 스레드의 스레드 ID를 정확하게 찾고 싶습니다. Python3.4 +에서는 스레딩 라이브러리가 threading.main_thread()있지만 모두가 3.4+를 사용하지 않으므로 모든 스레드를 검색하여 메인 스레드를 검색하여 ID를 저장합니다. 메인 스레드가 이미 종료 된 경우 threading.enumerate(). RuntimeError()이 경우 더 나은 해결책을 찾을 때까지 a 를 올립니다 .
main_id = None
for t in threading.enumerate():
if t.name == 'MainThread':
main_id = t.ident
break
if not main_id:
raise RuntimeError("Main thread exited before execution")
다음으로 메인 스레드의 첫 번째 스택 프레임을 찾습니다. cPython 특정 함수를 사용하여 sys._current_frames() 모든 스레드의 현재 스택 프레임에 대한 사전을 얻습니다. 그런 다음 사용 inspect.getouterframes()하여 주 스레드와 첫 번째 프레임에 대한 전체 스택을 검색 할 수 있습니다. current_main_frame = sys._current_frames () [main_id] base_frame = inspect.getouterframes (current_main_frame) [-1] 마지막으로 Windows와 Linux 구현 간의 차이점을 inspect.getouterframes()처리해야합니다. 위 세척 파일 이름을 사용 os.path.abspath()하고 os.path.dirname()청소 일까지.
if platform.system() == 'Windows':
filename = base_frame.filename
else:
filename = base_frame[0].f_code.co_filename
global ROOT_DIR
ROOT_DIR = os.path.dirname(os.path.abspath(filename))
지금까지 Windows의 Python2.7 및 3.6과 WSL의 Python3.4에서 이것을 테스트했습니다.
<ROOT>/__init__.py존재 하는가?