我最近一直在尝试做类似的事情,但我发现这些答案不足以满足我的用例(需要检测项目根目录的分布式库)。主要是我一直在与不同的环境和平台作斗争,但仍然没有找到完全通用的东西。
项目本地代码
我已经看到了这个示例,并在一些地方(例如Django等)使用了该示例。
import os
print(os.path.dirname(os.path.abspath(__file__)))
如此简单,仅当代码片段所在的文件实际上是项目的一部分时才起作用。我们不检索项目目录,而是片段的目录
同样,从应用程序的入口点之外调用时,sys.modules方法会崩溃,特别是我观察到子线程无法在不与' main '模块相关的情况下确定该方法。我已将导入明确地放在一个函数中,以演示从子线程进行的导入,将其移至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。threading.main_thread()但是,在Python3.4 +中,线程库已经使用了,每个人都没有使用3.4+,因此我们在所有线程中进行搜索,以查找除ID外的主线程。如果主线程已经退出,则不会在中列出threading.enumerate()。RuntimeError()在这种情况下,我们提出一个,直到找到更好的解决方案。
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]最后,inspect.getouterframes()需要处理Windows和Linux实现之间的差异。使用清理后的文件名,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存在?