Son zamanlarda benzer bir şey yapmaya çalışıyorum ve bu cevapları kullanım durumlarım için yetersiz buldum (proje kökünü tespit etmesi gereken dağıtılmış bir kitaplık). Temelde farklı ortamlar ve platformlarla savaşıyorum ve hala mükemmel evrensel bir şey bulamadım.
Projeye yerel kodlama
Bu örnekten Django vb. Yerlerde bahsedildiğini ve kullanıldığını gördüm.
import os
print(os.path.dirname(os.path.abspath(__file__)))
Bu kadar basit, yalnızca parçacığın içinde bulunduğu dosya aslında projenin bir parçası olduğunda çalışır. Proje dizinini değil, kod parçacığının dizinini alıyoruz
Benzer şekilde, sys.modules yaklaşımı , uygulamanın giriş noktasının dışından çağrıldığında bozuluyor , özellikle bir çocuk iş parçacığının bunu ' ana ' modülle ilişkisi olmadan belirleyemediğini gözlemledim . Bir alt iş parçacığından içe aktarmayı göstermek için içe aktarmayı açıkça bir işlevin içine koydum, onu app.py'nin en üst düzeyine taşımak sorunu düzeltecektir.
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
Bu programı çalıştırmak bir öznitelik hatası oluşturur:
>>> 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__'
... dolayısıyla iş parçacığı tabanlı bir çözüm
Yerden bağımsız
Öncekiyle aynı uygulama yapısını kullanıp ayarları değiştirerek.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))
Bunu parçalamak: Önce ana iş parçacığının iş parçacığı kimliğini doğru bir şekilde bulmak istiyoruz. Python3.4 + 'da iş parçacığı kitaplığı vardır, threading.main_thread()ancak herkes 3.4+ kullanmaz, bu nedenle kimliğini kaydetmeden ana iş parçacığını arayan tüm iş parçacıklarını ararız. Ana ileti dizisinden zaten çıkılmışsa, içinde listelenmeyecektir threading.enumerate(). RuntimeError()Ben daha iyi bir çözüm bulana kadar bu durumda a yükseltiyoruz .
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")
Daha sonra ana iş parçacığının ilk yığın çerçevesini buluyoruz. CPython'a özgü işlevi kullanarak, sys._current_frames() her iş parçacığının mevcut yığın çerçevesinin bir sözlüğünü elde ederiz. Daha sonra kullanarak inspect.getouterframes()ana iş parçacığı ve ilk çerçeve için tüm yığını alabiliriz. current_main_frame = sys._current_frames () [main_id] base_frame = inspect.getouterframes (current_main_frame) [- 1] Son olarak, Windows ve Linux uygulamaları arasındaki farkların inspect.getouterframes()ele alınması gerekir. Temizlenmiş dosya adını kullanma os.path.abspath()veos.path.dirname() her şeyi temizleyin.
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))
Şimdiye kadar bunu Windows'ta Python2.7 ve 3.6'da ve WSL'de Python3.4'te test ettim
<ROOT>/__init__.pyvar?