Python 包层次结构
Python package hierarchy
我开发了一个Python包。我的结构如下:
- my_package
- conf
...
- src
- my_package
- main.py
...
为了执行包,我需要从conf
目录中读取一些配置文件。最初,我考虑将 conf
目录放在包外,以便在用户克隆包存储库时将其暴露给用户,以便用户可以轻松查看其中找到的文件并根据需要进行修改。
当然,用户可以更改配置文件的路径,使其与其位置相匹配。
BUT 我还希望安装包 运行 开箱即用。因此,在代码内部,默认值是相对于main.py
设置的。这适用于开发模式。
但是,安装包时,结构发生变化,变为:
- /some/path/to/env/Lib/site-packages/my_package.egg
- conf
- my_package
main.py
并且我在结构中丢失了一层,从而破坏了我最初设置的相对路径。难道我做错了什么 ?我遵循 these 包装原则。我当然可以很容易地添加一个开发标志,它改变了在开发模式和生产模式下处理路径的方式。但我觉得我在包装规则中遗漏了一些更基本的东西,我想确定一下。
谢谢!
用户配置通常应保存在应用程序配置的 os 特定位置。作为包作者通常的处理方式是
- 编写默认配置文件并将其放入包中,例如在
src/my_package/default.ini
- 这比将配置写成代码要好,就像你的 main.py
可能做的那样,因为你可以重新使用你的解析步骤,可以复制默认配置以获得有效的用户配置,并且只需要维护一个文件类型
- 将默认配置复制到用户特定的目录中,使用
appdirs
找出每个 os 上哪个是正确的
- 您可以询问您的用户是否可以创建新配置,例如
/home/user/.configs/my_app
并在确认后执行,或者只是执行并告诉他们在哪里可以找到它,以防他们想要自定义某些东西
- 在应用程序启动时,首先解析默认配置,然后解析用户配置并覆盖不同的设置
- 如果您还允许通过环境变量进行配置,ose 将在之后进行解析,并且应该覆盖默认配置和用户配置
作为起点,我的配置解析通常看起来像这样:
config.py,在你包裹的某处
from configparser import ConfigParser
from pathlib import Path
import os
# 2nd party in python3.8+ only, pip-install and use importlib_resources on <3.7
import importlib.resources
# 3rd party, needs needs to be pip-installed
from appdirs import user_config_dir
# a ConfigParser instance behaves like a dictionary, but has some nice utility
# like parsing .ini files, a common config file format.
#: import this object to access the package's configuration
config = ConfigParser(interpolation=None)
def load_default_config():
# this is how to access data files within a package
with importlib.resources.path("my_package", "default.ini") as default_config:
with default_config.open() as f:
config.read_file(f, source="default")
def load_user_config():
user_config = Path(appdirs.user_config_dir("my_package")) / "user.ini"
try:
with user_config.open() as f:
config.read_file(f, source="user")
except FileNotFoundError:
print(f"User config expected at '{user_config}', but not found.")
load_default_config()
load_user_config()
load_env() # in case you need it
要填充初始用户配置,您可以 运行 像这样一次:
import importlib.resources
import pathlib
import os
import appdirs
with importlib.resources.path("my_package", "default.ini") as config:
with config.open() as f:
content = f.read()
target = pathlib.Path(appdirs.user_config_dir("my_package")) / "user.ini"
os.makedirs(target.parent)
with open(target, "w") as f:
f.write(content)
print(f"Created new user config at {target}.")
我开发了一个Python包。我的结构如下:
- my_package
- conf
...
- src
- my_package
- main.py
...
为了执行包,我需要从conf
目录中读取一些配置文件。最初,我考虑将 conf
目录放在包外,以便在用户克隆包存储库时将其暴露给用户,以便用户可以轻松查看其中找到的文件并根据需要进行修改。
当然,用户可以更改配置文件的路径,使其与其位置相匹配。
BUT 我还希望安装包 运行 开箱即用。因此,在代码内部,默认值是相对于main.py
设置的。这适用于开发模式。
但是,安装包时,结构发生变化,变为:
- /some/path/to/env/Lib/site-packages/my_package.egg
- conf
- my_package
main.py
并且我在结构中丢失了一层,从而破坏了我最初设置的相对路径。难道我做错了什么 ?我遵循 these 包装原则。我当然可以很容易地添加一个开发标志,它改变了在开发模式和生产模式下处理路径的方式。但我觉得我在包装规则中遗漏了一些更基本的东西,我想确定一下。
谢谢!
用户配置通常应保存在应用程序配置的 os 特定位置。作为包作者通常的处理方式是
- 编写默认配置文件并将其放入包中,例如在
src/my_package/default.ini
- 这比将配置写成代码要好,就像你的main.py
可能做的那样,因为你可以重新使用你的解析步骤,可以复制默认配置以获得有效的用户配置,并且只需要维护一个文件类型 - 将默认配置复制到用户特定的目录中,使用
appdirs
找出每个 os 上哪个是正确的- 您可以询问您的用户是否可以创建新配置,例如
/home/user/.configs/my_app
并在确认后执行,或者只是执行并告诉他们在哪里可以找到它,以防他们想要自定义某些东西
- 您可以询问您的用户是否可以创建新配置,例如
- 在应用程序启动时,首先解析默认配置,然后解析用户配置并覆盖不同的设置
- 如果您还允许通过环境变量进行配置,ose 将在之后进行解析,并且应该覆盖默认配置和用户配置
作为起点,我的配置解析通常看起来像这样:
config.py,在你包裹的某处
from configparser import ConfigParser
from pathlib import Path
import os
# 2nd party in python3.8+ only, pip-install and use importlib_resources on <3.7
import importlib.resources
# 3rd party, needs needs to be pip-installed
from appdirs import user_config_dir
# a ConfigParser instance behaves like a dictionary, but has some nice utility
# like parsing .ini files, a common config file format.
#: import this object to access the package's configuration
config = ConfigParser(interpolation=None)
def load_default_config():
# this is how to access data files within a package
with importlib.resources.path("my_package", "default.ini") as default_config:
with default_config.open() as f:
config.read_file(f, source="default")
def load_user_config():
user_config = Path(appdirs.user_config_dir("my_package")) / "user.ini"
try:
with user_config.open() as f:
config.read_file(f, source="user")
except FileNotFoundError:
print(f"User config expected at '{user_config}', but not found.")
load_default_config()
load_user_config()
load_env() # in case you need it
要填充初始用户配置,您可以 运行 像这样一次:
import importlib.resources
import pathlib
import os
import appdirs
with importlib.resources.path("my_package", "default.ini") as config:
with config.open() as f:
content = f.read()
target = pathlib.Path(appdirs.user_config_dir("my_package")) / "user.ini"
os.makedirs(target.parent)
with open(target, "w") as f:
f.write(content)
print(f"Created new user config at {target}.")