Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

### Fixes

- **Raise a clear error instead of silently returning no elements when `strategy="fast"` can't be honored**: `partition_pdf()` skips pdfminer text extraction for PDFs flagged by `is_pdf_too_complex()` as mostly vector graphics, since pdfminer is slow and unreliable on them. For `strategy="auto"` this correctly falls back to another strategy, but an explicitly-requested `strategy="fast"` had no fallback available and silently returned `[]` with no indication anything went wrong. It now raises a `ValueError` explaining why and suggesting `"hi_res"` or `"auto"` instead. `strategy="auto"` is unaffected and continues to fall back gracefully.

- **Stop the `GLOBAL_WORKING_DIR` tests from disturbing other pytest-xdist workers**: test-only change, no library behavior changes. The two tests exercising `GLOBAL_WORKING_DIR_ENABLED` now redirect the working dir to a private `tmp_path` and restore `tempfile.tempdir` unconditionally, rather than moving the shared pgid-keyed directory aside mid-run and leaving the worker's `tempfile.tempdir` pointed at it. That shared path made `test_dockerfile` fail intermittently, with an unrelated test dying inside `tempfile`.

## 0.25.2
Expand Down
22 changes: 22 additions & 0 deletions test_unstructured/partition/pdf_image/test_pdf.py
Original file line number Diff line number Diff line change
Expand Up @@ -1767,6 +1767,28 @@ def test_is_pdf_too_complex_returns_false_for_normal_pdf():
assert not pdf.is_pdf_too_complex(filename=example_doc_path("pdf/layout-parser-paper.pdf"))


def test_partition_pdf_raises_for_explicit_fast_strategy_on_complex_pdf():
"""strategy="fast" cannot honor a PDF flagged as too complex for pdfminer text
extraction (see is_pdf_too_complex). Previously this silently returned an empty
element list; it should now raise a clear, actionable error instead. See #4260."""
filename = example_doc_path("pdf/layout-parser-paper.pdf")

with mock.patch.object(pdf, "is_pdf_too_complex", return_value=True):
with pytest.raises(ValueError, match="too complex"):
pdf.partition_pdf(filename=filename, strategy=PartitionStrategy.FAST)


def test_partition_pdf_auto_strategy_still_falls_back_on_complex_pdf():
"""strategy="auto" should keep degrading gracefully (no exception) when a PDF is
flagged as too complex -- only an explicitly-requested "fast" strategy should raise."""
filename = example_doc_path("pdf/layout-parser-paper.pdf")

with mock.patch.object(pdf, "is_pdf_too_complex", return_value=True):
elements = pdf.partition_pdf(filename=filename, strategy=PartitionStrategy.AUTO)

assert len(elements) > 0


def test_document_to_element_list_omits_coord_system_when_coord_points_absent():
# TODO (yao): investigate why we need this test. The LayoutElement definition suggests bbox
# can't be None and it has to be a Rectangle object that has x1, y1, x2, y2 attributes.
Expand Down
25 changes: 16 additions & 9 deletions unstructured/partition/pdf.py
Original file line number Diff line number Diff line change
Expand Up @@ -300,14 +300,21 @@ def partition_pdf_or_image(
pdf_text_extractable = False

if not is_image:
try:
if is_pdf_too_complex(filename=filename, file=file):
logger.info(
"PDF is too complex for text extraction based on heuristic checks. "
"Falling back to hi_res strategy without text extraction."
if is_pdf_too_complex(filename=filename, file=file):
if strategy == PartitionStrategy.FAST:
raise ValueError(
"PDF is too complex for text extraction based on heuristic checks "
"(high ratio of vector graphics to text), so the fast strategy "
"cannot reliably extract text from it. Use strategy='hi_res' or "
"strategy='auto' instead."
)
logger.info(
"PDF is too complex for text extraction based on heuristic checks. "
"Falling back to hi_res strategy without text extraction."
)

else:
else:
try:
extracted_elements = extractable_elements(
filename=filename,
file=spooled_to_bytes_io_if_needed(file),
Expand All @@ -323,9 +330,9 @@ def partition_pdf_or_image(
for page_elements in extracted_elements
for el in page_elements
)
except Exception as e:
logger.debug(e)
logger.info("PDF text extraction failed, skip text extraction...")
except Exception as e:
logger.debug(e)
logger.info("PDF text extraction failed, skip text extraction...")

strategy = determine_pdf_or_image_strategy(
strategy,
Expand Down