"""
環境檢測工具模組
Environment detection utilities
"""
import os
import sys
import io
import json
from ..debug import server_debug_log as debug_log
# 常數定義
SSH_ENV_VARS = ["SSH_CONNECTION", "SSH_CLIENT", "SSH_TTY"]
REMOTE_ENV_VARS = ["REMOTE_CONTAINERS", "CODESPACES"]
def init_encoding() -> bool:
"""初始化編碼設置,確保正確處理中文字符"""
try:
# Windows 特殊處理
if sys.platform == "win32":
import msvcrt
# 設置為二進制模式
msvcrt.setmode(sys.stdin.fileno(), os.O_BINARY)
msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)
# 重新包裝為 UTF-8 文本流,並禁用緩衝
# 修復 union-attr 錯誤 - 安全獲取 buffer 或 detach
stdin_buffer = getattr(sys.stdin, "buffer", None)
if stdin_buffer is None and hasattr(sys.stdin, "detach"):
stdin_buffer = sys.stdin.detach()
stdout_buffer = getattr(sys.stdout, "buffer", None)
if stdout_buffer is None and hasattr(sys.stdout, "detach"):
stdout_buffer = sys.stdout.detach()
sys.stdin = io.TextIOWrapper(
stdin_buffer, encoding="utf-8", errors="replace", newline=None
)
sys.stdout = io.TextIOWrapper(
stdout_buffer,
encoding="utf-8",
errors="replace",
newline="",
write_through=True, # 關鍵:禁用寫入緩衝
)
else:
# 非 Windows 系統的標準設置
if hasattr(sys.stdout, "reconfigure"):
sys.stdout.reconfigure(encoding="utf-8", errors="replace")
if hasattr(sys.stdin, "reconfigure"):
sys.stdin.reconfigure(encoding="utf-8", errors="replace")
# 設置 stderr 編碼(用於調試訊息)
if hasattr(sys.stderr, "reconfigure"):
sys.stderr.reconfigure(encoding="utf-8", errors="replace")
return True
except Exception:
# 如果編碼設置失敗,嘗試基本設置
try:
if hasattr(sys.stdout, "reconfigure"):
sys.stdout.reconfigure(encoding="utf-8", errors="replace")
if hasattr(sys.stdin, "reconfigure"):
sys.stdin.reconfigure(encoding="utf-8", errors="replace")
if hasattr(sys.stderr, "reconfigure"):
sys.stderr.reconfigure(encoding="utf-8", errors="replace")
except:
pass
return False
def is_wsl_environment() -> bool:
"""
檢測是否在 WSL (Windows Subsystem for Linux) 環境中運行
Returns:
bool: True 表示 WSL 環境,False 表示其他環境
"""
try:
# 檢查 /proc/version 文件是否包含 WSL 標識
if os.path.exists("/proc/version"):
with open("/proc/version") as f:
version_info = f.read().lower()
if "microsoft" in version_info or "wsl" in version_info:
debug_log("偵測到 WSL 環境(通過 /proc/version)")
return True
# 檢查 WSL 相關環境變數
wsl_env_vars = ["WSL_DISTRO_NAME", "WSL_INTEROP", "WSLENV"]
for env_var in wsl_env_vars:
if os.getenv(env_var):
debug_log(f"偵測到 WSL 環境變數: {env_var}")
return True
# 檢查是否存在 WSL 特有的路徑
wsl_paths = ["/mnt/c", "/mnt/d", "/proc/sys/fs/binfmt_misc/WSLInterop"]
for path in wsl_paths:
if os.path.exists(path):
debug_log(f"偵測到 WSL 特有路徑: {path}")
return True
except Exception as e:
debug_log(f"WSL 檢測過程中發生錯誤: {e}")
return False
def is_remote_environment() -> bool:
"""
檢測是否在遠端環境中運行
Returns:
bool: True 表示遠端環境,False 表示本地環境
"""
# WSL 不應被視為遠端環境,因為它可以訪問 Windows 瀏覽器
if is_wsl_environment():
debug_log("WSL 環境不被視為遠端環境")
return False
# 檢查 SSH 連線指標
for env_var in SSH_ENV_VARS:
if os.getenv(env_var):
debug_log(f"偵測到 SSH 環境變數: {env_var}")
return True
# 檢查遠端開發環境
for env_var in REMOTE_ENV_VARS:
if os.getenv(env_var):
debug_log(f"偵測到遠端開發環境: {env_var}")
return True
# 檢查 Docker 容器
if os.path.exists("/.dockerenv"):
debug_log("偵測到 Docker 容器環境")
return True
# Windows 遠端桌面檢查
if sys.platform == "win32":
session_name = os.getenv("SESSIONNAME", "")
if session_name and "RDP" in session_name:
debug_log(f"偵測到 Windows 遠端桌面: {session_name}")
return True
# Linux 無顯示環境檢查(但排除 WSL)
if (
sys.platform.startswith("linux")
and not os.getenv("DISPLAY")
and not is_wsl_environment()
):
debug_log("偵測到 Linux 無顯示環境")
return True
return False
def get_system_info_dict() -> dict:
"""
獲取系統環境資訊字典
Returns:
dict: 系統資訊字典
"""
is_remote = is_remote_environment()
is_wsl = is_wsl_environment()
return {
"平台": sys.platform,
"Python 版本": sys.version.split()[0],
"WSL 環境": is_wsl,
"遠端環境": is_remote,
"介面類型": "Web UI",
"環境變數": {
"SSH_CONNECTION": os.getenv("SSH_CONNECTION"),
"SSH_CLIENT": os.getenv("SSH_CLIENT"),
"DISPLAY": os.getenv("DISPLAY"),
"VSCODE_INJECTION": os.getenv("VSCODE_INJECTION"),
"SESSIONNAME": os.getenv("SESSIONNAME"),
"WSL_DISTRO_NAME": os.getenv("WSL_DISTRO_NAME"),
"WSL_INTEROP": os.getenv("WSL_INTEROP"),
"WSLENV": os.getenv("WSLENV"),
},
}