|
35 | 35 | from datetime import datetime
|
36 | 36 | from io import BytesIO, FileIO, UnsupportedOperation
|
37 | 37 | from pathlib import Path
|
| 38 | +from types import TracebackType |
38 | 39 | from typing import (
|
39 | 40 | Any,
|
40 | 41 | Callable,
|
|
45 | 46 | Mapping,
|
46 | 47 | Optional,
|
47 | 48 | Tuple,
|
| 49 | + Type, |
48 | 50 | Union,
|
49 | 51 | cast,
|
50 | 52 | )
|
@@ -278,6 +280,9 @@ class PdfReader:
|
278 | 280 | password: Decrypt PDF file at initialization. If the
|
279 | 281 | password is None, the file will not be decrypted.
|
280 | 282 | Defaults to ``None``
|
| 283 | +
|
| 284 | + Can also be instantiated as a contextmanager which will automatically close |
| 285 | + the underlying file pointer if passed via filenames. |
281 | 286 | """
|
282 | 287 |
|
283 | 288 | @property
|
@@ -312,8 +317,10 @@ def __init__(
|
312 | 317 | __name__,
|
313 | 318 | )
|
314 | 319 |
|
| 320 | + self._opened_automatically = False |
315 | 321 | if isinstance(stream, (str, Path)):
|
316 | 322 | stream = FileIO(stream, "rb")
|
| 323 | + self._opened_automatically = True |
317 | 324 | weakref.finalize(self, stream.close)
|
318 | 325 |
|
319 | 326 | self.read(stream)
|
@@ -349,6 +356,20 @@ def close(self) -> None:
|
349 | 356 | """Close the underlying file handle"""
|
350 | 357 | self.stream.close()
|
351 | 358 |
|
| 359 | + def __enter__(self) -> "PdfReader": |
| 360 | + """Use PdfReader as context manager""" |
| 361 | + return self |
| 362 | + |
| 363 | + def __exit__( |
| 364 | + self, |
| 365 | + exc_type: Optional[Type[BaseException]], |
| 366 | + exc: Optional[BaseException], |
| 367 | + traceback: Optional[TracebackType], |
| 368 | + ) -> None: |
| 369 | + """Close the underlying stream if owned by the PdfReader""" |
| 370 | + if self._opened_automatically: |
| 371 | + self.close() |
| 372 | + |
352 | 373 | @property
|
353 | 374 | def root_object(self) -> DictionaryObject:
|
354 | 375 | """Provide access to "/Root". standardized with PdfWriter."""
|
|
0 commit comments