如何避免pdoc泄露pydantic-settings的环境变量?
解决方案
方案1:自定义pdoc渲染钩子,修改变量显示
pdoc支持通过钩子函数自定义渲染逻辑,我们可以拦截模块中Settings实例的显示,仅输出类型名称而非具体值:
- 创建钩子文件
pdoc_hook.py:
import pdoc.render.html def custom_format_module_variable(value, ctx): # 仅对当前模块的Settings实例做处理 if hasattr(ctx.module, 'Settings') and isinstance(value, ctx.module.Settings): return f"{type(value).__name__}" # 其他变量沿用默认格式化逻辑 return pdoc.render.html.format_module_variable.__wrapped__(value, ctx) # 替换默认的模块变量格式化函数 pdoc.render.html.format_module_variable = custom_format_module_variable
- 运行pdoc时指定钩子:
pdoc --hook pdoc_hook.py my_app
优点:无需修改业务代码,仅在文档生成时生效,运行时的__repr__完全不受影响。
缺点:需要维护额外的钩子文件,对pdoc版本有一定依赖(需确保钩子API兼容)。
方案2:模块级延迟初始化,避免pdoc扫描时实例化
通过模块的__getattr__实现SETTINGS的延迟加载,让pdoc在扫描模块时不会触发实例化,自然不会显示具体值:
# my_app/config.py from pydantic_settings import BaseSettings from typing import Optional class Settings(BaseSettings): app_name: str = "Awesome API" admin_email: str items_per_user: int = 50 # 私有变量存储实例,初始为None _SETTINGS: Optional[Settings] = None def __getattr__(name): if name == "SETTINGS": global _SETTINGS if _SETTINGS is None: _SETTINGS = Settings() return _SETTINGS raise AttributeError(f"module {__name__} has no attribute {name}") # 导出符号,确保其他模块可以正常导入 __all__ = ["SETTINGS", "Settings"] """ 全局配置实例SETTINGS: 通过延迟加载机制初始化,包含应用的所有配置参数(来自config.yaml、.env及环境变量)。 其他模块可通过`from my_app.config import SETTINGS`直接访问。 """
优点:业务代码改动小,完全隔离文档生成和运行时的实例化逻辑,不会泄露任何配置值。
缺点:需要理解模块级__getattr__的行为,首次访问SETTINGS时会触发初始化(对性能影响可忽略)。
方案3:基于环境变量控制__repr__输出
在Settings类的__repr__方法中判断当前是否处于文档生成环境,动态调整输出内容:
# my_app/config.py import os from pydantic_settings import BaseSettings class Settings(BaseSettings): app_name: str = "Awesome API" admin_email: str items_per_user: int = 50 def __repr__(self): # 检测是否在pdoc生成环境 if os.getenv("PDOC_BUILD"): return self.__class__.__name__ # 运行时保留完整repr用于日志 return super().__repr__() SETTINGS = Settings()
运行pdoc时设置环境变量:
PDOC_BUILD=1 pdoc my_app
优点:逻辑简单直观,无需额外文件,仅在文档生成时修改输出。
缺点:需要确保文档生成流程中正确设置环境变量,否则仍会泄露值。
更优的跨模块配置共享方案
上述方案已能解决文档泄露问题,若要进一步优化配置共享:
- 采用依赖注入(如FastAPI的
Depends):在需要配置的地方通过Depends(get_settings)获取实例,而非直接导入模块变量,这种方式更易于测试(可替换测试配置)。 - 使用单例模式封装:将
get_settings函数封装成一个单例类,确保全局只有一个配置实例,同时提供清晰的访问入口。
内容的提问来源于stack exchange,提问作者aleneum
相关产品推荐
相关产品推荐

