fix: 强制设置有效 locale 修复 vnpy_ctp CTP 登录崩溃
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
import os
|
||||
|
||||
# vnpy_ctp C++ 扩展需要有效 locale,否则 CTP 登录后可能崩溃
|
||||
os.environ.setdefault("LANG", "C.UTF-8")
|
||||
os.environ.setdefault("LC_ALL", "C.UTF-8")
|
||||
from locale_fix import ensure_process_locale
|
||||
|
||||
ensure_process_locale()
|
||||
|
||||
import sqlite3
|
||||
import time
|
||||
|
||||
@@ -29,8 +29,8 @@ need_install git git
|
||||
# vnpy_ctp 在 Linux 上需本地编译(Meson + pkg-config 查找 python3-dev)
|
||||
echo "==> 安装 vnpy_ctp 编译依赖..."
|
||||
apt-get install -y build-essential python3-dev pkg-config locales
|
||||
locale-gen en_US.UTF-8 2>/dev/null || true
|
||||
update-locale LANG=C.UTF-8 LC_ALL=C.UTF-8 2>/dev/null || true
|
||||
locale-gen en_US.UTF-8 C.UTF-8 2>/dev/null || true
|
||||
update-locale LANG=en_US.UTF-8 LC_ALL=en_US.UTF-8 2>/dev/null || true
|
||||
|
||||
if ! command -v pm2 &>/dev/null; then
|
||||
echo "==> 安装 PM2..."
|
||||
|
||||
+1
-1
@@ -346,7 +346,7 @@ pm2 restart qihuo
|
||||
|-----------|------|
|
||||
| `pip install vnpy_ctp` 编译失败 / `Python dependency not found` | 安装 `build-essential python3-dev pkg-config` 后重试 |
|
||||
| CTP 连接超时 | 检查前置 IP、端口、SimNow 是否维护、是否在允许连接时段 |
|
||||
| 连接后立即崩溃 `locale::facet::_S_create_c_locale` | 系统 locale 未配置;`export LANG=C.UTF-8 LC_ALL=C.UTF-8` 后 `pm2 restart qihuo`,或 `git pull` 使用最新 `ecosystem.config.cjs` |
|
||||
| 连接后立即崩溃 `locale::facet::_S_create_c_locale` | 安装 locale:`apt install -y locales && locale-gen en_US.UTF-8`,`git pull` 后 `pm2 restart qihuo --update-env` |
|
||||
| 服务器 `180.168.146.187` 超时 | 换 SimNow 备用前置 `182.254.243.31:30001/30011`(见 [SIMNOW.md](./SIMNOW.md)) |
|
||||
| 已连接但下单拒单 | 检查合约代码、价格精度、是否有足够保证金 |
|
||||
|
||||
|
||||
+1
-1
@@ -183,7 +183,7 @@ python scripts/test_simnow.py
|
||||
| 报错 **4097** / 握手失败 | `pip install -U vnpy vnpy_ctp`,`.env` 设 `SIMNOW_ENV=实盘` |
|
||||
| **不合法的登录** | 投资者代码/密码错,或未在快期改过一次密码 |
|
||||
| 快期能登、脚本不能 | 多为网络或前置地址,换 SimNow 官网其他组前置试 |
|
||||
| 连上后进程崩溃 `locale::facet::_S_create_c_locale` | 执行 `export LANG=C.UTF-8 LC_ALL=C.UTF-8`,`pm2 restart qihuo --update-env` |
|
||||
| 连上后进程崩溃 `locale::facet::_S_create_c_locale` | `apt install -y locales && locale-gen en_US.UTF-8`,`git pull` 后 `pm2 restart qihuo --update-env` |
|
||||
|
||||
### 提示「未安装 vnpy / vnpy_ctp」
|
||||
|
||||
|
||||
@@ -11,9 +11,9 @@ module.exports = {
|
||||
max_memory_restart: "300M",
|
||||
env: {
|
||||
NODE_ENV: "production",
|
||||
LANG: "C.UTF-8",
|
||||
LC_ALL: "C.UTF-8",
|
||||
LC_CTYPE: "C.UTF-8",
|
||||
LANG: "en_US.UTF-8",
|
||||
LC_ALL: "en_US.UTF-8",
|
||||
LC_CTYPE: "en_US.UTF-8",
|
||||
},
|
||||
error_file: "/opt/qihuo/logs/pm2-error.log",
|
||||
out_file: "/opt/qihuo/logs/pm2-out.log",
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
"""Linux 上 vnpy_ctp 连接 CTP 前须设置有效 locale(否则 C++ 层 abort)。"""
|
||||
from __future__ import annotations
|
||||
|
||||
import locale
|
||||
import logging
|
||||
import os
|
||||
import subprocess
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
_LOCALE_DONE = False
|
||||
_LOCALE_NAME = ""
|
||||
|
||||
|
||||
def _list_locale_candidates() -> list[str]:
|
||||
names: list[str] = []
|
||||
for item in (
|
||||
"C.UTF-8",
|
||||
"en_US.UTF-8",
|
||||
"en_US.utf8",
|
||||
"POSIX",
|
||||
"C",
|
||||
):
|
||||
if item not in names:
|
||||
names.append(item)
|
||||
try:
|
||||
out = subprocess.check_output(["locale", "-a"], text=True, stderr=subprocess.DEVNULL)
|
||||
for line in out.splitlines():
|
||||
loc = line.strip()
|
||||
if not loc:
|
||||
continue
|
||||
low = loc.lower()
|
||||
if "utf" in low and loc not in names:
|
||||
names.insert(0, loc)
|
||||
except (OSError, subprocess.SubprocessError):
|
||||
pass
|
||||
return names
|
||||
|
||||
|
||||
def ensure_process_locale() -> str:
|
||||
"""强制设置进程 locale,覆盖系统里无效的旧值。"""
|
||||
global _LOCALE_DONE, _LOCALE_NAME
|
||||
if _LOCALE_DONE:
|
||||
return _LOCALE_NAME
|
||||
|
||||
last_err: locale.Error | None = None
|
||||
for name in _list_locale_candidates():
|
||||
try:
|
||||
locale.setlocale(locale.LC_ALL, name)
|
||||
os.environ["LANG"] = name
|
||||
os.environ["LC_ALL"] = name
|
||||
os.environ["LC_CTYPE"] = name
|
||||
_LOCALE_DONE = True
|
||||
_LOCALE_NAME = name
|
||||
logger.info("进程 locale 已设置: %s", name)
|
||||
return name
|
||||
except locale.Error as exc:
|
||||
last_err = exc
|
||||
continue
|
||||
|
||||
raise RuntimeError(
|
||||
"未找到可用 locale,vnpy_ctp 会在 CTP 登录后崩溃。"
|
||||
"请执行: apt install -y locales && locale-gen en_US.UTF-8 && "
|
||||
"update-locale LANG=en_US.UTF-8 LC_ALL=en_US.UTF-8"
|
||||
) from last_err
|
||||
@@ -6,12 +6,13 @@ import os
|
||||
import socket
|
||||
import sys
|
||||
|
||||
os.environ.setdefault("LANG", "C.UTF-8")
|
||||
os.environ.setdefault("LC_ALL", "C.UTF-8")
|
||||
|
||||
BASE = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
sys.path.insert(0, BASE)
|
||||
|
||||
from locale_fix import ensure_process_locale
|
||||
|
||||
ensure_process_locale()
|
||||
|
||||
from dotenv import load_dotenv
|
||||
|
||||
load_dotenv(os.path.join(BASE, ".env"))
|
||||
@@ -38,6 +39,7 @@ def main() -> int:
|
||||
env = os.getenv("SIMNOW_ENV", "实盘")
|
||||
|
||||
print("=== SimNow 配置 ===")
|
||||
print(f"locale = {ensure_process_locale()}")
|
||||
print(f"SIMNOW_USER = {user or '(未设置)'}")
|
||||
print(f"SIMNOW_PASSWORD = {'*' * 8 if os.getenv('SIMNOW_PASSWORD') else '(未设置)'}")
|
||||
print(f"SIMNOW_TD = {td}")
|
||||
|
||||
+5
-3
@@ -7,9 +7,9 @@ import threading
|
||||
import time
|
||||
from typing import Any, Optional
|
||||
|
||||
# vnpy_ctp C++ 在部分 Linux 上缺 locale 会抛 std::runtime_error
|
||||
os.environ.setdefault("LANG", "C.UTF-8")
|
||||
os.environ.setdefault("LC_ALL", "C.UTF-8")
|
||||
from locale_fix import ensure_process_locale
|
||||
|
||||
ensure_process_locale()
|
||||
|
||||
from ctp_symbol import ths_to_vnpy_symbol, to_vnpy_exchange
|
||||
|
||||
@@ -89,6 +89,7 @@ class CtpBridge:
|
||||
self._init_engine()
|
||||
|
||||
def _init_engine(self) -> None:
|
||||
ensure_process_locale()
|
||||
try:
|
||||
from vnpy.event import EventEngine
|
||||
from vnpy.trader.engine import MainEngine
|
||||
@@ -163,6 +164,7 @@ class CtpBridge:
|
||||
|
||||
self._ee.register(EVENT_LOG, _on_log)
|
||||
try:
|
||||
ensure_process_locale()
|
||||
logger.info(
|
||||
"CTP 连接 [%s] user=%s td=%s env=%s",
|
||||
mode,
|
||||
|
||||
Reference in New Issue
Block a user