我正在编写我想在PyPI上传的第一个python包。我根据此blog post构建了我的代码。
我想将用户设置存储在config.ini文件中。在同一个包中的单独python模块中读取一次(每次运行包)并将用户设置保存在该模块的全局变量中。稍后在其他模块中导入。
要重新创建错误,我在博客文章中描述的模板中编辑了几行代码。 (请参考它,因为在此处重新创建整个内容需要太多的输入。)
唯一的区别是我的stuff.py
从配置文件读取如下:
from ConfigParser import SafeConfigParser
config = SafeConfigParser()
config.read('config.ini')
TEST_KEY = config.get('main', 'test_key')
以下是config.ini
的内容(与stuff.py
位于同一目录中):
[main]
test_key=value
我的bootstrap.py
只会导入并打印TEST_KEY
from .stuff import TEST_KEY
def main():
print(TEST_KEY)
但是在执行包时,导入失败会出现此错误
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'
导入不断提供 ConfigParser.NoSectionError ,但如果你只构建/运行stuff.py(我使用sublime3),模块不会出现错误,打印TEST_KEY
会给出{{1}作为输出。
另外,当我在dir中使用3个文件(config,stuff,main)时,这种导入方法确实有效,只需将main作为脚本执行即可。但我必须像这样导入它
value
我只是使用该帖子中描述的显式相对导入,但对这些没有足够的理解。我猜错误是由于项目结构和导入,因为作为独立脚本运行from stuff import TEST_KEY
不会引发stuff.py
。
读取配置文件一次然后在其他模块中使用数据的其他方法也非常有用。
答案 0 :(得分:1)
abhimanyuPathania:问题在于config.ini
中stuff.py
的路径。在config.read('config.ini')
中将config.read('./bootstrap/config.ini')
更改为stuff.py
。我尝试了解决方案。它对我有用。
享受Pythoning ......
答案 1 :(得分:0)
这个问题有两个方面。 首先是ConfigParser
的奇怪行为。 ConfigParser
无法找到.ini
文件时;出于某些恼人的原因,它永远不会给出IOError
或错误,表明它无法读取文件。
就我而言,当ConfigParser.NoSectionError
明显存在时,它会继续提供section
。当我发现ConfigParser.NoSectionError
错误时,它给了ImportError
!但它永远不会告诉你,它根本无法读取文件。
第二是如何安全地阅读包中包含的数据文件。我发现这样做的唯一方法是使用__file__
参数。对于Python27和Python3,您可以安全地阅读上述问题中的config.ini
:
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
这取决于config.ini
位于我们的包bootstrap
内并且预计会附带它的事实。
重要的是你如何得到config_path
:
config_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'config.ini')
__file__
指的是正在执行的当前script
的位置。在我的问题中,这意味着stuff.py
的位置,位于bootstrap
文件夹内,config.ini
也是如此。
上面的代码行意味着;得到stuff.py
的绝对路径;从那个获取路径到包含它的目录;并将其与config.ini
连接(因为它位于同一目录中),以提供config.ini
的绝对路径。然后,您可以继续阅读并提出exception
以防万一。
即使您在pip
上发布包并且用户从那里安装包,这也会有用。
作为奖励,并且稍微偏离了这个问题,如果您要在pip
上发布包中的数据文件,那么您必须告诉setuptools
在构建sdist
和bdist
时将它们包含在您的包中。因此,要在config.ini
setup
中添加setup.py
类调用,请在上面的包中添加include_package_data = True,
package_data = {
# If any package contains *.ini files, include them
'': ['*.ini'],
},
:
MANIFEST.IN
但在某些情况下它仍然可能无效,例如。建立轮子等。所以你也在include LICENSE
include bootstrap/*.ini
文件中做同样的事情:
{{1}}