Resource Cleanup and Lifecycle Management#
Docling manages several classes of resources that require explicit teardown: VLM engine instances held in model-stage objects, input document backends (PDF, DOCX, etc.), and temporary directories created during DOCX/DrawingML rendering. Failures in any of these can leave GPU memory, file handles, or disk space unreleased — or produce spurious errors during Python interpreter shutdown.
There are four distinct cleanup patterns in the codebase:
- VLM engine
__del__+cleanup()hook — used by model stages BaseVlmEngine.cleanup()delegation — used by theAutoInlineVlmEnginewrapper_execute_pipeline/_unload_input_document— converter-level backend teardown on init failuretry/finallyaroundmkdtemp()— temp dir cleanup in DOCX rendering
VLM Engine Destructors#
Both page-level and enrichment-level VLM model stages define __del__ methods that call engine.cleanup().
-
VlmConvertModel.__del__callsself.engine.cleanup()and useshasattr(self, "engine")to guard against incomplete construction. Exceptions are caught, but the handler logs via the module-level_logwithout aNonecheck — leaving this method vulnerable to interpreter-shutdown crashes (see below). -
CodeFormulaVlmModel.__del__uses the same pattern but adds an explicitif _log is not Noneguard before calling_log.warning(). This guard was added by PR #3534 to fix a crash where Python's interpreter-shutdown sequence sets module globals (including loggers) toNonebefore__del__runs — causingAttributeError: 'NoneType' object has no attribute 'warning'instead of a clean teardown.
AutoInlineVlmEngine.cleanup() delegates cleanup to the concrete engine it wraps (self.actual_engine.cleanup()) and sets self.actual_engine = None afterward, preventing double-cleanup.
The base contract lives in BaseVlmEngine.cleanup(), which is an optional hook that concrete engines override to release GPU memory or API connections .
Pattern to follow: Always guard
_logcalls in__del__withif _log is not None. Usehasattr(self, "engine")orif self.engine is not Noneto handle incomplete construction. Do not rely on__del__as the sole cleanup path — it is non-deterministic.
Input Backend Cleanup on Pipeline Failure#
When a pipeline fails to initialize (e.g., an OCR model raises during setup), the InputDocument backend's unload() method was previously never called. On Windows, this left PDF file handles open and blocked temporary directory deletion, obscuring the original exception.
PR #3715 fixed this in DocumentConverter._execute_pipeline:
- A
pipeline_startedboolean flag tracks whether pipeline execution actually began. - A
finallyblock callsself._unload_input_document(in_doc)only whenpipeline_startedisFalse— ensuring the pipeline retains ownership of cleanup when it does start. - Invalid input documents always trigger unload via a separate
try/finally.
The helper _unload_input_document safely uses getattr(in_doc, "_backend", None) to avoid AttributeError when the backend was never attached.
AbstractDocumentBackend.unload() (the contract all backends implement) closes open BytesIO streams and nulls path_or_stream .
DrawingML Temporary Directory Cleanup#
get_pil_from_dml_docx() in docling/backend/docx/drawingml/utils.py converts embedded DrawingML to PNG by writing a temporary DOCX, converting to PDF via LibreOffice, and rendering with pypdfium2. All three steps can raise. PR #3797 wrapped the entire rendering block in try/finally so that shutil.rmtree(temp_dir, ignore_errors=True) runs even if any step fails.
The ignore_errors=True flag ensures cleanup does not itself raise if the directory has already been removed or is locked. The same pattern is used by _isolated_libreoffice_profile and convert_to_modern_format in the same file .
Page-Level Resource Release#
The pipeline also frees per-page resources after the assemble stage. _release_page_resources() frees the _image_cache and unloads the page backend. It sets keep_backend = True when any downstream enrichment stage is active .
Key Source Files#
| File | Role |
|---|---|
docling/models/stages/vlm_convert/vlm_convert_model.py | VlmConvertModel.__del__ — VLM engine teardown |
docling/models/stages/code_formula/code_formula_vlm_model.py | CodeFormulaVlmModel.__del__ — interpreter-shutdown-safe teardown |
docling/models/inference_engines/vlm/auto_inline_engine.py | AutoInlineVlmEngine.cleanup() — delegation to concrete engine |
docling/document_converter.py | _execute_pipeline / _unload_input_document — backend cleanup on init failure |
docling/backend/docx/drawingml/utils.py | get_pil_from_dml_docx() — temp dir cleanup via try/finally |