feat: 添加 doc/xls/ppt 旧格式文档支持

- 新增 DocReader,支持 markitdown 和 pypandoc-binary 解析器
- 新增 XlsReader,支持 unstructured、markitdown 和 pandas+xlrd 解析器
- 新增 PptReader,支持 markitdown 解析器
- 添加 olefile 依赖用于验证 OLE2 格式
- 更新 config.py 添加 doc/xls/ppt 依赖配置
- 更新 --advice 支持 doc/xls/ppt 格式
- 添加相应的测试用例
- 同步 specs 到主目录
This commit is contained in:
2026-03-10 23:09:13 +08:00
parent e53e64d386
commit cf10458dd6
32 changed files with 756 additions and 5 deletions

View File

@@ -1,6 +1,6 @@
# lyxy-document # lyxy-document
统一文档解析工具 - 将 DOCX、XLSX、PPTX、PDF、HTML/URL 转换为 Markdown 统一文档解析工具 - 将 DOC、DOCX、XLS、XLSX、PPT、PPTX、PDF、HTML/URL 转换为 Markdown
## 项目概述 ## 项目概述
@@ -25,8 +25,11 @@ scripts/
│ └── exceptions.py # 异常定义 │ └── exceptions.py # 异常定义
├── readers/ # 格式阅读器 ├── readers/ # 格式阅读器
│ ├── base.py # Reader 基类 │ ├── base.py # Reader 基类
│ ├── doc/ # DOC 解析器(旧格式)
│ ├── docx/ # DOCX 解析器 │ ├── docx/ # DOCX 解析器
│ ├── xls/ # XLS 解析器(旧格式)
│ ├── xlsx/ # XLSX 解析器 │ ├── xlsx/ # XLSX 解析器
│ ├── ppt/ # PPT 解析器(旧格式)
│ ├── pptx/ # PPTX 解析器 │ ├── pptx/ # PPTX 解析器
│ ├── pdf/ # PDF 解析器 │ ├── pdf/ # PDF 解析器
│ └── html/ # HTML/URL 解析器 │ └── html/ # HTML/URL 解析器

View File

@@ -0,0 +1,53 @@
## Purpose
DOC 文档解析能力,支持解析 Microsoft Word 97-2003 旧格式文档。
## Requirements
### Requirement: DOC 文档解析
系统 SHALL 支持解析 .doc 格式文档,按优先级尝试多个解析器。
#### Scenario: 按优先级尝试解析器
- **WHEN** 解析 DOC 文档
- **THEN** 系统按 markitdown → pypandoc-binary 的顺序尝试
#### Scenario: 成功解析
- **WHEN** 任一解析器成功
- **THEN** 系统返回解析结果
#### Scenario: 所有解析器失败
- **WHEN** 所有解析器均失败
- **THEN** 系统返回失败列表并退出非零状态码
### Requirement: markitdown 解析器
系统 SHALL 支持使用 markitdown 库解析 DOC。
#### Scenario: markitdown 解析成功
- **WHEN** markitdown 库可用且文档有效
- **THEN** 系统返回 Markdown 内容
#### Scenario: markitdown 库未安装
- **WHEN** markitdown 库未安装
- **THEN** 系统尝试下一个解析器
### Requirement: pypandoc-binary 解析器
系统 SHALL 支持使用 pypandoc-binary 库解析 DOC。
#### Scenario: pypandoc 解析成功
- **WHEN** pypandoc-binary 库可用且文档有效
- **THEN** 系统返回 Markdown 内容
#### Scenario: pypandoc-binary 库未安装
- **WHEN** pypandoc-binary 库未安装
- **THEN** 系统尝试下一个解析器
### Requirement: 每个解析器独立文件
系统 SHALL 将每个解析器实现为独立的单文件模块。
#### Scenario: markitdown 解析器在独立文件
- **WHEN** 使用 markitdown 解析器
- **THEN** 从 readers/doc/markitdown.py 导入
#### Scenario: pypandoc 解析器在独立文件
- **WHEN** 使用 pypandoc 解析器
- **THEN** 从 readers/doc/pypandoc.py 导入

View File

@@ -0,0 +1,38 @@
## Purpose
PPT 文档解析能力,支持解析 Microsoft PowerPoint 97-2003 旧格式文档。
## Requirements
### Requirement: PPT 文档解析
系统 SHALL 支持解析 .ppt 格式文档,按优先级尝试多个解析器。
#### Scenario: 按优先级尝试解析器
- **WHEN** 解析 PPT 文档
- **THEN** 系统按 markitdown 的顺序尝试
#### Scenario: 成功解析
- **WHEN** 任一解析器成功
- **THEN** 系统返回解析结果
#### Scenario: 所有解析器失败
- **WHEN** 所有解析器均失败
- **THEN** 系统返回失败列表并退出非零状态码
### Requirement: markitdown 解析器
系统 SHALL 支持使用 markitdown 库解析 PPT。
#### Scenario: markitdown 解析成功
- **WHEN** markitdown 库可用且文档有效
- **THEN** 系统返回 Markdown 内容
#### Scenario: markitdown 库未安装
- **WHEN** markitdown 库未安装
- **THEN** 系统返回失败信息
### Requirement: 每个解析器独立文件
系统 SHALL 将每个解析器实现为独立的单文件模块。
#### Scenario: markitdown 解析器在独立文件
- **WHEN** 使用 markitdown 解析器
- **THEN** 从 readers/ppt/markitdown.py 导入

View File

@@ -0,0 +1,68 @@
## Purpose
XLS 文档解析能力,支持解析 Microsoft Excel 97-2003 旧格式文档。
## Requirements
### Requirement: XLS 文档解析
系统 SHALL 支持解析 .xls 格式文档,按优先级尝试多个解析器。
#### Scenario: 按优先级尝试解析器
- **WHEN** 解析 XLS 文档
- **THEN** 系统按 unstructured → markitdown → pandas+xlrd 的顺序尝试
#### Scenario: 成功解析
- **WHEN** 任一解析器成功
- **THEN** 系统返回解析结果
#### Scenario: 所有解析器失败
- **WHEN** 所有解析器均失败
- **THEN** 系统返回失败列表并退出非零状态码
### Requirement: unstructured 解析器
系统 SHALL 支持使用 unstructured 库解析 XLS。
#### Scenario: unstructured 解析成功
- **WHEN** unstructured 库可用且文档有效
- **THEN** 系统返回 Markdown 内容
#### Scenario: unstructured 库未安装
- **WHEN** unstructured 库未安装
- **THEN** 系统尝试下一个解析器
### Requirement: markitdown 解析器
系统 SHALL 支持使用 markitdown 库解析 XLS。
#### Scenario: markitdown 解析成功
- **WHEN** markitdown 库可用且文档有效
- **THEN** 系统返回 Markdown 内容
#### Scenario: markitdown 库未安装
- **WHEN** markitdown 库未安装
- **THEN** 系统尝试下一个解析器
### Requirement: pandas+xlrd 解析器
系统 SHALL 支持使用 pandas + xlrd 库解析 XLS。
#### Scenario: pandas+xlrd 解析成功
- **WHEN** pandas 和 xlrd 库可用且文档有效
- **THEN** 系统返回 Markdown 格式的表格内容,包含所有工作表
#### Scenario: pandas 或 xlrd 库未安装
- **WHEN** pandas 或 xlrd 库未安装
- **THEN** 系统尝试下一个解析器
### Requirement: 每个解析器独立文件
系统 SHALL 将每个解析器实现为独立的单文件模块。
#### Scenario: unstructured 解析器在独立文件
- **WHEN** 使用 unstructured 解析器
- **THEN** 从 readers/xls/unstructured.py 导入
#### Scenario: markitdown 解析器在独立文件
- **WHEN** 使用 markitdown 解析器
- **THEN** 从 readers/xls/markitdown.py 导入
#### Scenario: pandas 解析器在独立文件
- **WHEN** 使用 pandas 解析器
- **THEN** 从 readers/xls/pandas.py 导入

View File

@@ -97,5 +97,37 @@ DEPENDENCIES = {
"selenium" "selenium"
] ]
} }
},
"doc": {
"default": {
"python": None,
"dependencies": [
"markitdown[docx]",
"pypandoc-binary",
"olefile"
]
}
},
"xls": {
"default": {
"python": None,
"dependencies": [
"unstructured[xlsx]",
"markitdown[xls]",
"pandas",
"tabulate",
"xlrd",
"olefile"
]
}
},
"ppt": {
"default": {
"python": None,
"dependencies": [
"markitdown[pptx]",
"olefile"
]
}
} }
} }

View File

@@ -12,6 +12,9 @@ from readers import (
XlsxReader, XlsxReader,
PptxReader, PptxReader,
HtmlReader, HtmlReader,
DocReader,
XlsReader,
PptReader,
) )
@@ -22,6 +25,9 @@ _READER_KEY_MAP: Dict[Type[BaseReader], str] = {
XlsxReader: "xlsx", XlsxReader: "xlsx",
PptxReader: "pptx", PptxReader: "pptx",
HtmlReader: "html", HtmlReader: "html",
DocReader: "doc",
XlsReader: "xls",
PptReader: "ppt",
} }

View File

@@ -1,5 +1,5 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
"""文档解析器命令行交互模块,提供命令行接口。支持 DOCX、PPTX、XLSX、PDF、HTML 和 URL。""" """文档解析器命令行交互模块,提供命令行接口。支持 DOC、DOCX、XLS、XLSX、PPT、PPTX、PDF、HTML 和 URL。"""
import argparse import argparse
import logging import logging
@@ -39,10 +39,10 @@ from readers import READERS
def main() -> None: def main() -> None:
parser = argparse.ArgumentParser( parser = argparse.ArgumentParser(
description="将 DOCX、PPTX、XLSX、PDF、HTML 文件或 URL 解析为 Markdown" description="将 DOC、DOCX、XLS、XLSX、PPT、PPTX、PDF、HTML 文件或 URL 解析为 Markdown"
) )
parser.add_argument("input_path", help="DOCX、PPTX、XLSX、PDF、HTML 文件或 URL") parser.add_argument("input_path", help="DOC、DOCX、XLS、XLSX、PPT、PPTX、PDF、HTML 文件或 URL")
parser.add_argument( parser.add_argument(
"-a", "-a",

View File

@@ -6,6 +6,9 @@ from .xlsx import XlsxReader
from .pptx import PptxReader from .pptx import PptxReader
from .pdf import PdfReader from .pdf import PdfReader
from .html import HtmlReader from .html import HtmlReader
from .doc import DocReader
from .xls import XlsReader
from .ppt import PptReader
READERS = [ READERS = [
DocxReader, DocxReader,
@@ -13,6 +16,9 @@ READERS = [
PptxReader, PptxReader,
PdfReader, PdfReader,
HtmlReader, HtmlReader,
DocReader,
XlsReader,
PptReader,
] ]
__all__ = [ __all__ = [
@@ -22,5 +28,8 @@ __all__ = [
"PptxReader", "PptxReader",
"PdfReader", "PdfReader",
"HtmlReader", "HtmlReader",
"DocReader",
"XlsReader",
"PptReader",
"READERS", "READERS",
] ]

View File

@@ -0,0 +1,48 @@
"""DOC 文件阅读器,支持多种解析方法。"""
import os
from typing import List, Optional, Tuple
from readers.base import BaseReader
from utils import is_valid_doc
from . import markitdown
from . import pypandoc
PARSERS = [
("MarkItDown", markitdown.parse),
("pypandoc-binary", pypandoc.parse),
]
class DocReader(BaseReader):
"""DOC 文件阅读器"""
def supports(self, file_path: str) -> bool:
return file_path.lower().endswith('.doc')
def parse(self, file_path: str) -> Tuple[Optional[str], List[str]]:
failures = []
# 检查文件是否存在
if not os.path.exists(file_path):
return None, ["文件不存在"]
# 验证文件格式
if not is_valid_doc(file_path):
return None, ["不是有效的 DOC 文件"]
content = None
for parser_name, parser_func in PARSERS:
try:
content, error = parser_func(file_path)
if content is not None:
return content, failures
else:
failures.append(f"- {parser_name}: {error}")
except Exception as e:
failures.append(f"- {parser_name}: [意外异常] {type(e).__name__}: {str(e)}")
return None, failures

View File

@@ -0,0 +1,10 @@
"""使用 MarkItDown 库解析 DOC 文件"""
from typing import Optional, Tuple
from readers._utils import parse_via_markitdown
def parse(file_path: str) -> Tuple[Optional[str], Optional[str]]:
"""使用 MarkItDown 库解析 DOC 文件"""
return parse_via_markitdown(file_path)

View File

@@ -0,0 +1,29 @@
"""使用 pypandoc-binary 库解析 DOC 文件"""
from typing import Optional, Tuple
def parse(file_path: str) -> Tuple[Optional[str], Optional[str]]:
"""使用 pypandoc-binary 库解析 DOC 文件"""
try:
import pypandoc
except ImportError:
return None, "pypandoc-binary 库未安装"
try:
content = pypandoc.convert_file(
source_file=file_path,
to="md",
format="doc",
outputfile=None,
extra_args=["--wrap=none"],
)
except OSError as exc:
return None, f"pypandoc-binary 缺少 Pandoc 可执行文件: {exc}"
except RuntimeError as exc:
return None, f"pypandoc-binary 解析失败: {exc}"
content = content.strip()
if not content:
return None, "文档为空"
return content, None

View File

@@ -0,0 +1,46 @@
"""PPT 文件阅读器,支持多种解析方法。"""
import os
from typing import List, Optional, Tuple
from readers.base import BaseReader
from utils import is_valid_ppt
from . import markitdown
PARSERS = [
("MarkItDown", markitdown.parse),
]
class PptReader(BaseReader):
"""PPT 文件阅读器"""
def supports(self, file_path: str) -> bool:
return file_path.lower().endswith('.ppt')
def parse(self, file_path: str) -> Tuple[Optional[str], List[str]]:
failures = []
# 检查文件是否存在
if not os.path.exists(file_path):
return None, ["文件不存在"]
# 验证文件格式
if not is_valid_ppt(file_path):
return None, ["不是有效的 PPT 文件"]
content = None
for parser_name, parser_func in PARSERS:
try:
content, error = parser_func(file_path)
if content is not None:
return content, failures
else:
failures.append(f"- {parser_name}: {error}")
except Exception as e:
failures.append(f"- {parser_name}: [意外异常] {type(e).__name__}: {str(e)}")
return None, failures

View File

@@ -0,0 +1,10 @@
"""使用 MarkItDown 库解析 PPT 文件"""
from typing import Optional, Tuple
from readers._utils import parse_via_markitdown
def parse(file_path: str) -> Tuple[Optional[str], Optional[str]]:
"""使用 MarkItDown 库解析 PPT 文件"""
return parse_via_markitdown(file_path)

View File

@@ -0,0 +1,50 @@
"""XLS 文件阅读器,支持多种解析方法。"""
import os
from typing import List, Optional, Tuple
from readers.base import BaseReader
from utils import is_valid_xls
from . import unstructured
from . import markitdown
from . import pandas
PARSERS = [
("unstructured", unstructured.parse),
("MarkItDown", markitdown.parse),
("pandas+xlrd", pandas.parse),
]
class XlsReader(BaseReader):
"""XLS 文件阅读器"""
def supports(self, file_path: str) -> bool:
return file_path.lower().endswith('.xls')
def parse(self, file_path: str) -> Tuple[Optional[str], List[str]]:
failures = []
# 检查文件是否存在
if not os.path.exists(file_path):
return None, ["文件不存在"]
# 验证文件格式
if not is_valid_xls(file_path):
return None, ["不是有效的 XLS 文件"]
content = None
for parser_name, parser_func in PARSERS:
try:
content, error = parser_func(file_path)
if content is not None:
return content, failures
else:
failures.append(f"- {parser_name}: {error}")
except Exception as e:
failures.append(f"- {parser_name}: [意外异常] {type(e).__name__}: {str(e)}")
return None, failures

View File

@@ -0,0 +1,10 @@
"""使用 MarkItDown 库解析 XLS 文件"""
from typing import Optional, Tuple
from readers._utils import parse_via_markitdown
def parse(file_path: str) -> Tuple[Optional[str], Optional[str]]:
"""使用 MarkItDown 库解析 XLS 文件"""
return parse_via_markitdown(file_path)

View File

@@ -0,0 +1,41 @@
"""使用 pandas+xlrd 库解析 XLS 文件"""
from typing import Optional, Tuple
def parse(file_path: str) -> Tuple[Optional[str], Optional[str]]:
"""使用 pandas+xlrd 库解析 XLS 文件"""
try:
import pandas as pd
from tabulate import tabulate
except ImportError as e:
if "pandas" in str(e):
missing_lib = "pandas"
elif "xlrd" in str(e):
missing_lib = "xlrd"
else:
missing_lib = "tabulate"
return None, f"{missing_lib} 库未安装"
try:
sheets = pd.read_excel(file_path, sheet_name=None, engine="xlrd")
markdown_parts = []
for sheet_name, df in sheets.items():
if len(df) == 0:
markdown_parts.append(f"## {sheet_name}\n\n*工作表为空*")
continue
table_md = tabulate(
df, headers="keys", tablefmt="pipe", showindex=True, missingval=""
)
markdown_parts.append(f"## {sheet_name}\n\n{table_md}")
if not markdown_parts:
return None, "Excel 文件为空"
markdown_content = "# Excel数据转换结果\n\n" + "\n\n".join(markdown_parts)
return markdown_content, None
except Exception as e:
return None, f"pandas 解析失败: {str(e)}"

View File

@@ -0,0 +1,22 @@
"""使用 unstructured 库解析 XLS 文件"""
from typing import Optional, Tuple
from readers._utils import convert_unstructured_to_markdown
def parse(file_path: str) -> Tuple[Optional[str], Optional[str]]:
"""使用 unstructured 库解析 XLS 文件"""
try:
from unstructured.partition.xlsx import partition_xlsx
except ImportError:
return None, "unstructured 库未安装"
try:
elements = partition_xlsx(filename=file_path, infer_table_structure=True)
content = convert_unstructured_to_markdown(elements)
if not content.strip():
return None, "文档为空"
return content, None
except Exception as e:
return None, f"unstructured 解析失败: {str(e)}"

View File

@@ -5,6 +5,9 @@ from .file_detection import (
is_valid_pptx, is_valid_pptx,
is_valid_xlsx, is_valid_xlsx,
is_valid_pdf, is_valid_pdf,
is_valid_doc,
is_valid_xls,
is_valid_ppt,
is_html_file, is_html_file,
is_url, is_url,
detect_file_type, detect_file_type,
@@ -15,6 +18,9 @@ __all__ = [
"is_valid_pptx", "is_valid_pptx",
"is_valid_xlsx", "is_valid_xlsx",
"is_valid_pdf", "is_valid_pdf",
"is_valid_doc",
"is_valid_xls",
"is_valid_ppt",
"is_html_file", "is_html_file",
"is_url", "is_url",
"detect_file_type", "detect_file_type",

View File

@@ -5,6 +5,19 @@ import zipfile
from typing import List, Optional from typing import List, Optional
def _is_valid_ole(file_path: str) -> bool:
"""验证 OLE2 格式文件DOC/XLS/PPT"""
try:
import olefile
except ImportError:
# 如果 olefile 未安装,就不做严格验证
return True
try:
return olefile.isOleFile(file_path)
except Exception:
return False
def _is_valid_ooxml(file_path: str, required_files: List[str]) -> bool: def _is_valid_ooxml(file_path: str, required_files: List[str]) -> bool:
"""验证 OOXML 格式文件DOCX/PPTX/XLSX""" """验证 OOXML 格式文件DOCX/PPTX/XLSX"""
try: try:
@@ -35,6 +48,21 @@ def is_valid_xlsx(file_path: str) -> bool:
return _is_valid_ooxml(file_path, _XLSX_REQUIRED) return _is_valid_ooxml(file_path, _XLSX_REQUIRED)
def is_valid_doc(file_path: str) -> bool:
"""验证文件是否为有效的 DOC 格式"""
return _is_valid_ole(file_path)
def is_valid_xls(file_path: str) -> bool:
"""验证文件是否为有效的 XLS 格式"""
return _is_valid_ole(file_path)
def is_valid_ppt(file_path: str) -> bool:
"""验证文件是否为有效的 PPT 格式"""
return _is_valid_ole(file_path)
def is_valid_pdf(file_path: str) -> bool: def is_valid_pdf(file_path: str) -> bool:
"""验证文件是否为有效的 PDF 格式""" """验证文件是否为有效的 PDF 格式"""
try: try:
@@ -61,13 +89,18 @@ _FILE_TYPE_VALIDATORS = {
".pptx": is_valid_pptx, ".pptx": is_valid_pptx,
".xlsx": is_valid_xlsx, ".xlsx": is_valid_xlsx,
".pdf": is_valid_pdf, ".pdf": is_valid_pdf,
".doc": is_valid_doc,
".xls": is_valid_xls,
".ppt": is_valid_ppt,
} }
def detect_file_type(file_path: str) -> Optional[str]: def detect_file_type(file_path: str) -> Optional[str]:
"""检测文件类型,返回 'docx''pptx''xlsx''pdf'""" """检测文件类型,返回 'docx''pptx''xlsx''pdf''doc''xls''ppt'"""
ext = os.path.splitext(file_path)[1].lower() ext = os.path.splitext(file_path)[1].lower()
validator = _FILE_TYPE_VALIDATORS.get(ext) validator = _FILE_TYPE_VALIDATORS.get(ext)
if validator and validator(file_path): if validator and validator(file_path):
return ext.lstrip(".") return ext.lstrip(".")
return None return None

View File

@@ -38,6 +38,27 @@ class TestCLIAdviceOption:
output = stdout + stderr output = stdout + stderr
assert "无法识别" in output or "错误" in output assert "无法识别" in output or "错误" in output
def test_advice_option_doc(self, cli_runner):
"""测试 --advice 选项对 DOC 文件。"""
stdout, stderr, exit_code = cli_runner(["test.doc", "--advice"])
assert exit_code == 0
assert "文件类型: DOC" in stdout
def test_advice_option_xls(self, cli_runner):
"""测试 --advice 选项对 XLS 文件。"""
stdout, stderr, exit_code = cli_runner(["test.xls", "--advice"])
assert exit_code == 0
assert "文件类型: XLS" in stdout
def test_advice_option_ppt(self, cli_runner):
"""测试 --advice 选项对 PPT 文件。"""
stdout, stderr, exit_code = cli_runner(["test.ppt", "--advice"])
assert exit_code == 0
assert "文件类型: PPT" in stdout
class TestCLIDefaultOutput: class TestCLIDefaultOutput:
"""测试 CLI 默认输出功能。""" """测试 CLI 默认输出功能。"""

View File

@@ -0,0 +1 @@
"""测试 DocReader 模块。"""

View File

@@ -0,0 +1,21 @@
"""测试所有 DOC Readers 的一致性。"""
import pytest
from readers.doc import markitdown, pypandoc
class TestDocReadersConsistency:
"""验证 DOC Readers 模块结构正确。"""
def test_parsers_importable(self):
"""测试所有 parser 模块可以正确导入。"""
# 验证模块导入成功
assert markitdown is not None
assert pypandoc is not None
assert hasattr(markitdown, 'parse')
assert hasattr(pypandoc, 'parse')
def test_parser_functions_callable(self):
"""测试 parse 函数是可调用的。"""
assert callable(markitdown.parse)
assert callable(pypandoc.parse)

View File

@@ -0,0 +1,25 @@
"""测试 MarkItDown DOC Reader 的解析功能。"""
import pytest
import os
from readers.doc import markitdown
class TestMarkitdownDocReaderParse:
"""测试 MarkItDown DOC Reader 的 parse 方法。"""
def test_module_importable(self):
"""测试模块可以正确导入。"""
assert markitdown is not None
assert hasattr(markitdown, 'parse')
assert callable(markitdown.parse)
def test_file_not_exists(self, tmp_path):
"""测试文件不存在的情况。"""
non_existent_file = str(tmp_path / "non_existent.doc")
content, error = markitdown.parse(non_existent_file)
# 验证返回 None 和错误信息
assert content is None
assert error is not None

View File

@@ -0,0 +1,25 @@
"""测试 pypandoc DOC Reader 的解析功能。"""
import pytest
import os
from readers.doc import pypandoc
class TestPypandocDocReaderParse:
"""测试 pypandoc DOC Reader 的 parse 方法。"""
def test_module_importable(self):
"""测试模块可以正确导入。"""
assert pypandoc is not None
assert hasattr(pypandoc, 'parse')
assert callable(pypandoc.parse)
def test_file_not_exists(self, tmp_path):
"""测试文件不存在的情况。"""
non_existent_file = str(tmp_path / "non_existent.doc")
content, error = pypandoc.parse(non_existent_file)
# 验证返回 None 和错误信息
assert content is None
assert error is not None

View File

@@ -0,0 +1 @@
"""测试 PptReader 模块。"""

View File

@@ -0,0 +1,18 @@
"""测试所有 PPT Readers 的一致性。"""
import pytest
from readers.ppt import markitdown
class TestPptReadersConsistency:
"""验证 PPT Readers 模块结构正确。"""
def test_parsers_importable(self):
"""测试所有 parser 模块可以正确导入。"""
# 验证模块导入成功
assert markitdown is not None
assert hasattr(markitdown, 'parse')
def test_parser_functions_callable(self):
"""测试 parse 函数是可调用的。"""
assert callable(markitdown.parse)

View File

@@ -0,0 +1,25 @@
"""测试 MarkItDown PPT Reader 的解析功能。"""
import pytest
import os
from readers.ppt import markitdown
class TestMarkitdownPptReaderParse:
"""测试 MarkItDown PPT Reader 的 parse 方法。"""
def test_module_importable(self):
"""测试模块可以正确导入。"""
assert markitdown is not None
assert hasattr(markitdown, 'parse')
assert callable(markitdown.parse)
def test_file_not_exists(self, tmp_path):
"""测试文件不存在的情况。"""
non_existent_file = str(tmp_path / "non_existent.ppt")
content, error = markitdown.parse(non_existent_file)
# 验证返回 None 和错误信息
assert content is None
assert error is not None

View File

@@ -0,0 +1 @@
"""测试 XlsReader 模块。"""

View File

@@ -0,0 +1,24 @@
"""测试所有 XLS Readers 的一致性。"""
import pytest
from readers.xls import unstructured, markitdown, pandas
class TestXlsReadersConsistency:
"""验证 XLS Readers 模块结构正确。"""
def test_parsers_importable(self):
"""测试所有 parser 模块可以正确导入。"""
# 验证模块导入成功
assert unstructured is not None
assert markitdown is not None
assert pandas is not None
assert hasattr(unstructured, 'parse')
assert hasattr(markitdown, 'parse')
assert hasattr(pandas, 'parse')
def test_parser_functions_callable(self):
"""测试 parse 函数是可调用的。"""
assert callable(unstructured.parse)
assert callable(markitdown.parse)
assert callable(pandas.parse)

View File

@@ -0,0 +1,25 @@
"""测试 MarkItDown XLS Reader 的解析功能。"""
import pytest
import os
from readers.xls import markitdown
class TestMarkitdownXlsReaderParse:
"""测试 MarkItDown XLS Reader 的 parse 方法。"""
def test_module_importable(self):
"""测试模块可以正确导入。"""
assert markitdown is not None
assert hasattr(markitdown, 'parse')
assert callable(markitdown.parse)
def test_file_not_exists(self, tmp_path):
"""测试文件不存在的情况。"""
non_existent_file = str(tmp_path / "non_existent.xls")
content, error = markitdown.parse(non_existent_file)
# 验证返回 None 和错误信息
assert content is None
assert error is not None

View File

@@ -0,0 +1,25 @@
"""测试 pandas XLS Reader 的解析功能。"""
import pytest
import os
from readers.xls import pandas
class TestPandasXlsReaderParse:
"""测试 pandas XLS Reader 的 parse 方法。"""
def test_module_importable(self):
"""测试模块可以正确导入。"""
assert pandas is not None
assert hasattr(pandas, 'parse')
assert callable(pandas.parse)
def test_file_not_exists(self, tmp_path):
"""测试文件不存在的情况。"""
non_existent_file = str(tmp_path / "non_existent.xls")
content, error = pandas.parse(non_existent_file)
# 验证返回 None 和错误信息
assert content is None
assert error is not None

View File

@@ -0,0 +1,25 @@
"""测试 unstructured XLS Reader 的解析功能。"""
import pytest
import os
from readers.xls import unstructured
class TestUnstructuredXlsReaderParse:
"""测试 unstructured XLS Reader 的 parse 方法。"""
def test_module_importable(self):
"""测试模块可以正确导入。"""
assert unstructured is not None
assert hasattr(unstructured, 'parse')
assert callable(unstructured.parse)
def test_file_not_exists(self, tmp_path):
"""测试文件不存在的情况。"""
non_existent_file = str(tmp_path / "non_existent.xls")
content, error = unstructured.parse(non_existent_file)
# 验证返回 None 和错误信息
assert content is None
assert error is not None