From 15b938e08938902074618a413a4576d9e97ef802 Mon Sep 17 00:00:00 2001 From: David Manthey Date: Mon, 1 Apr 2024 08:54:57 -0400 Subject: [PATCH] Reduce bioformats keeping file handles open When bioformats tries to open a zip file, it opens the file once per internal file. If it fails to find a file that can be opened within the zip, it throws an error and leaves all of the file handles open. By dividing the reader into more steps, we can ask to open an empty file, which somehow releases the file handles. --- .pre-commit-config.yaml | 8 ++++---- CHANGELOG.md | 1 + pyproject.toml | 10 +++++----- .../large_image_source_bioformats/__init__.py | 16 +++++++++++++++- tox.ini | 6 +++--- 5 files changed, 28 insertions(+), 13 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 27f360648..f3e50f595 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -63,7 +63,7 @@ repos: - '--options' - './girder/girder_large_image/web_client/package.json' - repo: https://github.com/asottile/pyupgrade - rev: v3.15.0 + rev: v3.15.2 hooks: - id: pyupgrade args: @@ -71,7 +71,7 @@ repos: - --keep-percent-format - repo: https://github.com/astral-sh/ruff-pre-commit # Ruff version. - rev: v0.1.0 + rev: v0.3.4 hooks: - id: ruff args: [--fix, --exit-non-zero-on-fix] @@ -81,11 +81,11 @@ repos: hooks: - id: autopep8 - repo: https://github.com/PyCQA/flake8 - rev: 6.1.0 + rev: 7.0.0 hooks: - id: flake8 - repo: https://github.com/pycqa/isort - rev: 5.12.0 + rev: 5.13.2 hooks: - id: isort name: isort (python) diff --git a/CHANGELOG.md b/CHANGELOG.md index ed18b4e27..df9f04fcc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ - Add a dependency to the zarr source to read more compression types ([#1480](../../pull/1480)) - Guard fetching internal metadata on zarr sources that have less data ([#1481](../../pull/1481)) - Add a method to list registered extensions and mimetypes ([#1488](../../pull/1488)) +- Reduce bioformats keeping file handles open ([#1492](../../pull/1492)) ### Changes - Prohibit bioformats from reading zip directly ([#1491](../../pull/1491)) diff --git a/pyproject.toml b/pyproject.toml index 84bbde54b..f663fd7a5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,7 +9,7 @@ exclude = [ "*/web_client/*", "*/*egg*/*", ] -ignore = [ +lint.ignore = [ "B017", "B026", "B904", @@ -38,7 +38,7 @@ ignore = [ "PT017", ] line-length = 100 -select = [ +lint.select = [ "B", # bugbear "C90", # mccabe "D", # pydocstyle @@ -61,14 +61,14 @@ select = [ "RSE", ] -[tool.ruff.per-file-ignores] +[tool.ruff.lint.per-file-ignores] # allow "useless expressions" as it shows output # allow non-top level imports # allow long lines "docs/large_image_examples.ipynb" = ["B018", "E402", "E501"] -[tool.ruff.flake8-quotes] +[tool.ruff.lint.flake8-quotes] inline-quotes = "single" -[tool.ruff.mccabe] +[tool.ruff.lint.mccabe] max-complexity = 14 diff --git a/sources/bioformats/large_image_source_bioformats/__init__.py b/sources/bioformats/large_image_source_bioformats/__init__.py index 71477572f..c04b6ac0f 100644 --- a/sources/bioformats/large_image_source_bioformats/__init__.py +++ b/sources/bioformats/large_image_source_bioformats/__init__.py @@ -221,7 +221,21 @@ def __init__(self, path, **kwargs): # noqa try: javabridge.attach() try: - self._bioimage = bioformats.ImageReader(largeImagePath) + self._bioimage = bioformats.ImageReader(largeImagePath, perform_init=False) + try: + # So this as a separate step so, if it fails, we can ask to + # open something that does not exist and bioformats will + # release some file handles. + self._bioimage.init_reader() + except Exception as exc: + try: + # Ask to open a file that should never exist + self._bioimage.rdr.setId('__\0__') + except Exception: + pass + self._bioimage.close() + self._bioimage = None + raise exc except (AttributeError, OSError) as exc: if not os.path.isfile(largeImagePath): raise TileSourceFileNotFoundError(largeImagePath) from None diff --git a/tox.ini b/tox.ini index ef05e2848..20807ce05 100644 --- a/tox.ini +++ b/tox.ini @@ -172,7 +172,7 @@ deps = flake8-quotes ruff commands = - ruff large_image sources utilities girder girder_annotation examples docs test + ruff check large_image sources utilities girder girder_annotation examples docs test flake8 [testenv:type] @@ -213,7 +213,7 @@ skip_install = true deps = ruff commands = - ruff large_image sources utilities girder girder_annotation examples docs test {posargs} + ruff check large_image sources utilities girder girder_annotation examples docs test {posargs} [testenv:format] description = Autoformat import order and autopep8 @@ -228,7 +228,7 @@ commands = isort . autopep8 -ria large_image sources utilities girder girder_annotation examples docs test unify --in-place --recursive large_image sources utilities girder girder_annotation examples docs test - ruff large_image sources utilities girder girder_annotation examples docs test --fix + ruff check large_image sources utilities girder girder_annotation examples docs test --fix [testenv:lintclient] description = Lint the girder large_image plugin client