Saya baru-baru ini mencoba melakukan sesuatu yang serupa dan saya menemukan jawaban ini tidak memadai untuk kasus penggunaan saya (perpustakaan terdistribusi yang perlu mendeteksi root proyek). Terutama saya telah berjuang melawan lingkungan dan platform yang berbeda, dan masih belum menemukan sesuatu yang sangat universal.
Kode lokal untuk proyek
Saya telah melihat contoh ini disebutkan dan digunakan di beberapa tempat, Django, dll.
import os
print(os.path.dirname(os.path.abspath(__file__)))
Sesederhana ini, ini hanya berfungsi jika file tempat cuplikan sebenarnya adalah bagian dari proyek. Kami tidak mengambil direktori proyek, melainkan direktori cuplikan
Demikian pula, pendekatan sys.modules rusak ketika dipanggil dari luar titik masuk aplikasi, khususnya saya telah mengamati utas anak tidak dapat menentukan ini tanpa hubungannya kembali ke modul ' utama '. Saya secara eksplisit meletakkan impor di dalam fungsi untuk mendemonstrasikan impor dari utas anak, memindahkannya ke tingkat atas app.py akan memperbaikinya.
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
Menjalankan program ini menghasilkan kesalahan atribut:
>>> 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__'
... maka solusi berbasis threading
Lokasi independen
Menggunakan struktur aplikasi yang sama seperti sebelumnya tetapi memodifikasi 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))
Menguraikannya: Pertama, kami ingin menemukan ID utas utas utama secara akurat. Dalam Python3.4 + perpustakaan threading threading.main_thread()bagaimanapun, semua orang tidak menggunakan 3.4+ jadi kami mencari melalui semua utas mencari utas utama simpan ID-nya. Jika utas utama sudah keluar, utas tidak akan terdaftar di threading.enumerate(). Kami meningkatkan a RuntimeError()dalam kasus ini sampai saya menemukan solusi yang lebih baik.
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")
Selanjutnya kita menemukan bingkai tumpukan pertama dari utas utama. Dengan menggunakan fungsi spesifik cPython, sys._current_frames() kami mendapatkan kamus bingkai tumpukan setiap utas saat ini. Kemudian dengan memanfaatkannya, inspect.getouterframes()kita dapat mengambil seluruh tumpukan untuk utas utama dan bingkai pertama. current_main_frame = sys._current_frames () [main_id] base_frame = inspect.getouterframes (current_main_frame) [- 1] Akhirnya, perbedaan antara implementasi Windows dan Linux inspect.getouterframes()perlu ditangani. Menggunakan nama file yang dibersihkan, os.path.abspath()dan os.path.dirname()membersihkan semuanya.
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))
Sejauh ini saya telah menguji ini di Python2.7 dan 3.6 di Windows serta Python3.4 di WSL
<ROOT>/__init__.pyada?