Files
playwrite/config/env_loader.py
Misaka aaa46ef282 feat: migrate configuration to .env environment variables
This commit implements a complete migration from JSON-based configuration
to .env environment variables, providing better security and flexibility.

Key Changes:
- Add python-dotenv dependency for environment variable support
- Create config/env_loader.py with type conversion utilities
- Add from_env() class methods to all config dataclasses
- Update ConfigLoader to prioritize environment variables
- Add save_to_env() method for .env file management
- Implement database connection factory pattern
- Add base DAO and connection classes for better abstraction
- Support both SQL Server and MySQL with unified interface
- Create migration script (scripts/migrate_to_env.py)
- Update GUI to read/write .env files
- Add comprehensive migration documentation

New Files:
- config/env_loader.py - Environment variable loader
- db/base_connection.py - Base database connection interface
- db/base_dao.py - Base DAO with common utilities
- db/connection_factory.py - Factory for creating connections
- db/mysql_connection.py - MySQL-specific connection
- db/sqlserver_connection.py - SQL Server-specific connection
- db/table_name_converter.py - SQL dialect converter
- scripts/migrate_to_env.py - Configuration migration tool
- docs/ENV_MIGRATION.md - Complete migration guide
- .env.example - Environment variable template

Testing:
- Verified MySQL connection (8.0.44)
- Tested all DAO operations
- Confirmed 150 tables accessible
- Validated configuration loading

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-09 22:39:14 +08:00

202 lines
5.0 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
环境变量加载器
使用 python-dotenv 加载 .env 文件,并提供类型转换功能。
"""
import os
from pathlib import Path
from typing import Any, Optional, Type, TypeVar
from dotenv import load_dotenv
# 项目根目录
PROJECT_ROOT = Path(__file__).parent.parent
def load_env_file(env_file: Optional[str] = None) -> None:
"""
加载 .env 文件
Args:
env_file: .env 文件路径,默认为项目根目录下的 .env
"""
if env_file is None:
env_file = PROJECT_ROOT / ".env"
else:
env_file = Path(env_file)
load_dotenv(env_file)
def get_env(key: str, default: Any = None) -> str:
"""
获取环境变量
Args:
key: 环境变量名
default: 默认值
Returns:
环境变量值
"""
return os.getenv(key, default)
def get_env_bool(key: str, default: bool = False) -> bool:
"""
获取布尔类型环境变量
Args:
key: 环境变量名
default: 默认值
Returns:
布尔值
"""
value = os.getenv(key, "")
if not value:
return default
return value.lower() in ("true", "1", "yes", "on")
def get_env_int(key: str, default: int = 0) -> int:
"""
获取整数类型环境变量
Args:
key: 环境变量名
default: 默认值
Returns:
整数值
"""
value = os.getenv(key, "")
if not value:
return default
try:
return int(value)
except ValueError:
return default
def get_env_float(key: str, default: float = 0.0) -> float:
"""
获取浮点数类型环境变量
Args:
key: 环境变量名
default: 默认值
Returns:
浮点数值
"""
value = os.getenv(key, "")
if not value:
return default
try:
return float(value)
except ValueError:
return default
def set_env(key: str, value: Any) -> None:
"""
设置环境变量(仅在当前进程中有效)
Args:
key: 环境变量名
value: 环境变量值
"""
os.environ[key] = str(value)
def save_env_file(env_file: Optional[str] = None, env_dict: Optional[dict] = None) -> bool:
"""
保存环境变量到 .env 文件
Args:
env_file: .env 文件路径,默认为项目根目录下的 .env
env_dict: 要保存的环境变量字典,如果为 None 则保存当前所有环境变量
Returns:
保存是否成功
"""
if env_file is None:
env_file = PROJECT_ROOT / ".env"
else:
env_file = Path(env_file)
try:
# 确保目录存在
env_file.parent.mkdir(parents=True, exist_ok=True)
# 读取现有的 .env 文件以保留注释
existing_lines = []
if env_file.exists():
with open(env_file, "r", encoding="utf-8") as f:
existing_lines = f.readlines()
# 如果提供了 env_dict则保存指定的环境变量
if env_dict is not None:
# 构建新的文件内容
new_content = []
processed_keys = set()
for line in existing_lines:
stripped = line.strip()
# 保留注释和空行
if not stripped or stripped.startswith("#"):
new_content.append(line)
# 更新已存在的键值对
elif "=" in stripped and not stripped.startswith("#"):
key = stripped.split("=")[0].strip()
if key in env_dict:
value = env_dict[key]
# 处理布尔值的格式
if isinstance(value, bool):
value = "true" if value else "false"
new_content.append(f"{key}={value}\n")
processed_keys.add(key)
else:
new_content.append(line)
# 添加新的键值对
for key, value in env_dict.items():
if key not in processed_keys:
# 处理布尔值的格式
if isinstance(value, bool):
value = "true" if value else "false"
new_content.append(f"{key}={value}\n")
# 写入文件
with open(env_file, "w", encoding="utf-8") as f:
f.writelines(new_content)
else:
# 如果没有提供 env_dict则不执行任何操作
# 因为保存所有环境变量可能会包含系统变量
return False
return True
except IOError as e:
print(f"保存 .env 文件失败: {e}")
return False
def update_env_file(env_file: Optional[str] = None, **kwargs) -> bool:
"""
更新 .env 文件中的特定环境变量
Args:
env_file: .env 文件路径
**kwargs: 要更新的环境变量键值对
Returns:
更新是否成功
"""
return save_env_file(env_file, kwargs)
# 自动加载 .env 文件
load_env_file()