"""密码解析模块(独立于 auth / config,避免循环 import)。 按优先级解析密码: 环境变量 > keyring > config.ini。 """ from __future__ import annotations import logging logger = logging.getLogger("sapcli.password") # keyring 可用性标志 _KEYRING_AVAILABLE = False _keyring = None _keyring_mod = None try: import keyring as _keyring_mod _keyring = _keyring_mod _KEYRING_AVAILABLE = True except ImportError: logger.debug("keyring 库不可用,密码将仅从 config.ini 读取") def _service_name(host: str, client: str) -> str: """构建 keyring 服务名。""" return f"sap-cli:{host}:{client}" def get_password(host: str, client: str, user: str) -> str | None: """从 keyring 获取密码。 Args: host: SAP 主机地址 client: SAP client 号 user: 用户名 Returns: 密码字符串,如果 keyring 不可用则返回 None """ if not _KEYRING_AVAILABLE: return None try: return _keyring.get_password(_service_name(host, client), user) except Exception as e: logger.warning("从 keyring 读取密码失败: %s", e) return None def resolve_password( host: str, client: str, user: str, config_password: str = "", env_password: str = "", ) -> str: """按优先级解析密码: 环境变量 > keyring > config.ini。 Args: host: SAP 主机地址 client: SAP client 号 user: 用户名 config_password: 配置文件中的密码 env_password: 环境变量中的密码 Returns: 最终使用的密码 """ # 环境变量优先 if env_password: return env_password # keyring 其次 kr_password = get_password(host, client, user) if kr_password: return kr_password # config.ini 兜底 return config_password