在 python 包中添加和读取 config.ini 文件

Abh*_*nia 6 python import config configparser python-2.7

我正在编写我想上传到 PyPI 的第一个 python 包。我根据这篇博文构建了我的代码。

我想将用户设置存储在 config.ini 文件中。在同一包中的单独 python 模块中读取一次(每次包运行时),并将用户设置保存在该模块的全局变量中。稍后导入其他模块中的那些。

为了重现错误,我刚刚在博客文章中描述的模板中编辑了几行代码。(请参考它,因为在此处重新创建整个内容需要太多的输入。)

唯一的区别是我stuff.py从这样的配置文件中读取:

from ConfigParser import SafeConfigParser

config = SafeConfigParser()
config.read('config.ini')

TEST_KEY = config.get('main', 'test_key')
Run Code Online (Sandbox Code Playgroud)

以下是config.ini(放置在与 相同的目录中stuff.py)的内容:

[main]
test_key=value
Run Code Online (Sandbox Code Playgroud)

而我bootstrap.py刚刚导入并打印TEST_KEY

from .stuff import TEST_KEY

def main():
    print(TEST_KEY)
Run Code Online (Sandbox Code Playgroud)

但是在执行包时,导入失败给出这个错误

Traceback (most recent call last):
    File "D:\Coding\bootstrap\bootstrap-runner.py", line 8, in <module>
    from bootstrap.bootstrap import main
    File "D:\Coding\bootstrap\bootstrap\bootstrap.py", line 11, in <module>
    from .stuff import TEST_KEY
    File "D:\Coding\bootstrap\bootstrap\stuff.py", line 14, in <module>
    TEST_KEY = config.get('main', 'test_key')
    File "C:\Python27\Lib\ConfigParser.py", line 607, in get
    raise NoSectionError(section)
ConfigParser.NoSectionError: No section: 'main'
Run Code Online (Sandbox Code Playgroud)

进口一直给ConfigParser.NoSectionError,但如果你建立/只运行stuff.py(我使用sublime3),该模块提供任何错误和打印TEST_KEYvalue作为输出。

此外,当我只在目录中使用 3 个文件(config、stuff、main)并将 main 作为脚本执行时,这种导入方法确实有效。但我不得不像这样导入它

from stuff import TEST_KEY
Run Code Online (Sandbox Code Playgroud)

我只是使用该帖子中描述的显式相对导入,但对这些没有足够的了解。我猜这个错误是由于项目结构和导入造成的,因为stuff.py作为独立脚本运行不会引发ConfigParser.NoSectionError.

读取配置文件一次然后在其他模块中使用数据的其他方法也将非常有用。

Abh*_*nia 7

这个问题有两个方面。首先ConfigParser. 何时ConfigParser无法定位.ini文件;由于某些烦人的原因,它永远不会给出一个IOError或错误,表明它无法读取文件。

在我的情况下,ConfigParser.NoSectionErrorsection明确存在时,它会继续给予。当我发现ConfigParser.NoSectionError错误时,它给出了一个ImportError! 但它永远不会告诉您它根本无法读取文件。

其次是如何安全地读取包中包含数据文件。我发现这样做的唯一方法是使用__file__参数。config.ini对于 Python27 和 Python3,您可以这样安全地阅读上述问题中的内容:

import os

try:
    # >3.2
    from configparser import ConfigParser
except ImportError:
    # python27
    # Refer to the older SafeConfigParser as ConfigParser
    from ConfigParser import SafeConfigParser as ConfigParser

config = ConfigParser()

# get the path to config.ini
config_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'config.ini')

# check if the path is to a valid file
if not os.path.isfile(config_path):
    raise BadConfigError # not a standard python exception

config.read(config_path)

TEST_KEY = config.get('main', 'test_key') # value
Run Code Online (Sandbox Code Playgroud)

这取决于config.ini位于我们包裹内bootstrap并预计随包裹一起发货的事实。

重要的是你如何获得config_path

config_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'config.ini')
Run Code Online (Sandbox Code Playgroud)

__file__指的是当前script正在执行的位置。在我的问题中,这意味着stuff.py, 即bootstrap文件夹内的位置,config.ini.

上面的代码行意味着; 获取到的绝对路径stuff.py;从那个获取路径到包含它的目录;并将其加入config.ini(因为它在同一目录中)以提供config.ini. 然后你可以继续阅读它并提出一个exception以防万一。

即使您发布软件包pip并且用户从那里安装它,这也将起作用。

作为奖励,并且稍微偏离问题,如果您要在包中pip使用数据文件发布包,则必须setuptools在构建sdistbdists时告诉将它们包含在包中。所以要包含config.ini在上面的包中,将以下行添加到setup类调用中setup.py

include_package_data = True,
package_data = {
    # If any package contains *.ini files, include them
    '': ['*.ini'],
},
Run Code Online (Sandbox Code Playgroud)

但在某些情况下它仍然可能不起作用,例如。建造轮子等。所以你也在你的MANIFEST.IN文件中做同样的事情:

include LICENSE
include bootstrap/*.ini
Run Code Online (Sandbox Code Playgroud)


小智 3

abhimanyuPathania:问题在于config.iniin的路径stuff.py。更改config.read('config.ini')config.read('./bootstrap/config.ini')stuff.py. 我尝试了解决方案。这个对我有用。

享受Python语言...