Files
2026-06-26 20:50:07 +08:00

60 lines
2.1 KiB
Python

"""Runtime configuration loader — reads secrets from a gitignored ``.env``.
Broker login lives in ``.env`` (gitignored). The bridge reads these at runtime
and injects them into ``tester.ini``'s ``[Common]`` section **without printing
them** (doc 07 §5). Never hard-code credentials and never print them.
"""
from __future__ import annotations
import os
from pathlib import Path
from typing import Optional
try:
from dotenv import load_dotenv # type: ignore
_HAS_DOTENV = True
except ImportError:
_HAS_DOTENV = False
def load_env(project_root: Optional[str | Path] = None) -> dict[str, str]:
"""Load ``.env`` from ``project_root`` (defaults to this file's parent).
Returns the MT5 credential keys without their values exposed in logs.
Falls back to plain line parsing if ``python-dotenv`` isn't installed.
"""
root = Path(project_root) if project_root else Path(__file__).resolve().parent.parent
env_path = root / ".env"
if _HAS_DOTENV and env_path.exists():
load_dotenv(env_path)
elif env_path.exists():
_parse_plain(env_path)
return {
"MT5_DEMO_LOGIN": os.environ.get("MT5_DEMO_LOGIN", ""),
"MT5_DEMO_SERVER": os.environ.get("MT5_DEMO_SERVER", ""),
# Password is intentionally not returned here; read via get_secret().
"MT5_TERMINAL_PATH": os.environ.get(
"MT5_TERMINAL_PATH",
r"C:\Program Files\MetaTrader 5 IC Markets Global\terminal64.exe",
),
}
def get_secret(name: str) -> str:
"""Read a secret from the environment (load_env must be called first)."""
return os.environ.get(name, "")
def _parse_plain(env_path: Path) -> None:
"""Minimal ``.env`` parser (KEY=VALUE) without python-dotenv."""
for line in env_path.read_text(encoding="utf-8").splitlines():
line = line.strip()
if not line or line.startswith("#") or "=" not in line:
continue
k, _, v = line.partition("=")
k, v = k.strip(), v.strip()
# Strip optional surrounding quotes.
if len(v) >= 2 and v[0] == v[-1] and v[0] in ("'", '"'):
v = v[1:-1]
os.environ.setdefault(k, v)