File manager that supports pickling by reopening a file object. Use PickleableFileManager for wrapping file-like objects that do not natively support pickling (e.g., netCDF4.Dataset and h5netcdf.File) in cases where a global cache is not desirable (e.g., for netCDF files opened from byt
| 342 | |
| 343 | |
| 344 | class PickleableFileManager(FileManager[T_File]): |
| 345 | """File manager that supports pickling by reopening a file object. |
| 346 | |
| 347 | Use PickleableFileManager for wrapping file-like objects that do not natively |
| 348 | support pickling (e.g., netCDF4.Dataset and h5netcdf.File) in cases where a |
| 349 | global cache is not desirable (e.g., for netCDF files opened from bytes in |
| 350 | memory, or from existing file objects). |
| 351 | """ |
| 352 | |
| 353 | def __init__( |
| 354 | self, |
| 355 | opener: Callable[..., T_File], |
| 356 | *args: Any, |
| 357 | mode: Any = _OMIT_MODE, |
| 358 | lock: Lock | None | Literal[False] = None, |
| 359 | kwargs: Mapping[str, Any] | None = None, |
| 360 | ): |
| 361 | kwargs = {} if kwargs is None else dict(kwargs) |
| 362 | self._opener = opener |
| 363 | self._args = args |
| 364 | self._mode = "a" if mode == "w" else mode |
| 365 | self._kwargs = kwargs |
| 366 | self._lock = lock |
| 367 | |
| 368 | # Note: No need for locking with PickleableFileManager, because all |
| 369 | # opening of files happens in the constructor. |
| 370 | if mode != _OMIT_MODE: |
| 371 | kwargs = kwargs | {"mode": mode} |
| 372 | self._file: T_File | None = opener(*args, **kwargs) |
| 373 | |
| 374 | @property |
| 375 | def _closed(self) -> bool: |
| 376 | # If opener() raised an error in the constructor, _file may not be set |
| 377 | return getattr(self, "_file", None) is None |
| 378 | |
| 379 | def _get_unclosed_file(self) -> T_File: |
| 380 | if self._closed: |
| 381 | raise RuntimeError("file is closed") |
| 382 | file = self._file |
| 383 | assert file is not None |
| 384 | return file |
| 385 | |
| 386 | def acquire(self, needs_lock: bool = True) -> T_File: |
| 387 | del needs_lock # unused |
| 388 | return self._get_unclosed_file() |
| 389 | |
| 390 | @contextmanager |
| 391 | def acquire_context(self, needs_lock: bool = True) -> Iterator[T_File]: |
| 392 | del needs_lock # unused |
| 393 | yield self._get_unclosed_file() |
| 394 | |
| 395 | def close(self, needs_lock: bool = True) -> None: |
| 396 | if not self._closed: |
| 397 | file = self._get_unclosed_file() |
| 398 | if needs_lock and self._lock: |
| 399 | with self._lock: |
| 400 | file.close() |
| 401 | else: |
no outgoing calls
searching dependent graphs…