From 224eaa82e6e9b2978e6b3c0ff9e433f20587a8e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=8E=E4=B8=96=E6=97=A0=E4=BA=89=E5=89=91=E5=AE=87?= =?UTF-8?q?=E5=AD=90?= <1530683628@qq.com> Date: Tue, 20 Oct 2026 23:38:49 +0800 Subject: [PATCH] fix(xls): avoid treating doc files as workbooks --- .../markitdown/converters/_xlsx_converter.py | 8 +++++-- packages/markitdown/tests/test_module_misc.py | 21 +++++++++++++++++++ packages/markitdown/tests/test_xlsx.py | 13 ++++++++++++ 3 files changed, 40 insertions(+), 2 deletions(-) diff --git a/packages/markitdown/src/markitdown/converters/_xlsx_converter.py b/packages/markitdown/src/markitdown/converters/_xlsx_converter.py index 9f794a3b77..80b143cd3d 100644 --- a/packages/markitdown/src/markitdown/converters/_xlsx_converter.py +++ b/packages/markitdown/src/markitdown/converters/_xlsx_converter.py @@ -208,8 +208,12 @@ def accepts( mimetype = (stream_info.mimetype or "").lower() extension = (stream_info.extension or "").lower() - if extension in ACCEPTED_XLS_FILE_EXTENSIONS: - return True + # Some upstream systems label every legacy Office Compound File as + # application/vnd.ms-excel, including Word .doc files. When the + # extension is known, use it as the discriminator so a .doc is not + # handed to xlrd. MIME type remains a fallback for extensionless input. + if extension: + return extension in ACCEPTED_XLS_FILE_EXTENSIONS for prefix in ACCEPTED_XLS_MIME_TYPE_PREFIXES: if mimetype.startswith(prefix): diff --git a/packages/markitdown/tests/test_module_misc.py b/packages/markitdown/tests/test_module_misc.py index 23d6f49aac..d482e5b39d 100644 --- a/packages/markitdown/tests/test_module_misc.py +++ b/packages/markitdown/tests/test_module_misc.py @@ -363,6 +363,27 @@ def test_exceptions() -> None: assert type(exc_info.value.attempts[0].converter).__name__ == "PptxConverter" +def test_legacy_doc_with_excel_mimetype_is_not_sent_to_xls_converter( + monkeypatch: pytest.MonkeyPatch, +) -> None: + markitdown = MarkItDown() + monkeypatch.setattr( + markitdown._magika, + "identify_stream", + MagicMock(return_value=SimpleNamespace(status="unknown")), + ) + + with pytest.raises(UnsupportedFormatException): + markitdown.convert_stream( + io.BytesIO(b"\x00\x01\x02\x03" * 64), + stream_info=StreamInfo( + filename="legacy.doc", + extension=".doc", + mimetype="application/vnd.ms-excel", + ), + ) + + def test_markitdown_llm_parameters() -> None: """Test that LLM parameters are correctly passed to the client.""" mock_client = MagicMock() diff --git a/packages/markitdown/tests/test_xlsx.py b/packages/markitdown/tests/test_xlsx.py index 9282622952..4bff32801a 100644 --- a/packages/markitdown/tests/test_xlsx.py +++ b/packages/markitdown/tests/test_xlsx.py @@ -446,6 +446,19 @@ def entity(name: str, content: bytes) -> bytes: render.assert_not_called() +def test_xls_acceptance_prefers_known_extension_over_mimetype() -> None: + converter = XlsConverter() + excel_mimetype = "application/vnd.ms-excel" + + assert converter.accepts( + io.BytesIO(), StreamInfo(extension=".xls", mimetype=excel_mimetype) + ) + assert converter.accepts(io.BytesIO(), StreamInfo(mimetype=excel_mimetype)) + assert not converter.accepts( + io.BytesIO(), StreamInfo(extension=".doc", mimetype=excel_mimetype) + ) + + def test_missing_dependencies_and_legacy_xls_stay_separate( monkeypatch: pytest.MonkeyPatch, ) -> None: