perf(document): lazy-import heavy document parsers
Move pypdf, python-docx, openpyxl, and python-pptx imports from module level into the _extract_pdf / _extract_docx / _extract_xlsx / _extract_pptx functions that actually use them. These four libraries became core dependencies in v0.1.5.post2 (~25 MB combined) and were paying the import cost on every nanobot startup even when no document parsing was needed for the session. The module-level SUPPORTED_EXTENSIONS set and the extract_text() dispatch stay as-is; the "[error: <lib> not installed]" branches move from the old module-level None sentinels into the corresponding extractor's try/except ImportError block. Behavior for the error message and for successful parses is identical. All 20 tests in tests/test_document_parsing.py pass unchanged. Fixes #3422
This commit is contained in:
+19
-29
@@ -7,26 +7,6 @@ from loguru import logger
|
|||||||
|
|
||||||
from nanobot.utils.helpers import detect_image_mime
|
from nanobot.utils.helpers import detect_image_mime
|
||||||
|
|
||||||
try:
|
|
||||||
from pypdf import PdfReader
|
|
||||||
except ImportError:
|
|
||||||
PdfReader = None # type: ignore
|
|
||||||
|
|
||||||
try:
|
|
||||||
from docx import Document as DocxDocument
|
|
||||||
except ImportError:
|
|
||||||
DocxDocument = None # type: ignore
|
|
||||||
|
|
||||||
try:
|
|
||||||
from openpyxl import load_workbook
|
|
||||||
except ImportError:
|
|
||||||
load_workbook = None # type: ignore
|
|
||||||
|
|
||||||
try:
|
|
||||||
from pptx import Presentation as PptxPresentation
|
|
||||||
except ImportError:
|
|
||||||
PptxPresentation = None # type: ignore
|
|
||||||
|
|
||||||
|
|
||||||
# Supported file extensions for text extraction
|
# Supported file extensions for text extraction
|
||||||
SUPPORTED_EXTENSIONS: set[str] = {
|
SUPPORTED_EXTENSIONS: set[str] = {
|
||||||
@@ -78,22 +58,16 @@ def extract_text(path: Path) -> str | None:
|
|||||||
|
|
||||||
ext = path.suffix.lower()
|
ext = path.suffix.lower()
|
||||||
|
|
||||||
# Document formats
|
# Document formats -- each branch lazily imports its parser so that
|
||||||
|
# startup does not pay the ~25 MB cost of loading openpyxl /
|
||||||
|
# python-docx / python-pptx / pypdf up front (see issue #3422).
|
||||||
if ext == ".pdf":
|
if ext == ".pdf":
|
||||||
if PdfReader is None:
|
|
||||||
return "[error: pypdf not installed]"
|
|
||||||
return _extract_pdf(path)
|
return _extract_pdf(path)
|
||||||
elif ext == ".docx":
|
elif ext == ".docx":
|
||||||
if DocxDocument is None:
|
|
||||||
return "[error: python-docx not installed]"
|
|
||||||
return _extract_docx(path)
|
return _extract_docx(path)
|
||||||
elif ext == ".xlsx":
|
elif ext == ".xlsx":
|
||||||
if load_workbook is None:
|
|
||||||
return "[error: openpyxl not installed]"
|
|
||||||
return _extract_xlsx(path)
|
return _extract_xlsx(path)
|
||||||
elif ext == ".pptx":
|
elif ext == ".pptx":
|
||||||
if PptxPresentation is None:
|
|
||||||
return "[error: python-pptx not installed]"
|
|
||||||
return _extract_pptx(path)
|
return _extract_pptx(path)
|
||||||
elif _is_text_extension(ext):
|
elif _is_text_extension(ext):
|
||||||
return _extract_text_file(path)
|
return _extract_text_file(path)
|
||||||
@@ -107,6 +81,10 @@ def extract_text(path: Path) -> str | None:
|
|||||||
|
|
||||||
def _extract_pdf(path: Path) -> str:
|
def _extract_pdf(path: Path) -> str:
|
||||||
"""Extract text from PDF using pypdf."""
|
"""Extract text from PDF using pypdf."""
|
||||||
|
try:
|
||||||
|
from pypdf import PdfReader
|
||||||
|
except ImportError:
|
||||||
|
return "[error: pypdf not installed]"
|
||||||
try:
|
try:
|
||||||
reader = PdfReader(path)
|
reader = PdfReader(path)
|
||||||
pages: list[str] = []
|
pages: list[str] = []
|
||||||
@@ -121,6 +99,10 @@ def _extract_pdf(path: Path) -> str:
|
|||||||
|
|
||||||
def _extract_docx(path: Path) -> str:
|
def _extract_docx(path: Path) -> str:
|
||||||
"""Extract text from DOCX using python-docx."""
|
"""Extract text from DOCX using python-docx."""
|
||||||
|
try:
|
||||||
|
from docx import Document as DocxDocument
|
||||||
|
except ImportError:
|
||||||
|
return "[error: python-docx not installed]"
|
||||||
try:
|
try:
|
||||||
doc = DocxDocument(path)
|
doc = DocxDocument(path)
|
||||||
paragraphs: list[str] = [p.text for p in doc.paragraphs if p.text.strip()]
|
paragraphs: list[str] = [p.text for p in doc.paragraphs if p.text.strip()]
|
||||||
@@ -132,6 +114,10 @@ def _extract_docx(path: Path) -> str:
|
|||||||
|
|
||||||
def _extract_xlsx(path: Path) -> str:
|
def _extract_xlsx(path: Path) -> str:
|
||||||
"""Extract text from XLSX using openpyxl."""
|
"""Extract text from XLSX using openpyxl."""
|
||||||
|
try:
|
||||||
|
from openpyxl import load_workbook
|
||||||
|
except ImportError:
|
||||||
|
return "[error: openpyxl not installed]"
|
||||||
try:
|
try:
|
||||||
wb = load_workbook(path, read_only=True, data_only=True)
|
wb = load_workbook(path, read_only=True, data_only=True)
|
||||||
try:
|
try:
|
||||||
@@ -155,6 +141,10 @@ def _extract_xlsx(path: Path) -> str:
|
|||||||
|
|
||||||
def _extract_pptx(path: Path) -> str:
|
def _extract_pptx(path: Path) -> str:
|
||||||
"""Extract text from PPTX using python-pptx."""
|
"""Extract text from PPTX using python-pptx."""
|
||||||
|
try:
|
||||||
|
from pptx import Presentation as PptxPresentation
|
||||||
|
except ImportError:
|
||||||
|
return "[error: python-pptx not installed]"
|
||||||
try:
|
try:
|
||||||
prs = PptxPresentation(path)
|
prs = PptxPresentation(path)
|
||||||
slides: list[str] = []
|
slides: list[str] = []
|
||||||
|
|||||||
Reference in New Issue
Block a user