From b275b4ebea08870a19788391376f3a13c6545b17 Mon Sep 17 00:00:00 2001 From: yejmin Date: Wed, 22 Jul 2026 23:36:06 +0800 Subject: [PATCH] fix: prevent mimetypes hang on Windows by skipping registry init (#2059) Co-authored-by: E Co-authored-by: zhulinsen <42829555+ZhuLinsen@users.noreply.github.com> --- api/app.py | 11 ++++ docs/CHANGELOG.md | 1 + tests/test_mimetypes_windows_init.py | 89 ++++++++++++++++++++++++++++ 3 files changed, 101 insertions(+) create mode 100644 tests/test_mimetypes_windows_init.py diff --git a/api/app.py b/api/app.py index f69d113db..9c4be5b87 100644 --- a/api/app.py +++ b/api/app.py @@ -19,6 +19,17 @@ import asyncio import json import logging import mimetypes + +import sys + +if sys.platform == "win32" and not mimetypes.inited: + _orig_read_windows_registry = getattr(mimetypes.MimeTypes, 'read_windows_registry', None) + if _orig_read_windows_registry is not None: + mimetypes.MimeTypes.read_windows_registry = lambda self, strict=True: None + try: mimetypes.init() + finally: mimetypes.MimeTypes.read_windows_registry = _orig_read_windows_registry + else: + mimetypes.init() import os import re from contextlib import asynccontextmanager, suppress diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 347d9a8b1..a0c7706d5 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -18,6 +18,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/). - [新功能] Tushare 数据源支持通过 `TUSHARE_HTTP_URL` 环境变量自定义接入地址,便于网络无法直达 `api.tushare.pro` 时切换自建网关或第三方兼容镜像;留空保持官方默认地址不变(fixes #1985) - [文档] `.env.example` 与 `.github/workflows/00-daily-analysis.yml` 同步映射 `TUSHARE_HTTP_URL`,避免出现"配置项有但 workflow 漏映射"的半修状态 - [修复] #2051 PR Review 的特权 `pull_request_target` 流程不再检出 fork PR head:敏感文件、标签、报告与 AI 审查统一通过 GitHub API 将 PR 元数据和 diff 作为数据读取,只执行主分支可信脚本;Python 语法、Flake8、确定性检查和离线测试继续由无 secrets 的 `pull_request` CI / `backend-gate` 执行,兼容 `actions/checkout` 新增的 fork checkout 安全保护。 +- [修复] 修复 Windows 上 mimetypes 冷启动时读取注册表导致的进程卡死 ## [3.27.0] - 2026-07-19 diff --git a/tests/test_mimetypes_windows_init.py b/tests/test_mimetypes_windows_init.py new file mode 100644 index 000000000..4e32b6cba --- /dev/null +++ b/tests/test_mimetypes_windows_init.py @@ -0,0 +1,89 @@ +# -*- coding: utf-8 -*- +"""Tests for mimetypes cold-start behaviour on Windows.""" + +import importlib +import mimetypes +import sys +import unittest +from unittest.mock import patch + + +class MimetypesWindowsInitTestCase(unittest.TestCase): + """Cold-start: Windows skips registry; non-Windows keeps full MIME db.""" + + def setUp(self): + self._types_map_backup = dict(mimetypes.types_map) + + def tearDown(self): + mimetypes.types_map.clear() + mimetypes.types_map.update(self._types_map_backup) + + @staticmethod + def _simulate_cold_start(): + mimetypes.inited = False + mimetypes._db = None + + def test_windows_cold_import_skips_registry(self): + """Windows cold-start must not call read_windows_registry.""" + import api.app + + self._simulate_cold_start() + with patch("sys.platform", "win32"), \ + patch.object(mimetypes.MimeTypes, "read_windows_registry") as mock_registry: + importlib.reload(api.app) + + mock_registry.assert_not_called() + + def test_non_windows_retains_full_mime_guessing(self): + """Non-Windows must NOT replace the system MIME database.""" + import api.app + + self._simulate_cold_start() + with patch("sys.platform", "linux"): + importlib.reload(api.app) + + self.assertEqual( + mimetypes.guess_type("index.html")[0], + "text/html", + "HTML must still be detected on non-Windows", + ) + self.assertEqual( + mimetypes.guess_type("test.pdf")[0], + "application/pdf", + "PDF must still be detected on non-Windows", + ) + + def test_types_map_consistent_with_db(self): + """types_map and _db.types_map[True] must be the same object.""" + import api.app + + self._simulate_cold_start() + with patch("sys.platform", "win32"): + importlib.reload(api.app) + + self.assertIs( + mimetypes.types_map, + mimetypes._db.types_map[True], + "types_map must reference _db internals (no split-brain)", + ) + + def test_frontend_mime_types_registered_after_init(self): + """_register_frontend_asset_mime_types must work after cold-start init.""" + import api.app + + self._simulate_cold_start() + with patch("sys.platform", "win32"): + importlib.reload(api.app) + + api.app._register_frontend_asset_mime_types() + + self.assertEqual( + mimetypes.guess_type("app.js")[0], "text/javascript" + ) + self.assertEqual( + mimetypes.guess_type("style.css")[0], "text/css" + ) + + +if __name__ == "__main__": + unittest.main()