Python - 获取根项目结构的路径
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/25389095/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Python - Get path of root project structure
提问by Shookie
I've got a python project with a configuration file in the project root. The configuration file needs to be accessed in a few different files throughout the project.
我有一个 python 项目,在项目根目录中有一个配置文件。需要在整个项目的几个不同文件中访问配置文件。
So it looks something like: <ROOT>/configuration.conf<ROOT>/A/a.py, <ROOT>/A/B/b.py(when b,a.py access the configuration file).
所以它看起来像:<ROOT>/configuration.conf<ROOT>/A/a.py, <ROOT>/A/B/b.py(当 b,a.py 访问配置文件时)。
What's the best / easiest way to get the path to the project root and the configuration file without depending on which file inside the project I'm in? i.e without using ../../? It's okay to assume that we know the project root's name.
在不依赖于我所在项目中的哪个文件的情况下,获取项目根目录和配置文件的路径的最佳/最简单方法是什么?即不使用../../?假设我们知道项目根的名称是可以的。
回答by jrd1
You can do this how Django does it: define a variable to the Project Root from a file that is in the top-level of the project.For example, if this is what your project structure looks like:
您可以按照 Django 的方式执行此操作:从位于项目顶层的文件中为项目根目录定义一个变量。例如,如果这是您的项目结构:
project/
configuration.conf
definitions.py
main.py
utils.py
In definitions.pyyou can define (this requires import os):
在definitions.py你可以定义(这需要import os):
ROOT_DIR = os.path.dirname(os.path.abspath(__file__)) # This is your Project Root
Thus, with the Project Rootknown, you can create a variable that points to the location of the configuration(this can be defined anywhere, but a logical place would be to put it in a location where constants are defined - e.g. definitions.py):
因此,在项目根已知的情况下,您可以创建一个指向配置位置的变量(这可以在任何地方定义,但合乎逻辑的位置是将它放在定义常量的位置 - 例如definitions.py):
CONFIG_PATH = os.path.join(ROOT_DIR, 'configuration.conf') # requires `import os`
Then, you can easily access the constant (in any of the other files) with the import statement (e.g. in utils.py): from definitions import CONFIG_PATH.
然后,您可以使用 import 语句(例如 in utils.py)轻松访问常量(在任何其他文件中):from definitions import CONFIG_PATH。
回答by DevPlayer
To get the path of the "root" module, you can use:
要获取“root”模块的路径,您可以使用:
import os
import sys
os.path.dirname(sys.modules['__main__'].__file__)
But more interestingly if you have an config "object" in your top-most module you could -read- from it like so:
但更有趣的是,如果您的最顶层模块中有一个配置“对象”,您可以像这样读取它:
app = sys.modules['__main__']
stuff = app.config.somefunc()
回答by shrewmouse
A standard way to achieve this would be to use the pkg_resourcesmodule which is part of the setuptoolspackage. setuptoolsis used to create an install-able python package.
实现此目的的标准方法是使用pkg_resources作为setuptools包一部分的模块。 setuptools用于创建可安装的 python 包。
You can use pkg_resourcesto return the contents of your desired file as a string and you can use pkg_resourcesto get the actual path of the desired file on your system.
您可以使用pkg_resources将所需文件的内容作为字符串返回,并且可以使用pkg_resources来获取系统上所需文件的实际路径。
Let's say that you have a package called stackoverflow.
假设您有一个名为stackoverflow.
stackoverflow/
|-- app
| `-- __init__.py
`-- resources
|-- bands
| |-- Dream\ Theater
| |-- __init__.py
| |-- King's\ X
| |-- Megadeth
| `-- Rush
`-- __init__.py
3 directories, 7 files
Now let's say that you want to access the file Rush from a module app.run. Use pkg_resources.resouces_filenameto get the path to Rush and pkg_resources.resource_stringto get the contents of Rush; thusly:
现在假设您要从模块访问文件 Rush app.run。使用pkg_resources.resouces_filename得到的路径,拉什和pkg_resources.resource_string得到拉什的内容; 因此:
import pkg_resources
if __name__ == "__main__":
print pkg_resources.resource_filename('resources.bands', 'Rush')
print pkg_resources.resource_string('resources.bands', 'Rush')
The output:
输出:
/home/sri/workspace/stackoverflow/resources/bands/Rush
Base: Geddy Lee
Vocals: Geddy Lee
Guitar: Alex Lifeson
Drums: Neil Peart
This works for all packages in your python path. So if you want to know where lxml.etreeexists on your system:
这适用于 python 路径中的所有包。因此,如果您想知道lxml.etree系统上的位置:
import pkg_resources
if __name__ == "__main__":
print pkg_resources.resource_filename('lxml', 'etree')
output:
输出:
/usr/lib64/python2.7/site-packages/lxml/etree
The point is that you can use this standard method to access files that are installed on your system (e.g pip install xxx or yum -y install python-xxx) and files that are within the module that you're currently working on.
关键是您可以使用此标准方法访问系统上安装的文件(例如 pip install xxx 或 yum -y install python-xxx)以及您当前正在处理的模块内的文件。
回答by Gaz_Edge
This worked for me using a standard PyCharm project with my virtual environment (venv) under the project root directory.
这对我使用标准 PyCharm 项目和项目根目录下的虚拟环境 (venv) 有效。
Code below isnt the prettiest, but consistently gets the project root. It returns the full directory path to venv from the VIRTUAL_ENVenvironment variable e.g. /Users/NAME/documents/PROJECT/venv
下面的代码不是最漂亮的,但始终获得项目根。它从VIRTUAL_ENV环境变量返回到 venv 的完整目录路径,例如/Users/NAME/documents/PROJECT/venv
It then splits the path at the last /, giving an array with two elements. The first element will be the project path e.g. /Users/NAME/documents/PROJECT
然后它在最后分割路径/,给出一个包含两个元素的数组。第一个元素将是项目路径,例如/Users/NAME/documents/PROJECT
import os
print(os.path.split(os.environ['VIRTUAL_ENV'])[0])
回答by Joseph Burnitz
I've recently been trying to do something similar and I have found these answers inadequate for my use cases (a distributed library that needs to detect project root). Mainly I've been battling different environments and platforms, and still haven't found something perfectly universal.
我最近一直在尝试做类似的事情,但我发现这些答案不适用于我的用例(需要检测项目根的分布式库)。主要是我一直在与不同的环境和平台作斗争,但仍然没有找到完全通用的东西。
Code local to project
项目本地代码
I've seen this example mentioned and used in a few places, Django, etc.
我在一些地方看到过这个例子,Django 等。
import os
print(os.path.dirname(os.path.abspath(__file__)))
Simple as this is, it only works when the file that the snippet is in is actually part of the project. We do not retrieve the project directory, but instead the snippet's directory
如此简单,它仅在代码片段所在的文件实际上是项目的一部分时才有效。我们不检索项目目录,而是检索片段的目录
Similarly, the sys.modulesapproach breaks down when calledfrom outside the entrypoint of the application, specifically I've observed a child thread cannot determine this without relation back to the 'main' module. I've explicitly put the import inside a function to demonstrate an import from a child thread, moving it to top level of app.py would fix it.
类似地,当从应用程序的入口点外部调用时,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
设置.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
Running this program produces an attribute error:
运行这个程序会产生一个属性错误:
>>> 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__'
...hence a threading-based solution
...因此是基于线程的解决方案
Location independent
位置无关
Using the same application structure as before but modifying settings.py
使用与之前相同的应用程序结构,但修改 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))
Breaking this down:
First we want to accurately find the thread ID of the main thread. In Python3.4+ the threading library has threading.main_thread()however, everybody doesn't use 3.4+ so we search through all threads looking for the main thread save it's ID. If the main thread has already exited, it won't be listed in the threading.enumerate(). We raise a RuntimeError()in this case until I find a better solution.
分解:首先,我们要准确地找到主线程的线程 ID。threading.main_thread()然而,在 Python3.4+ 中,线程库有,但不是每个人都使用 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")
Next we find the very first stack frame of the main thread. Using the cPython specific functionsys._current_frames()we get a dictionary of every thread's current stack frame. Then utilizing inspect.getouterframes()we can retrieve the entire stack for the main thread and the very first frame.
current_main_frame = sys._current_frames()[main_id]
base_frame = inspect.getouterframes(current_main_frame)[-1]
Finally, the differences between Windows and Linux implementations of inspect.getouterframes()need to be handled. Using the cleaned up filename, os.path.abspath()and os.path.dirname()clean things up.
接下来我们找到主线程的第一个栈帧。使用特定于 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))
So far I've tested this on Python2.7 and 3.6 on Windows as well as Python3.4 on WSL
到目前为止,我已经在 Windows 上的 Python2.7 和 3.6 以及 WSL 上的 Python3.4 上进行了测试
回答by RikH
Other answers advice to use a file in the top-level of the project. This is not necessary if you use pathlib.Pathand parent(Python 3.4 and up). Consider the following directory structure where all files except README.mdand utils.pyhave been omitted.
其他答案建议在项目的顶层使用文件。如果您使用pathlib.Path和parent(Python 3.4 及更高版本),则不需要这样做。考虑以下目录结构,其中除了README.md和之外的所有文件utils.py都被省略了。
project
│ README.md
|
└───src
│ │ utils.py
| | ...
| ...
In utils.pywe define the following function.
在utils.py我们定义以下函数。
from pathlib import Path
def get_project_root() -> Path:
"""Returns project root folder."""
return Path(__file__).parent.parent
In any module in the project we can now get the project root as follows.
在项目中的任何模块中,我们现在可以按如下方式获取项目根目录。
from src.utils import get_project_root
root = get_project_root()
Benefits: Any module which calls get_project_rootcan be moved without changing program behavior. Only when the module utils.pyis moved we have to update get_project_rootand the imports (refactoring tools can be used to automate this).
优点:get_project_root可以在不改变程序行为的情况下移动任何调用的模块。只有当模块utils.py被移动时,我们才必须更新get_project_root和导入(重构工具可用于自动化)。
回答by harry
Try:
尝试:
ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
回答by Martim
All the previous solutions seem to be overly complicated for what I think you need, and often didn't work for me. The following one-line command does what you want:
以前的所有解决方案对于我认为您需要的东西似乎都过于复杂,并且通常对我不起作用。以下单行命令执行您想要的操作:
import os
ROOT_DIR = os.path.abspath(os.curdir)
回答by Guy
I struggled with this problem too until I came to this solution. This is the cleanest solution in my opinion.
在我找到这个解决方案之前,我也一直在努力解决这个问题。在我看来,这是最干净的解决方案。
In your setup.pyadd "packages"
在您的setup.py添加“包”
setup(
name='package_name'
version='0.0.1'
.
.
.
packages=['package_name']
.
.
.
)
In your python_script.py
在你的python_script.py
import pkg_resources
import os
resource_package = pkg_resources.get_distribution(
'package_name').location
config_path = os.path.join(resource_package,'configuration.conf')
回答by Domsch
If you are working with anaconda-project, you can query the PROJECT_ROOT from the environment variable --> os.getenv('PROJECT_ROOT'). This works only if the script is executed via anaconda-project run .
如果您正在使用 anaconda-project,则可以从环境变量 --> os.getenv('PROJECT_ROOT') 中查询 PROJECT_ROOT。这仅在通过 anaconda-project run 执行脚本时才有效。
If you do not want your script run by anaconda-project, you can query the absolute path of the executable binary of the Python interpreter you are using and extract the path string up to the envs directory exclusiv. For example: The python interpreter of my conda env is located at:
如果您不想让 anaconda-project 运行您的脚本,您可以查询您正在使用的 Python 解释器的可执行二进制文件的绝对路径,并将路径字符串提取到 envs 目录中。例如:我的 conda env 的 python 解释器位于:
/home/user/project_root/envs/default/bin/python
/home/user/project_root/envs/default/bin/python
# You can first retrieve the env variable PROJECT_DIR.
# If not set, get the python interpreter location and strip off the string till envs inclusiv...
if os.getenv('PROJECT_DIR'):
PROJECT_DIR = os.getenv('PROJECT_DIR')
else:
PYTHON_PATH = sys.executable
path_rem = os.path.join('envs', 'default', 'bin', 'python')
PROJECT_DIR = py_path.split(path_rem)[0]
This works only with conda-project with fixed project structure of a anaconda-project
这仅适用于具有 anaconda-project 固定项目结构的 conda-project

