Gần đây tôi đã cố gắng làm một cái gì đó tương tự và tôi đã tìm thấy những câu trả lời này không thỏa đáng cho các trường hợp sử dụng của tôi (một thư viện phân tán cần phát hiện root dự án). Chủ yếu là tôi đã chiến đấu với các môi trường và nền tảng khác nhau, và vẫn chưa tìm thấy thứ gì đó hoàn toàn phổ biến.
Mã địa phương để dự án
Tôi đã thấy ví dụ này được đề cập và sử dụng ở một vài nơi, Django, v.v.
import os
print(os.path.dirname(os.path.abspath(__file__)))
Đơn giản như vậy, nó chỉ hoạt động khi tệp mà đoạn mã nằm trong thực sự là một phần của dự án. Chúng tôi không truy xuất thư mục dự án, mà thay vào đó là thư mục của đoạn trích
Tương tự, cách tiếp cận sys.modules bị hỏng khi được gọi từ bên ngoài điểm vào của ứng dụng, cụ thể tôi đã quan sát thấy một luồng con có thể xác định điều này mà không liên quan đến mô đun ' chính '. Tôi đã đặt rõ ràng việc nhập vào bên trong một hàm để thể hiện việc nhập từ một luồng con, chuyển nó sang cấp cao nhất của app.py sẽ khắc phục nó.
app/
|-- config
| `-- __init__.py
| `-- settings.py
`-- app.py
ứng dụng
#!/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
cài đặt
import os
import sys
ROOT_DIR = None
def setup():
global ROOT_DIR
ROOT_DIR = os.path.dirname(sys.modules['__main__'].__file__)
# Do something slow
Chạy chương trình này tạo ra một lỗi thuộc tính:
>>> 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__'
... do đó, một giải pháp dựa trên luồng
Vị trí độc lập
Sử dụng cấu trúc ứng dụng giống như trước đây nhưng sửa đổi cài đặt
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))
Phá vỡ điều này: Đầu tiên chúng tôi muốn tìm chính xác ID luồng của luồng chính. threading.main_thread()Tuy nhiên, trong Python3.4 + thư viện phân luồng có , mọi người không sử dụng 3,4+ nên chúng tôi tìm kiếm trong tất cả các luồng tìm kiếm luồng chính lưu ID của nó. Nếu luồng chính đã thoát, nó sẽ không được liệt kê trong threading.enumerate(). Chúng tôi nêu ra một RuntimeError()trường hợp này cho đến khi tôi tìm thấy một giải pháp tốt hơn.
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")
Tiếp theo chúng ta tìm khung stack đầu tiên của luồng chính. Sử dụng chức năng cụ thể của cPython, sys._current_frames() chúng tôi có được một từ điển của khung ngăn xếp hiện tại của mỗi luồng. Sau đó, sử dụng inspect.getouterframes()chúng ta có thể lấy toàn bộ ngăn xếp cho luồng chính và khung đầu tiên. current_main_frame = sys._civerse_frames () [main_id] base_frame = notify.getouterframes (current_main_frame) [- 1] Cuối cùng, inspect.getouterframes()cần phải xử lý các khác biệt giữa việc triển khai Windows và Linux . Sử dụng tên tệp đã được dọn sạch os.path.abspath()và os.path.dirname()dọn dẹp mọi thứ.
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))
Cho đến nay tôi đã thử nghiệm điều này trên Python2.7 và 3.6 trên Windows cũng như Python3.4 trên WSL
<ROOT>/__init__.pytồn tại không?