diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index ce0168f..9099864 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -1,23 +1,18 @@
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
- rev: v5.0.0
+ rev: v6.0.0
hooks:
- id: check-docstring-first
- id: end-of-file-fixer
- id: trailing-whitespace
exclude: ^\.napari-hub/.*
- id: check-yaml # checks for correct yaml syntax for github actions ex.
- exclude:
- (?x)(^src/ndevio/ndev_settings\.yaml$)
- repo: https://github.com/astral-sh/ruff-pre-commit
- rev: v0.12.5
+ rev: v0.14.8
hooks:
- - id: ruff
- - repo: https://github.com/psf/black
- rev: 25.1.0
- hooks:
- - id: black
- - repo: https://github.com/tlambert03/napari-plugin-checks
+ - id: ruff-check
+ - id: ruff-format
+ - repo: https://github.com/napari/napari-plugin-checks
rev: v0.3.0
hooks:
- id: napari-plugin-checks
@@ -25,9 +20,3 @@ repos:
rev: v0.4.0
hooks:
- id: reset-settings-values
- # https://mypy.readthedocs.io/en/stable/
- # you may wish to add this as well!
- # - repo: https://github.com/pre-commit/mirrors-mypy
- # rev: v1.9.0
- # hooks:
- # - id: mypy
diff --git a/pyproject.toml b/pyproject.toml
index 6907ae1..4e70aac 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -95,12 +95,9 @@ markers = [
[tool.coverage]
report.fail_under = 80
-[tool.black]
-line-length = 79
-target-version = ['py311', 'py312', 'py313']
-
[tool.ruff]
line-length = 79
+format.quote-style = "single"
lint.select = [
"E", "F", "W", #flake8
"UP", # pyupgrade
@@ -115,31 +112,9 @@ lint.select = [
"SIM", # flake8-simplify
]
lint.ignore = [
- "E501", # line too long. let black handle this
+ "E501", # line too long. handled by formatter
"UP006", "UP007", # type annotation. As using magicgui require runtime type annotation then we disable this.
]
-
-exclude = [
- ".bzr",
- ".direnv",
- ".eggs",
- ".git",
- ".mypy_cache",
- ".pants.d",
- ".ruff_cache",
- ".svn",
- ".tox",
- ".venv",
- "__pypackages__",
- "_build",
- "buck-out",
- "build",
- "dist",
- "node_modules",
- "venv",
- "*vendored*",
- "*_vendor*",
-]
fix = true
[tool.pixi.workspace]
diff --git a/src/ndevio/__init__.py b/src/ndevio/__init__.py
index f91ef71..7a20a20 100644
--- a/src/ndevio/__init__.py
+++ b/src/ndevio/__init__.py
@@ -3,7 +3,7 @@
try: # noqa: D104
from ._version import version as __version__
except ImportError:
- __version__ = "unknown"
+ __version__ = 'unknown'
from . import helpers
@@ -14,15 +14,15 @@
def __getattr__(name: str):
"""Lazily import nImage to speed up package import."""
- if name == "nImage":
+ if name == 'nImage':
from .nimage import nImage
return nImage
- raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
+ raise AttributeError(f'module {__name__!r} has no attribute {name!r}')
__all__ = [
- "__version__",
- "helpers",
- "nImage",
+ '__version__',
+ 'helpers',
+ 'nImage',
]
diff --git a/src/ndevio/_bioio_plugin_utils.py b/src/ndevio/_bioio_plugin_utils.py
index db9497b..9ce480b 100644
--- a/src/ndevio/_bioio_plugin_utils.py
+++ b/src/ndevio/_bioio_plugin_utils.py
@@ -45,80 +45,80 @@
# 3. Known issues or limitations
BIOIO_PLUGINS = {
# Highest priority: OME formats with excellent metadata preservation
- "bioio-ome-zarr": {
- "extensions": [".zarr"],
- "description": "OME-Zarr files",
- "repository": "https://github.com/bioio-devs/bioio-ome-zarr",
- "core": True,
+ 'bioio-ome-zarr': {
+ 'extensions': ['.zarr'],
+ 'description': 'OME-Zarr files',
+ 'repository': 'https://github.com/bioio-devs/bioio-ome-zarr',
+ 'core': True,
},
- "bioio-ome-tiff": {
- "extensions": [".ome.tif", ".ome.tiff", ".tif", ".tiff"],
- "description": "OME-TIFF files with valid OME-XML metadata",
- "repository": "https://github.com/bioio-devs/bioio-ome-tiff",
- "core": True,
+ 'bioio-ome-tiff': {
+ 'extensions': ['.ome.tif', '.ome.tiff', '.tif', '.tiff'],
+ 'description': 'OME-TIFF files with valid OME-XML metadata',
+ 'repository': 'https://github.com/bioio-devs/bioio-ome-tiff',
+ 'core': True,
},
- "bioio-ome-tiled-tiff": {
- "extensions": [".tiles.ome.tif"],
- "description": "Tiled OME-TIFF files",
- "repository": "https://github.com/bioio-devs/bioio-ome-tiled-tiff",
+ 'bioio-ome-tiled-tiff': {
+ 'extensions': ['.tiles.ome.tif'],
+ 'description': 'Tiled OME-TIFF files',
+ 'repository': 'https://github.com/bioio-devs/bioio-ome-tiled-tiff',
},
# High priority: Format-specific readers with good metadata support
- "bioio-tifffile": {
- "extensions": [".tif", ".tiff"],
- "description": "TIFF files (including those without OME metadata)",
- "repository": "https://github.com/bioio-devs/bioio-tifffile",
- "core": True,
+ 'bioio-tifffile': {
+ 'extensions': ['.tif', '.tiff'],
+ 'description': 'TIFF files (including those without OME metadata)',
+ 'repository': 'https://github.com/bioio-devs/bioio-tifffile',
+ 'core': True,
},
- "bioio-nd2": {
- "extensions": [".nd2"],
- "description": "Nikon ND2 files",
- "repository": "https://github.com/bioio-devs/bioio-nd2",
+ 'bioio-nd2': {
+ 'extensions': ['.nd2'],
+ 'description': 'Nikon ND2 files',
+ 'repository': 'https://github.com/bioio-devs/bioio-nd2',
},
- "bioio-czi": {
- "extensions": [".czi"],
- "description": "Zeiss CZI files",
- "repository": "https://github.com/bioio-devs/bioio-czi",
+ 'bioio-czi': {
+ 'extensions': ['.czi'],
+ 'description': 'Zeiss CZI files',
+ 'repository': 'https://github.com/bioio-devs/bioio-czi',
},
- "bioio-lif": {
- "extensions": [".lif"],
- "description": "Leica LIF files",
- "repository": "https://github.com/bioio-devs/bioio-lif",
+ 'bioio-lif': {
+ 'extensions': ['.lif'],
+ 'description': 'Leica LIF files',
+ 'repository': 'https://github.com/bioio-devs/bioio-lif',
},
- "bioio-dv": {
- "extensions": [".dv", ".r3d"],
- "description": "DeltaVision files",
- "repository": "https://github.com/bioio-devs/bioio-dv",
+ 'bioio-dv': {
+ 'extensions': ['.dv', '.r3d'],
+ 'description': 'DeltaVision files',
+ 'repository': 'https://github.com/bioio-devs/bioio-dv',
},
- "bioio-sldy": {
- "extensions": [".sldy", ".dir"],
- "description": "3i SlideBook files",
- "repository": "https://github.com/bioio-devs/bioio-sldy",
+ 'bioio-sldy': {
+ 'extensions': ['.sldy', '.dir'],
+ 'description': '3i SlideBook files',
+ 'repository': 'https://github.com/bioio-devs/bioio-sldy',
},
# Lower priority: Generic/fallback readers
- "bioio-imageio": {
- "extensions": [".bmp", ".gif", ".jpg", ".jpeg", ".png"],
- "description": "Generic image formats (PNG, JPG, etc.)",
- "repository": "https://github.com/bioio-devs/bioio-imageio",
- "core": True,
+ 'bioio-imageio': {
+ 'extensions': ['.bmp', '.gif', '.jpg', '.jpeg', '.png'],
+ 'description': 'Generic image formats (PNG, JPG, etc.)',
+ 'repository': 'https://github.com/bioio-devs/bioio-imageio',
+ 'core': True,
},
- "bioio-tiff-glob": {
- "extensions": [".tiff"],
- "description": "TIFF sequences (glob patterns)",
- "repository": "https://github.com/bioio-devs/bioio-tiff-glob",
+ 'bioio-tiff-glob': {
+ 'extensions': ['.tiff'],
+ 'description': 'TIFF sequences (glob patterns)',
+ 'repository': 'https://github.com/bioio-devs/bioio-tiff-glob',
},
# Lowest priority: Requires external dependencies (Java)
- "bioio-bioformats": {
- "extensions": [".oib", ".oif", ".vsi", ".ims", ".lsm", ".stk"],
- "description": "Proprietary microscopy formats (requires Java)",
- "repository": "https://github.com/bioio-devs/bioio-bioformats",
- "note": "Requires Java Runtime Environment",
+ 'bioio-bioformats': {
+ 'extensions': ['.oib', '.oif', '.vsi', '.ims', '.lsm', '.stk'],
+ 'description': 'Proprietary microscopy formats (requires Java)',
+ 'repository': 'https://github.com/bioio-devs/bioio-bioformats',
+ 'note': 'Requires Java Runtime Environment',
},
}
# Map extensions to plugin names for quick lookup
_EXTENSION_TO_PLUGIN = {}
for plugin_name, info in BIOIO_PLUGINS.items():
- for ext in info["extensions"]:
+ for ext in info['extensions']:
if ext not in _EXTENSION_TO_PLUGIN:
_EXTENSION_TO_PLUGIN[ext] = []
_EXTENSION_TO_PLUGIN[ext].append(plugin_name)
@@ -181,7 +181,7 @@ def format_plugin_installation_message(
if not suggested_plugins:
return (
f"\n\nNo bioio plugins found for '{filename}'.\n"
- "See https://github.com/bioio-devs/bioio for available plugins."
+ 'See https://github.com/bioio-devs/bioio for available plugins.'
)
# Format the plugin list (filters out core plugins automatically)
@@ -190,36 +190,36 @@ def format_plugin_installation_message(
# Build appropriate message based on what's installed/missing
if installed_plugins and installable_plugins and plugin_list:
# Case 1: Some plugins installed but failed, suggest alternatives
- installed_str = ", ".join(sorted(installed_plugins))
+ installed_str = ', '.join(sorted(installed_plugins))
return (
f"\n\nInstalled plugin '{installed_str}' failed to read '{filename}'.\n"
- "Try one of these alternatives:\n\n"
- f"{plugin_list}"
- "\nRestart napari/Python after installing."
+ 'Try one of these alternatives:\n\n'
+ f'{plugin_list}'
+ '\nRestart napari/Python after installing.'
)
if installed_plugins and not installable_plugins:
# Case 2: All suggested plugins already installed but still failed
- installed_str = ", ".join(sorted(installed_plugins))
+ installed_str = ', '.join(sorted(installed_plugins))
return (
f"\nFile '{filename}' is supported by: {installed_str}\n"
- "However, the plugin failed to read it.\n"
- "This may indicate a corrupt file or incompatible format variant."
+ 'However, the plugin failed to read it.\n'
+ 'This may indicate a corrupt file or incompatible format variant.'
)
if plugin_list:
# Case 3: No installed plugins, suggest installing
return (
f"\n\nTo read '{filename}', install one of:\n\n"
- f"{plugin_list}"
- "\nRestart napari/Python after installing."
+ f'{plugin_list}'
+ '\nRestart napari/Python after installing.'
)
# Case 4: All suggested plugins are core plugins (already should be installed)
return (
f"\n\nRequired plugins for '{filename}' should already be installed.\n"
"If you're still having issues, check your installation or "
- "open an issue at https://github.com/ndev-kit/ndevio."
+ 'open an issue at https://github.com/ndev-kit/ndevio.'
)
@@ -254,11 +254,11 @@ def suggest_plugins_for_path(path: Path | str) -> list[str]:
# Check compound extensions first (.ome.tiff, .tiles.ome.tif, etc.)
for plugin_name, info in BIOIO_PLUGINS.items():
- for ext in info["extensions"]:
+ for ext in info['extensions']:
# Compound extension: multiple dots and matches filename
if (
- ext.startswith(".")
- and len(ext.split(".")) > 2
+ ext.startswith('.')
+ and len(ext.split('.')) > 2
and filename.endswith(ext)
):
return [plugin_name]
@@ -286,7 +286,7 @@ def _format_plugin_list(plugin_names: list[str]) -> str:
Formatted installation instructions
"""
if not plugin_names:
- return ""
+ return ''
lines = []
for plugin_name in plugin_names:
@@ -296,13 +296,13 @@ def _format_plugin_list(plugin_names: list[str]) -> str:
continue
# Skip core plugins (already installed with ndevio)
- if info.get("core", False):
+ if info.get('core', False):
continue
- lines.append(f" • {plugin_name}")
- lines.append(f" {info['description']}")
- if info.get("note"):
- lines.append(f" Note: {info['note']}")
- lines.append(f" Install: pip install {plugin_name}\n")
+ lines.append(f' • {plugin_name}')
+ lines.append(f' {info["description"]}')
+ if info.get('note'):
+ lines.append(f' Note: {info["note"]}')
+ lines.append(f' Install: pip install {plugin_name}\n')
- return "\n".join(lines)
+ return '\n'.join(lines)
diff --git a/src/ndevio/_colormap_utils.py b/src/ndevio/_colormap_utils.py
index 0f66819..19ae8ce 100644
--- a/src/ndevio/_colormap_utils.py
+++ b/src/ndevio/_colormap_utils.py
@@ -4,13 +4,13 @@
multichannel image display.
"""
-SINGLE_CHANNEL_COLORMAP = "gray"
+SINGLE_CHANNEL_COLORMAP = 'gray'
-TWO_CHANNEL_CYCLE = ["magenta", "green"]
+TWO_CHANNEL_CYCLE = ['magenta', 'green']
-MULTI_CHANNEL_CYCLE = ["cyan", "magenta", "yellow", "blue", "green", "red"]
+MULTI_CHANNEL_CYCLE = ['cyan', 'magenta', 'yellow', 'blue', 'green', 'red']
-RGB = ["red", "green", "blue"]
+RGB = ['red', 'green', 'blue']
def get_colormap_for_channel(channel_idx: int, n_channels: int) -> str:
@@ -43,9 +43,9 @@ def get_colormap_for_channel(channel_idx: int, n_channels: int) -> str:
__all__ = [
- "SINGLE_CHANNEL_COLORMAP",
- "TWO_CHANNEL_CYCLE",
- "MULTI_CHANNEL_CYCLE",
- "RGB",
- "get_colormap_for_channel",
+ 'SINGLE_CHANNEL_COLORMAP',
+ 'TWO_CHANNEL_CYCLE',
+ 'MULTI_CHANNEL_CYCLE',
+ 'RGB',
+ 'get_colormap_for_channel',
]
diff --git a/src/ndevio/_napari_reader.py b/src/ndevio/_napari_reader.py
index 7eaff17..6cbb310 100644
--- a/src/ndevio/_napari_reader.py
+++ b/src/ndevio/_napari_reader.py
@@ -51,17 +51,17 @@ def napari_get_reader(
open_first_scene_only = (
open_first_scene_only
if open_first_scene_only is not None
- else settings.ndevio_reader.scene_handling == "View First Scene Only" # type: ignore
+ else settings.ndevio_reader.scene_handling == 'View First Scene Only' # type: ignore
) or False
open_all_scenes = (
open_all_scenes
if open_all_scenes is not None
- else settings.ndevio_reader.scene_handling == "View All Scenes" # type: ignore
+ else settings.ndevio_reader.scene_handling == 'View All Scenes' # type: ignore
) or False
if isinstance(path, list):
- logger.info("Bioio: Expected a single path, got a list of paths.")
+ logger.info('Bioio: Expected a single path, got a list of paths.')
return None
try:
@@ -76,7 +76,7 @@ def napari_get_reader(
except UnsupportedFileFormatError as e:
# determine_reader_plugin() already enhanced the error message
- logger.error("ndevio: Unsupported file format: %s", path)
+ logger.error('ndevio: Unsupported file format: %s', path)
# Show plugin installer widget if enabled in settings
if settings.ndevio_reader.suggest_reader_plugins: # type: ignore
_open_plugin_installer(path, e)
@@ -85,7 +85,7 @@ def napari_get_reader(
return None
except Exception as e: # noqa: BLE001
- logger.warning("ndevio: Error reading file: %s", e)
+ logger.warning('ndevio: Error reading file: %s', e)
return None
@@ -121,7 +121,7 @@ def napari_reader_function(
"""
img = nImage(path, reader=reader)
- logger.info("Bioio: Reading file with %d scenes", len(img.scenes))
+ logger.info('Bioio: Reading file with %d scenes', len(img.scenes))
# open first scene only
if len(img.scenes) == 1 or open_first_scene_only:
@@ -152,8 +152,8 @@ def _open_scene_container(
viewer = napari.current_viewer()
viewer.window.add_dock_widget(
nImageSceneWidget(viewer, path, img, in_memory),
- area="right",
- name=f"{Path(path).stem}{DELIMITER}Scenes",
+ area='right',
+ name=f'{Path(path).stem}{DELIMITER}Scenes',
)
@@ -181,7 +181,7 @@ def _open_plugin_installer(
# Don't try to open widget if no viewer available (e.g., in tests)
if viewer is None:
logger.warning(
- "Cannot open plugin installer widget: No napari viewer available"
+ 'Cannot open plugin installer widget: No napari viewer available'
)
return
@@ -191,6 +191,6 @@ def _open_plugin_installer(
widget = PluginInstallerWidget(plugin_manager=manager)
viewer.window.add_dock_widget(
widget,
- area="right",
- name="Install BioIO Plugin",
+ area='right',
+ name='Install BioIO Plugin',
)
diff --git a/src/ndevio/_plugin_installer.py b/src/ndevio/_plugin_installer.py
index e5f09ee..98ed43e 100644
--- a/src/ndevio/_plugin_installer.py
+++ b/src/ndevio/_plugin_installer.py
@@ -58,7 +58,7 @@ def install_plugin(plugin_name: str) -> int:
InstallerTools,
)
- logger.info("Queueing installation for: %s", plugin_name)
+ logger.info('Queueing installation for: %s', plugin_name)
queue = get_installer_queue()
@@ -69,7 +69,7 @@ def install_plugin(plugin_name: str) -> int:
# Queue the installation
job_id = queue.install(tool=tool, pkgs=[plugin_name])
- logger.info("Installation queued with job ID: %s", job_id)
+ logger.info('Installation queued with job ID: %s', job_id)
return job_id
@@ -107,7 +107,7 @@ def verify_plugin_installed(plugin_name: str) -> bool:
"""
try:
# Convert plugin name to module name (bioio-czi -> bioio_czi)
- module_name = plugin_name.replace("-", "_")
+ module_name = plugin_name.replace('-', '_')
__import__(module_name)
return True
except ImportError:
diff --git a/src/ndevio/_plugin_manager.py b/src/ndevio/_plugin_manager.py
index 314b157..ac1fc9d 100644
--- a/src/ndevio/_plugin_manager.py
+++ b/src/ndevio/_plugin_manager.py
@@ -137,7 +137,7 @@ def feasibility_report(self) -> dict[str, PluginSupport]:
if self._feasibility_report is None and self.path:
from bioio import plugin_feasibility_report
- logger.debug("Generating feasibility report for: %s", self.path)
+ logger.debug('Generating feasibility report for: %s', self.path)
self._feasibility_report = plugin_feasibility_report(self.path)
return self._feasibility_report or {}
@@ -164,7 +164,7 @@ def installed_plugins(self) -> set[str]:
... print("OME-TIFF reader is available")
"""
report = self.feasibility_report
- return {name for name in report if name != "ArrayLike"}
+ return {name for name in report if name != 'ArrayLike'}
@property
def suggested_plugins(self) -> list[str]:
@@ -219,7 +219,7 @@ def installable_plugins(self) -> list[str]:
return [
plugin_name
for plugin_name in suggested
- if not BIOIO_PLUGINS.get(plugin_name, {}).get("core", False)
+ if not BIOIO_PLUGINS.get(plugin_name, {}).get('core', False)
and plugin_name not in installed
]
@@ -261,8 +261,8 @@ def get_working_reader(
"""
if not self.path:
logger.warning(
- "Cannot get working reader without a path. "
- "Initialize ReaderPluginManager with a file path."
+ 'Cannot get working reader without a path. '
+ 'Initialize ReaderPluginManager with a file path.'
)
return None
@@ -275,7 +275,7 @@ def get_working_reader(
and report[preferred_reader].supported
):
logger.info(
- "Using preferred reader: %s for %s",
+ 'Using preferred reader: %s for %s',
preferred_reader,
self.path,
)
@@ -287,7 +287,7 @@ def get_working_reader(
for reader_name in get_reader_priority():
if reader_name in report and report[reader_name].supported:
logger.info(
- "Using reader: %s for %s (from priority list)",
+ 'Using reader: %s for %s (from priority list)',
reader_name,
self.path,
)
@@ -295,15 +295,15 @@ def get_working_reader(
# Try any other installed reader that supports the file
for name, support in report.items():
- if name != "ArrayLike" and support.supported:
+ if name != 'ArrayLike' and support.supported:
logger.info(
- "Using reader: %s for %s (from installed plugins)",
+ 'Using reader: %s for %s (from installed plugins)',
name,
self.path,
)
return self._get_reader_module(name)
- logger.warning("No working reader found for: %s", self.path)
+ logger.warning('No working reader found for: %s', self.path)
return None
def get_installation_message(self) -> str:
@@ -325,7 +325,7 @@ def get_installation_message(self) -> str:
... print(manager.get_installation_message())
"""
if not self.path:
- return ""
+ return ''
from ._bioio_plugin_utils import format_plugin_installation_message
@@ -356,7 +356,7 @@ def _get_reader_module(reader_name: str) -> Reader:
If the reader module cannot be imported
"""
# Convert plugin name to module name (bioio-czi -> bioio_czi)
- module_name = reader_name.replace("-", "_")
- logger.debug("Importing reader module: %s", module_name)
+ module_name = reader_name.replace('-', '_')
+ logger.debug('Importing reader module: %s', module_name)
module = importlib.import_module(module_name)
return module.Reader
diff --git a/src/ndevio/helpers.py b/src/ndevio/helpers.py
index ea279cc..26e8b66 100644
--- a/src/ndevio/helpers.py
+++ b/src/ndevio/helpers.py
@@ -22,12 +22,12 @@
from ndevio import nImage
__all__ = [
- "check_for_missing_files",
- "create_id_string",
- "elide_string",
- "get_channel_names",
- "get_directory_and_files",
- "get_squeezed_dim_order",
+ 'check_for_missing_files',
+ 'create_id_string',
+ 'elide_string',
+ 'get_channel_names',
+ 'get_directory_and_files',
+ 'get_squeezed_dim_order',
]
@@ -57,17 +57,17 @@ def get_directory_and_files(
"""
if pattern is None:
pattern = [
- "tif",
- "tiff",
- "nd2",
- "czi",
- "lif",
- "oib",
- "png",
- "jpg",
- "jpeg",
- "bmp",
- "gif",
+ 'tif',
+ 'tiff',
+ 'nd2',
+ 'czi',
+ 'lif',
+ 'oib',
+ 'png',
+ 'jpg',
+ 'jpeg',
+ 'bmp',
+ 'gif',
]
if dir_path is None:
return None, []
@@ -75,16 +75,16 @@ def get_directory_and_files(
directory = Path(dir_path)
if dir_path is not None and not directory.exists():
- raise FileNotFoundError(f"Directory {dir_path} does not exist.")
+ raise FileNotFoundError(f'Directory {dir_path} does not exist.')
pattern = [pattern] if isinstance(pattern, str) else pattern
# add *. to each pattern if it doesn't already have either
pattern_glob = []
for pat in pattern:
- if "." not in pat:
- pat = f"*.{pat}"
- if "*" not in pat:
- pat = f"*{pat}"
+ if '.' not in pat:
+ pat = f'*.{pat}'
+ if '*' not in pat:
+ pat = f'*{pat}'
pattern_glob.append(pat)
files = []
@@ -113,15 +113,15 @@ def get_channel_names(img: nImage | BioImage) -> list[str]:
The channel names.
"""
- if "S" in img.dims.order:
- return ["red", "green", "blue"]
+ if 'S' in img.dims.order:
+ return ['red', 'green', 'blue']
# Ensure we have plain Python strings, not numpy string types
return [str(c) for c in img.channel_names]
def get_squeezed_dim_order(
img: nImage | BioImage,
- skip_dims: tuple[str, ...] | list[str] | str = ("C", "S"),
+ skip_dims: tuple[str, ...] | list[str] | str = ('C', 'S'),
) -> str:
"""
Return a string containing the squeezed dimensions of the given BioImage.
@@ -141,7 +141,7 @@ def get_squeezed_dim_order(
"""
if isinstance(skip_dims, str):
skip_dims = (skip_dims,)
- return "".join(
+ return ''.join(
{k: v for k, v in img.dims.items() if v > 1 and k not in skip_dims}
)
@@ -177,7 +177,7 @@ def create_id_string(img: nImage | BioImage, identifier: str) -> str:
scene = img.ome_metadata.images[scene_idx].name
except NotImplementedError:
scene = img.current_scene # not useful with OmeTiffReader, atm
- id_string = f"{identifier}__{scene_idx}__{scene}"
+ id_string = f'{identifier}__{scene_idx}__{scene}'
return id_string
@@ -216,7 +216,7 @@ def check_for_missing_files(
def elide_string(
- input_string: str, max_length: int = 15, location: str = "middle"
+ input_string: str, max_length: int = 15, location: str = 'middle'
) -> str:
"""
Elide a string if it exceeds the specified length.
@@ -249,11 +249,11 @@ def elide_string(
if max_length <= 5:
return input_string[:max_length]
# Elide the string based on the location
- if location == "start":
- return "..." + input_string[-(max_length - 3) :]
- if location == "end":
- return input_string[: max_length - 3] + "..."
- if location == "middle":
+ if location == 'start':
+ return '...' + input_string[-(max_length - 3) :]
+ if location == 'end':
+ return input_string[: max_length - 3] + '...'
+ if location == 'middle':
half_length = (max_length - 3) // 2
- return input_string[:half_length] + "..." + input_string[-half_length:]
+ return input_string[:half_length] + '...' + input_string[-half_length:]
raise ValueError('Invalid location. Must be "start", "middle", or "end".')
diff --git a/src/ndevio/nimage.py b/src/ndevio/nimage.py
index e8268ac..1925de5 100644
--- a/src/ndevio/nimage.py
+++ b/src/ndevio/nimage.py
@@ -17,10 +17,10 @@
logger = logging.getLogger(__name__)
-DELIM = " :: "
+DELIM = ' :: '
# Keywords that indicate a channel contains labels/segmentation data
-LABEL_KEYWORDS = ["label", "mask", "segmentation", "seg", "roi"]
+LABEL_KEYWORDS = ['label', 'mask', 'segmentation', 'seg', 'roi']
def determine_reader_plugin(
@@ -79,7 +79,7 @@ def determine_reader_plugin(
msg_extra = None
raise UnsupportedFileFormatError(
- reader_name="ndevio",
+ reader_name='ndevio',
path=str(image),
msg_extra=msg_extra,
)
@@ -215,7 +215,7 @@ def _get_layer_data(self, in_memory: bool | None = None) -> xr.DataArray:
except NotImplementedError:
logger.warning(
- "Bioio: Mosaic tile switching not supported for this reader"
+ 'Bioio: Mosaic tile switching not supported for this reader'
)
return None
else:
@@ -230,8 +230,8 @@ def _infer_layer_type(self, channel_name: str) -> str:
"""Infer layer type from channel name."""
name_lower = channel_name.lower()
if any(keyword in name_lower for keyword in LABEL_KEYWORDS):
- return "labels"
- return "image"
+ return 'labels'
+ return 'image'
def _get_scale(self) -> tuple | None:
"""Extract physical pixel scale from image metadata according to number of napari dims."""
@@ -263,10 +263,10 @@ def _build_metadata(self) -> dict:
'ome_metadata'.
"""
- img_meta = {"bioimage": self, "raw_image_metadata": self.metadata}
+ img_meta = {'bioimage': self, 'raw_image_metadata': self.metadata}
try:
- img_meta["ome_metadata"] = self.ome_metadata
+ img_meta['ome_metadata'] = self.ome_metadata
except NotImplementedError:
pass # Reader doesn't support OME metadata
except (ValueError, TypeError, KeyError) as e:
@@ -275,7 +275,7 @@ def _build_metadata(self) -> dict:
# As such, when accessing ome_metadata, we may get various exceptions
# Log warning but continue - raw metadata is still available
logger.warning(
- "Could not parse OME metadata: %s. "
+ 'Could not parse OME metadata: %s. '
"Raw metadata is still available in 'raw_image_metadata'.",
e,
)
@@ -302,11 +302,11 @@ def _build_layer_name(
"""
path_stem = (
- Path(self.path).stem if self.path is not None else "unknown path"
+ Path(self.path).stem if self.path is not None else 'unknown path'
)
# Check if scene info is meaningful
- no_scene = len(self.scenes) == 1 and self.current_scene == "Image:0"
+ no_scene = len(self.scenes) == 1 and self.current_scene == 'Image:0'
parts = []
if channel_name:
@@ -358,24 +358,24 @@ def _build_single_layer_tuple(
"""
meta = {
- "name": self._build_layer_name(channel_name),
- "metadata": base_metadata,
+ 'name': self._build_layer_name(channel_name),
+ 'metadata': base_metadata,
}
if scale:
- meta["scale"] = scale
+ meta['scale'] = scale
# Add image-specific metadata
- if layer_type == "image":
+ if layer_type == 'image':
from ._colormap_utils import get_colormap_for_channel
- meta["colormap"] = get_colormap_for_channel(
+ meta['colormap'] = get_colormap_for_channel(
channel_idx, n_channels
)
- meta["blending"] = (
- "additive"
+ meta['blending'] = (
+ 'additive'
if channel_idx > 0 and n_channels > 1
- else "translucent_no_depth"
+ else 'translucent_no_depth'
)
# Apply per-channel overrides
@@ -483,20 +483,20 @@ def get_layer_data_tuples(
# Handle RGB images specially
if DimensionNames.Samples in self.reader.dims.order:
meta = {
- "name": self._build_layer_name(),
- "rgb": True,
- "metadata": base_metadata,
+ 'name': self._build_layer_name(),
+ 'rgb': True,
+ 'metadata': base_metadata,
}
if scale:
- meta["scale"] = scale
+ meta['scale'] = scale
self.layer_data_tuples = [
- (self.napari_layer_data.data, meta, "image")
+ (self.napari_layer_data.data, meta, 'image')
]
return self.layer_data_tuples
# Single channel image (no channel dimension)
if channel_dim not in self.napari_layer_data.dims:
- effective_type = layer_type or "image"
+ effective_type = layer_type or 'image'
self.layer_data_tuples = [
self._build_single_layer_tuple(
data=self.napari_layer_data.data,
@@ -520,7 +520,7 @@ def get_layer_data_tuples(
layer_tuples = []
for i in range(n_channels):
channel_name = (
- channel_names[i] if i < len(channel_names) else f"channel_{i}"
+ channel_names[i] if i < len(channel_names) else f'channel_{i}'
)
effective_type = self._resolve_layer_type(
channel_name, layer_type, channel_types
diff --git a/src/ndevio/sampledata/__init__.py b/src/ndevio/sampledata/__init__.py
index 4b5f823..654872d 100644
--- a/src/ndevio/sampledata/__init__.py
+++ b/src/ndevio/sampledata/__init__.py
@@ -10,10 +10,10 @@
)
__all__ = [
- "ndev_logo",
- "neocortex",
- "neuron_labels",
- "neuron_labels_processed",
- "neuron_raw",
- "scratch_assay",
+ 'ndev_logo',
+ 'neocortex',
+ 'neuron_labels',
+ 'neuron_labels_processed',
+ 'neuron_raw',
+ 'scratch_assay',
]
diff --git a/src/ndevio/sampledata/_sample_data.py b/src/ndevio/sampledata/_sample_data.py
index ba38257..4cc45a4 100644
--- a/src/ndevio/sampledata/_sample_data.py
+++ b/src/ndevio/sampledata/_sample_data.py
@@ -19,13 +19,13 @@
if TYPE_CHECKING:
from napari.types import LayerDataTuple
-SAMPLE_DIR = Path(__file__).parent / "data"
+SAMPLE_DIR = Path(__file__).parent / 'data'
def ndev_logo() -> list[LayerDataTuple]:
"""Load the ndev logo image."""
return nImage(
- SAMPLE_DIR / "ndev-logo.png",
+ SAMPLE_DIR / 'ndev-logo.png',
reader=ImageIOReader,
).get_layer_data_tuples()
@@ -33,9 +33,9 @@ def ndev_logo() -> list[LayerDataTuple]:
def scratch_assay() -> list[LayerDataTuple]:
"""Load scratch assay data with labeled nuclei and cytoplasm."""
scratch_assay_raw_path = pooch.retrieve(
- url="doi:10.5281/zenodo.17845346/scratch-assay-labeled-10T-2Ch.tiff",
- known_hash="md5:2b98c4ea18cd741a1545e59855348a2f",
- fname="scratch-assay-labeled-10T-2Ch.tiff",
+ url='doi:10.5281/zenodo.17845346/scratch-assay-labeled-10T-2Ch.tiff',
+ known_hash='md5:2b98c4ea18cd741a1545e59855348a2f',
+ fname='scratch-assay-labeled-10T-2Ch.tiff',
path=SAMPLE_DIR,
)
img = nImage(
@@ -45,14 +45,14 @@ def scratch_assay() -> list[LayerDataTuple]:
return img.get_layer_data_tuples(
in_memory=True,
channel_types={
- "H3342": "image",
- "oblique": "image",
- "nuclei": "labels",
- "cyto": "labels",
+ 'H3342': 'image',
+ 'oblique': 'image',
+ 'nuclei': 'labels',
+ 'cyto': 'labels',
},
channel_kwargs={
- "H3342": {"colormap": "cyan"},
- "oblique": {"colormap": "gray"},
+ 'H3342': {'colormap': 'cyan'},
+ 'oblique': {'colormap': 'gray'},
},
)
@@ -60,9 +60,9 @@ def scratch_assay() -> list[LayerDataTuple]:
def neocortex() -> list[LayerDataTuple]:
"""Load neocortex 3-channel image data."""
neocortex_raw_path = pooch.retrieve(
- url="doi:10.5281/zenodo.17845346/neocortex-3Ch.tiff",
- known_hash="md5:eadc3fac751052461fb2e5f3c6716afa",
- fname="neocortex-3Ch.tiff",
+ url='doi:10.5281/zenodo.17845346/neocortex-3Ch.tiff',
+ known_hash='md5:eadc3fac751052461fb2e5f3c6716afa',
+ fname='neocortex-3Ch.tiff',
path=SAMPLE_DIR,
)
return nImage(
@@ -77,9 +77,9 @@ def neuron_raw() -> list[LayerDataTuple]:
This sample is downloaded from Zenodo if not present locally.
"""
neuron_raw_path = pooch.retrieve(
- url="doi:10.5281/zenodo.17845346/neuron-4Ch_raw.tiff",
- known_hash="md5:5d3e42bca2085e8588b6f23cf89ba87c",
- fname="neuron-4Ch_raw.tiff",
+ url='doi:10.5281/zenodo.17845346/neuron-4Ch_raw.tiff',
+ known_hash='md5:5d3e42bca2085e8588b6f23cf89ba87c',
+ fname='neuron-4Ch_raw.tiff',
path=SAMPLE_DIR,
)
return nImage(
@@ -87,9 +87,9 @@ def neuron_raw() -> list[LayerDataTuple]:
reader=OmeTiffReader,
).get_layer_data_tuples(
in_memory=True,
- layer_type="image",
+ layer_type='image',
channel_kwargs={
- "PHALL": {"colormap": "gray"},
+ 'PHALL': {'colormap': 'gray'},
},
)
@@ -97,20 +97,20 @@ def neuron_raw() -> list[LayerDataTuple]:
def neuron_labels() -> list[LayerDataTuple]:
"""Load neuron labels data."""
return nImage(
- SAMPLE_DIR / "neuron-4Ch_labels.tiff",
+ SAMPLE_DIR / 'neuron-4Ch_labels.tiff',
reader=OmeTiffReader,
).get_layer_data_tuples(
in_memory=True,
- layer_type="labels",
+ layer_type='labels',
)
def neuron_labels_processed() -> list[LayerDataTuple]:
"""Load processed neuron labels data."""
return nImage(
- SAMPLE_DIR / "neuron-4Ch_labels_processed.tiff",
+ SAMPLE_DIR / 'neuron-4Ch_labels_processed.tiff',
reader=OmeTiffReader,
).get_layer_data_tuples(
in_memory=True,
- layer_type="labels",
+ layer_type='labels',
)
diff --git a/src/ndevio/widgets/__init__.py b/src/ndevio/widgets/__init__.py
index 8e5235f..10ae809 100644
--- a/src/ndevio/widgets/__init__.py
+++ b/src/ndevio/widgets/__init__.py
@@ -6,9 +6,9 @@
from ._utilities_container import UtilitiesContainer
__all__ = [
- "PluginInstallerWidget",
- "nImageSceneWidget",
- "UtilitiesContainer",
- "DELIMITER",
- "ReaderPluginManager",
+ 'PluginInstallerWidget',
+ 'nImageSceneWidget',
+ 'UtilitiesContainer',
+ 'DELIMITER',
+ 'ReaderPluginManager',
]
diff --git a/src/ndevio/widgets/_plugin_install_widget.py b/src/ndevio/widgets/_plugin_install_widget.py
index 1479d59..84c91a2 100644
--- a/src/ndevio/widgets/_plugin_install_widget.py
+++ b/src/ndevio/widgets/_plugin_install_widget.py
@@ -81,23 +81,23 @@ def _init_widgets(self):
# Error mode: show file that failed
file_name = self.manager.path.name
self._title_label = Label(
- value=f"Cannot read file: {file_name}"
+ value=f'Cannot read file: {file_name}'
)
else:
# Standalone mode: general title
self._title_label = Label(
- value="Install BioIO Reader Plugin"
+ value='Install BioIO Reader Plugin'
)
self.append(self._title_label)
- self._info_label = Label(value="Select a plugin to install:")
+ self._info_label = Label(value='Select a plugin to install:')
self.append(self._info_label)
# Get all available plugin names from manager
plugin_names = self.manager.available_plugins
self._plugin_select = ComboBox(
- label="Plugin",
+ label='Plugin',
choices=plugin_names,
value=None,
nullable=True,
@@ -111,11 +111,11 @@ def _init_widgets(self):
self.append(self._plugin_select)
# Install button
- self._install_button = PushButton(text="Install Plugin")
+ self._install_button = PushButton(text='Install Plugin')
self.append(self._install_button)
# Status label
- self._status_label = Label(value="")
+ self._status_label = Label(value='')
self.append(self._status_label)
def _connect_events(self):
@@ -124,16 +124,16 @@ def _connect_events(self):
def _on_install_clicked(self):
"""Handle install button click."""
- self._status_label.value = "Installing..."
+ self._status_label.value = 'Installing...'
# Get selected plugin name
plugin_name = self._plugin_select.value
if not plugin_name:
- self._status_label.value = "No plugin selected"
+ self._status_label.value = 'No plugin selected'
return
- logger.info("User requested install of: %s", plugin_name)
+ logger.info('User requested install of: %s', plugin_name)
# Use napari-plugin-manager's InstallerQueue
from .._plugin_installer import get_installer_queue, install_plugin
@@ -144,8 +144,8 @@ def _on_install_clicked(self):
# Connect to the queue's signals to monitor progress
def on_process_finished(event):
"""Handle installation completion."""
- exit_code = event.get("exit_code", 1)
- pkgs = event.get("pkgs", [])
+ exit_code = event.get('exit_code', 1)
+ pkgs = event.get('pkgs', [])
# Check if this event is for our package
if plugin_name not in pkgs:
@@ -153,17 +153,17 @@ def on_process_finished(event):
if exit_code == 0:
self._status_label.value = (
- f"✓ Successfully installed {plugin_name}!\n\n"
- "⚠ It is recommended to restart napari."
+ f'✓ Successfully installed {plugin_name}!\n\n'
+ '⚠ It is recommended to restart napari.'
)
- logger.info("Plugin installed successfully: %s", plugin_name)
+ logger.info('Plugin installed successfully: %s', plugin_name)
else:
self._status_label.value = (
- f"✗ Installation failed for {plugin_name}\n"
- f"Exit code: {exit_code}\n"
- "Check the console for details."
+ f'✗ Installation failed for {plugin_name}\n'
+ f'Exit code: {exit_code}\n'
+ 'Check the console for details.'
)
- logger.error("Plugin installation failed: %s", plugin_name)
+ logger.error('Plugin installation failed: %s', plugin_name)
# Disconnect after completion (success or failure)
if self._queue_connection is not None:
@@ -180,4 +180,4 @@ def on_process_finished(event):
# Queue the installation (returns job ID)
job_id = install_plugin(plugin_name)
- logger.info("Installation job %s queued for %s", job_id, plugin_name)
+ logger.info('Installation job %s queued for %s', job_id, plugin_name)
diff --git a/src/ndevio/widgets/_scene_widget.py b/src/ndevio/widgets/_scene_widget.py
index 6501607..b4bb3d2 100644
--- a/src/ndevio/widgets/_scene_widget.py
+++ b/src/ndevio/widgets/_scene_widget.py
@@ -20,7 +20,7 @@
logger = logging.getLogger(__name__)
-DELIMITER = " :: "
+DELIMITER = ' :: '
class nImageSceneWidget(Container):
@@ -92,7 +92,7 @@ def __init__(
self.in_memory = in_memory
self.settings = get_settings()
self.scenes = [
- f"{idx}{DELIMITER}{scene}"
+ f'{idx}{DELIMITER}{scene}'
for idx, scene in enumerate(self.img.scenes)
]
diff --git a/src/ndevio/widgets/_utilities_container.py b/src/ndevio/widgets/_utilities_container.py
index 518a9f8..2ebcdf9 100644
--- a/src/ndevio/widgets/_utilities_container.py
+++ b/src/ndevio/widgets/_utilities_container.py
@@ -39,7 +39,7 @@
def save_ome_tiff(
data: np.ndarray,
uri: Path,
- dim_order: str = "TCZYX",
+ dim_order: str = 'TCZYX',
channel_names: list[str] | None = None,
image_name: str | None = None,
physical_pixel_sizes: PhysicalPixelSizes | None = None,
@@ -76,12 +76,12 @@ def save_ome_tiff(
# Validate channel_names count matches data
if channel_names is not None and dim_order:
- channel_idx = dim_order.upper().find("C")
+ channel_idx = dim_order.upper().find('C')
if channel_idx != -1:
if channel_idx >= len(data.shape):
logging.warning(
- "dim_order %r has C at index %d, but data has only %d "
- "dimensions. Ignoring channel_names.",
+ 'dim_order %r has C at index %d, but data has only %d '
+ 'dimensions. Ignoring channel_names.',
dim_order,
channel_idx,
len(data.shape),
@@ -139,8 +139,8 @@ def concatenate_and_save_files(
array_list = []
for file in files:
img = nImage(file)
- if "S" in img.dims.order:
- img_data = img.get_image_data("TSZYX")
+ if 'S' in img.dims.order:
+ img_data = img.get_image_data('TSZYX')
else:
img_data = img.data
@@ -152,19 +152,19 @@ def concatenate_and_save_files(
if not array_list:
raise ValueError(
- f"No valid channels found in files: {[str(f) for f in files]}"
+ f'No valid channels found in files: {[str(f) for f in files]}'
)
img_data = np.concatenate(array_list, axis=1)
# Save as OME-TIFF
save_directory.mkdir(parents=True, exist_ok=True)
- save_path = save_directory / f"{save_name}.tiff"
+ save_path = save_directory / f'{save_name}.tiff'
save_ome_tiff(
data=img_data,
uri=save_path,
- dim_order="TCZYX",
+ dim_order='TCZYX',
channel_names=channel_names,
image_name=save_name,
physical_pixel_sizes=p_sizes,
@@ -229,12 +229,12 @@ def extract_and_save_scenes_ome_tiff(
# Create ID string for this scene
image_id = helpers.create_id_string(img, base_save_name)
- save_path = save_directory / f"{image_id}.tiff"
+ save_path = save_directory / f'{image_id}.tiff'
save_ome_tiff(
data=img.data,
uri=save_path,
- dim_order="TCZYX",
+ dim_order='TCZYX',
channel_names=channel_names,
image_name=image_id,
physical_pixel_sizes=p_sizes,
@@ -296,7 +296,7 @@ def _init_batch_runner(self):
def _on_batch_start(self, total: int):
"""Callback when batch starts - initialize progress bar."""
- self._progress_bar.label = f"Processing {total} file sets"
+ self._progress_bar.label = f'Processing {total} file sets'
self._progress_bar.value = 0
self._progress_bar.max = total
self._set_batch_button_state(running=True)
@@ -306,7 +306,7 @@ def _on_batch_item_complete(self, result, ctx):
self._progress_bar.value = ctx.index + 1
# ctx.item is (files, save_name) tuple
_, save_name = ctx.item
- self._progress_bar.label = f"Processed {save_name}"
+ self._progress_bar.label = f'Processed {save_name}'
def _on_batch_complete(self):
"""Callback when the entire batch completes."""
@@ -314,37 +314,34 @@ def _on_batch_complete(self):
errors = self._batch_runner.error_count
if errors > 0:
self._progress_bar.label = (
- f"Completed {total - errors} file sets ({errors} Errors)"
+ f'Completed {total - errors} file sets ({errors} Errors)'
)
else:
- self._progress_bar.label = f"Completed {total} file sets"
+ self._progress_bar.label = f'Completed {total} file sets'
self._set_batch_button_state(running=False)
- self._results.value = (
- "Batch concatenated files in directory."
- f"\nAt {time.strftime('%H:%M:%S')}"
- )
+ self._results.value = f'Batch concatenated files in directory.\nAt {time.strftime("%H:%M:%S")}'
def _on_batch_error(self, ctx, exception):
"""Callback when a batch item fails."""
_, save_name = ctx.item
error_msg = str(exception)
if len(error_msg) > 100:
- error_msg = error_msg[:100] + "..."
- self._progress_bar.label = f"Error on {save_name}: {error_msg}"
+ error_msg = error_msg[:100] + '...'
+ self._progress_bar.label = f'Error on {save_name}: {error_msg}'
def _set_batch_button_state(self, running: bool):
"""Update batch button appearance based on running state."""
if running:
- self._concatenate_batch_button.text = "Cancel"
+ self._concatenate_batch_button.text = 'Cancel'
self._concatenate_batch_button.tooltip = (
- "Cancel the current batch operation."
+ 'Cancel the current batch operation.'
)
else:
- self._concatenate_batch_button.text = "Batch Concat."
+ self._concatenate_batch_button.text = 'Batch Concat.'
self._concatenate_batch_button.tooltip = (
- "Concatenate files in the selected directory by iterating"
- " over the remaining files in the directory based on the "
- "number of files selected."
+ 'Concatenate files in the selected directory by iterating'
+ ' over the remaining files in the directory based on the '
+ 'number of files selected.'
)
def _on_batch_button_clicked(self):
@@ -352,45 +349,45 @@ def _on_batch_button_clicked(self):
if self._batch_runner.is_running:
self._batch_runner.cancel()
self._set_batch_button_state(running=False)
- self._progress_bar.label = "Cancelled"
+ self._progress_bar.label = 'Cancelled'
else:
self.batch_concatenate_files()
def _init_widgets(self):
"""Initialize widgets."""
self._save_directory_prefix = LineEdit(
- label="Save Dir. Prefix",
- tooltip="Prefix for the save directories.",
+ label='Save Dir. Prefix',
+ tooltip='Prefix for the save directories.',
)
self._save_directory = FileEdit(
- mode="d",
- tooltip="Directory where images will be saved. \n"
- "Upon selecting the first file, the save directory will be set \n"
- "to the grandparent directory of the first file.",
+ mode='d',
+ tooltip='Directory where images will be saved. \n'
+ 'Upon selecting the first file, the save directory will be set \n'
+ 'to the grandparent directory of the first file.',
)
self._save_directory_container = Container(
widgets=[self._save_directory_prefix, self._save_directory],
- layout="horizontal",
+ layout='horizontal',
)
self._default_save_directory = self._save_directory.value
self._files = FileEdit(
- mode="rm",
- tooltip="Select file(s) to load.",
+ mode='rm',
+ tooltip='Select file(s) to load.',
)
- self._progress_bar = ProgressBar(label="Progress")
- self._results = TextEdit(label="Info")
+ self._progress_bar = ProgressBar(label='Progress')
+ self._results = TextEdit(label='Info')
def _init_save_name_container(self):
"""Initialize the save name container."""
- self._save_name_container = Container(layout="horizontal")
+ self._save_name_container = Container(layout='horizontal')
self._save_name = LineEdit(
- label="Save Name",
- tooltip="Name of the saved file. "
- "Proper extension will be added when saved.",
+ label='Save Name',
+ tooltip='Name of the saved file. '
+ 'Proper extension will be added when saved.',
)
self._append_scene_button = PushButton(
- label="Append Scene to Name",
+ label='Append Scene to Name',
)
self._save_name_container.extend(
[self._save_name, self._append_scene_button]
@@ -398,29 +395,29 @@ def _init_save_name_container(self):
def _init_open_image_container(self):
"""Initialize the open image container."""
- self._open_image_container = Container(layout="horizontal")
- self._open_image_button = PushButton(label="Open File(s)")
+ self._open_image_container = Container(layout='horizontal')
+ self._open_image_button = PushButton(label='Open File(s)')
self._select_next_image_button = PushButton(
- label="Select Next",
- tooltip="Select the next file(s) in the directory. \n"
- "Note that the files are sorted alphabetically and numerically.",
+ label='Select Next',
+ tooltip='Select the next file(s) in the directory. \n'
+ 'Note that the files are sorted alphabetically and numerically.',
)
self._open_image_container.append(self._open_image_button)
self._open_image_container.append(self._select_next_image_button)
def _init_concatenate_files_container(self):
self._concatenate_files_container = Container(
- layout="horizontal",
+ layout='horizontal',
)
- self._concatenate_files_button = PushButton(label="Concat. Files")
+ self._concatenate_files_button = PushButton(label='Concat. Files')
self._concatenate_batch_button = PushButton(
- label="Batch Concat.",
- tooltip="Concatenate files in the selected directory by iterating"
- " over the remaing files in the directory based on the number of"
- " files selected. The files are sorted "
- "alphabetically and numerically, which may not be consistent "
- "with your file viewer. But, opening related consecutive files "
- "should work as expected.",
+ label='Batch Concat.',
+ tooltip='Concatenate files in the selected directory by iterating'
+ ' over the remaing files in the directory based on the number of'
+ ' files selected. The files are sorted '
+ 'alphabetically and numerically, which may not be consistent '
+ 'with your file viewer. But, opening related consecutive files '
+ 'should work as expected.',
)
self._concatenate_files_container.extend(
[
@@ -432,68 +429,68 @@ def _init_concatenate_files_container(self):
def _init_metadata_container(self):
self._update_scale = CheckBox(
value=True,
- label="Scale",
- tooltip="Update the scale when files are selected.",
+ label='Scale',
+ tooltip='Update the scale when files are selected.',
)
self._update_channel_names = CheckBox(
value=True,
- label="Channel Names",
- tooltip="Update the channel names when files are selected.",
+ label='Channel Names',
+ tooltip='Update the channel names when files are selected.',
)
self._file_options_container = GroupBoxContainer(
- layout="horizontal",
- name="Update Metadata on File Select",
+ layout='horizontal',
+ name='Update Metadata on File Select',
labels=False,
label=False,
widgets=[self._update_scale, self._update_channel_names],
)
self._layer_metadata_update_button = PushButton(
- label="Update from Selected Layer"
+ label='Update from Selected Layer'
)
self._num_scenes_label = Label(
- label="Num. Scenes: ",
+ label='Num. Scenes: ',
)
self._dim_shape = LineEdit(
- label="Dims: ",
- tooltip="Sanity check for available dimensions.",
+ label='Dims: ',
+ tooltip='Sanity check for available dimensions.',
)
self._image_info_container = Container(
widgets=[self._num_scenes_label, self._dim_shape],
- layout="horizontal",
+ layout='horizontal',
)
self._channel_names = LineEdit(
- label="Channel Name(s)",
- tooltip="Enter channel names as a list. If left blank or the "
- "channel names are not the proper length, then default channel "
- "names will be used.",
+ label='Channel Name(s)',
+ tooltip='Enter channel names as a list. If left blank or the '
+ 'channel names are not the proper length, then default channel '
+ 'names will be used.',
)
self._scale_tuple = TupleEdit(
- label="Scale, ZYX",
- tooltip="Pixel size, usually in μm",
+ label='Scale, ZYX',
+ tooltip='Pixel size, usually in μm',
value=(0.0000, 1.0000, 1.0000),
- options={"step": 0.0001},
+ options={'step': 0.0001},
)
self._channel_scale_container = Container(
widgets=[self._channel_names, self._scale_tuple],
)
self._scale_layers_button = PushButton(
- label="Scale Layer(s)",
- tooltip="Scale the selected layer(s) based on the given scale.",
+ label='Scale Layer(s)',
+ tooltip='Scale the selected layer(s) based on the given scale.',
)
self._metadata_button_container = Container(
widgets=[
self._layer_metadata_update_button,
self._scale_layers_button,
],
- layout="horizontal",
+ layout='horizontal',
)
self._metadata_container = GroupBoxContainer(
- layout="vertical",
- name="Metadata",
+ layout='vertical',
+ name='Metadata',
widgets=[
self._file_options_container,
self._image_info_container,
@@ -506,16 +503,16 @@ def _init_metadata_container(self):
def _init_scene_container(self):
"""Initialize the scene container, allowing scene saving."""
self._scene_container = Container(
- layout="horizontal",
- tooltip="Must be in list index format. Ex: [0, 1, 2] or [5:10]",
+ layout='horizontal',
+ tooltip='Must be in list index format. Ex: [0, 1, 2] or [5:10]',
)
self._scenes_to_extract = LineEdit(
- tooltip="Enter the scenes to extract as a list. If left blank "
- "then all scenes will be extracted.",
+ tooltip='Enter the scenes to extract as a list. If left blank '
+ 'then all scenes will be extracted.',
)
self._extract_scenes = PushButton(
- label="Extract and Save Scenes",
- tooltip="Extract scenes from a single selected file.",
+ label='Extract and Save Scenes',
+ tooltip='Extract scenes from a single selected file.',
)
self._scene_container.append(self._scenes_to_extract)
self._scene_container.append(self._extract_scenes)
@@ -523,31 +520,31 @@ def _init_scene_container(self):
def _init_save_layers_container(self):
"""Initialize the container to save images, labels, and shapes."""
self._save_layers_button = PushButton(
- text="Selected Layers (TIFF)",
- tooltip="Concatenate and save all selected layers as OME-TIFF. "
- "Layers will save to corresponding directories based on the layer "
- "type, e.g. Images, Labels, ShapesAsLabels. Shapes are saved as "
- "labels based on the selected image layer dimensions. If multiple "
- "layer types are selected, then the image will save to Layers.",
+ text='Selected Layers (TIFF)',
+ tooltip='Concatenate and save all selected layers as OME-TIFF. '
+ 'Layers will save to corresponding directories based on the layer '
+ 'type, e.g. Images, Labels, ShapesAsLabels. Shapes are saved as '
+ 'labels based on the selected image layer dimensions. If multiple '
+ 'layer types are selected, then the image will save to Layers.',
)
self._export_figure_button = PushButton(
- text="Figure (PNG)",
- tooltip="Export the current canvas figure as a PNG to the Figure "
- "directory. Only works in 2D mode. Use Screenshot for 3D figures. "
- "Crops the figure to the extent of the data, attempting to remove "
- "margins. Increase or decrease scaling in the settings",
+ text='Figure (PNG)',
+ tooltip='Export the current canvas figure as a PNG to the Figure '
+ 'directory. Only works in 2D mode. Use Screenshot for 3D figures. '
+ 'Crops the figure to the extent of the data, attempting to remove '
+ 'margins. Increase or decrease scaling in the settings',
)
self._export_screenshot_button = PushButton(
- text="Canvas (PNG)",
- tooltip="Export the current canvas screenshot as a PNG to the "
- "Figure directory. Works in 2D and 3D mode. Uses the full canvas "
- "size, including margins. Increase or decrease scaling in the "
- "settings, and also it is possible to override the canvas size.",
+ text='Canvas (PNG)',
+ tooltip='Export the current canvas screenshot as a PNG to the '
+ 'Figure directory. Works in 2D and 3D mode. Uses the full canvas '
+ 'size, including margins. Increase or decrease scaling in the '
+ 'settings, and also it is possible to override the canvas size.',
)
self._save_layers_container = GroupBoxContainer(
- layout="horizontal",
- name="Export",
+ layout='horizontal',
+ name='Export',
labels=None,
)
@@ -566,7 +563,7 @@ def _init_layout(self):
self._files,
self._open_image_container,
],
- name="Opening",
+ name='Opening',
labels=False,
)
self._save_group = GroupBoxContainer(
@@ -578,7 +575,7 @@ def _init_layout(self):
self._save_layers_container,
self._progress_bar,
],
- name="Saving",
+ name='Saving',
labels=False,
)
@@ -639,7 +636,7 @@ def _update_metadata_from_Image(
update_scale: bool = True,
):
"""Update the metadata based on the given image."""
- dims = re.search(r"\[(.*?)\]", str(img.dims)).group(1)
+ dims = re.search(r'\[(.*?)\]', str(img.dims)).group(1)
self._dim_shape.value = dims
self._num_scenes_label.value = str(len(img.scenes))
@@ -673,36 +670,36 @@ def append_scene_to_name(self):
"""Append the scene to the save name."""
if self._viewer.layers.selection.active is not None:
try:
- img = self._viewer.layers.selection.active.metadata["bioimage"]
- scene = re.sub(r"[^\w\s]", "-", img.current_scene)
- self._save_name.value = f"{self._save_name.value}_{scene}"
+ img = self._viewer.layers.selection.active.metadata['bioimage']
+ scene = re.sub(r'[^\w\s]', '-', img.current_scene)
+ self._save_name.value = f'{self._save_name.value}_{scene}'
except AttributeError:
self._results.value = (
- "Tried to append scene to name, but layer not opened with"
- " ndevio reader."
+ 'Tried to append scene to name, but layer not opened with'
+ ' ndevio reader.'
)
else:
self._results.value = (
- "Tried to append scene to name, but no layer selected."
- " So the first scene from the first file will be appended."
+ 'Tried to append scene to name, but no layer selected.'
+ ' So the first scene from the first file will be appended.'
)
from ndevio import nImage
img = nImage(self._files.value[0])
- scene = re.sub(r"[^\w\s]", "-", img.current_scene)
- self._save_name.value = f"{self._save_name.value}_{scene}"
+ scene = re.sub(r'[^\w\s]', '-', img.current_scene)
+ self._save_name.value = f'{self._save_name.value}_{scene}'
def update_metadata_from_layer(self):
"""Update metadata from the selected layer."""
selected_layer = self._viewer.layers.selection.active
try:
- img = selected_layer.metadata["bioimage"]
+ img = selected_layer.metadata['bioimage']
self._update_metadata_from_Image(img)
except AttributeError:
self._results.value = (
- "Tried to update metadata, but no layer selected."
- f"\nAt {time.strftime('%H:%M:%S')}"
+ 'Tried to update metadata, but no layer selected.'
+ f'\nAt {time.strftime("%H:%M:%S")}'
)
except KeyError:
scale = selected_layer.scale
@@ -712,14 +709,14 @@ def update_metadata_from_layer(self):
scale[-1],
)
self._results.value = (
- "Tried to update metadata, but could only update scale"
- " because layer not opened with ndevio reader."
- f"\nAt {time.strftime('%H:%M:%S')}"
+ 'Tried to update metadata, but could only update scale'
+ ' because layer not opened with ndevio reader.'
+ f'\nAt {time.strftime("%H:%M:%S")}'
)
def open_images(self):
"""Open the selected images in the napari viewer with ndevio."""
- self._viewer.open(self._files.value, plugin="ndevio")
+ self._viewer.open(self._files.value, plugin='ndevio')
def select_next_images(self):
"""Open the next set of images in the directory."""
@@ -730,14 +727,14 @@ def select_next_images(self):
first_file = self._files.value[0]
parent_dir = first_file.parent
- files = list(parent_dir.glob(f"*{first_file.suffix}"))
+ files = list(parent_dir.glob(f'*{first_file.suffix}'))
files = os_sorted(files)
idx = files.index(first_file)
next_files = files[idx + num_files : idx + num_files + num_files]
if not next_files:
- self._results.value = "No more file sets to select."
+ self._results.value = 'No more file sets to select.'
return
from ndevio import nImage
@@ -800,7 +797,7 @@ def _get_dims_for_shape_layer(self) -> tuple[int, ...]:
)
if dim_layer is None:
raise ValueError(
- "No image or labels present to convert shapes layer."
+ 'No image or labels present to convert shapes layer.'
)
label_dim = dim_layer.data.shape
label_dim = label_dim[:-1] if label_dim[-1] == 3 else label_dim
@@ -816,24 +813,24 @@ def _get_save_loc(
return save_directory / file_name
def _determine_save_directory(self, save_dir: str | None = None) -> str:
- if self._save_directory_prefix.value != "":
- save_dir = f"{self._save_directory_prefix.value}_{save_dir}"
+ if self._save_directory_prefix.value != '':
+ save_dir = f'{self._save_directory_prefix.value}_{save_dir}'
else:
- save_dir = f"{save_dir}"
+ save_dir = f'{save_dir}'
return save_dir
def save_files_as_ome_tiff(self) -> None:
"""Save the selected files as OME-TIFF with threading."""
from napari.qt import create_worker
- save_dir = self._determine_save_directory("ConcatenatedImages")
+ save_dir = self._determine_save_directory('ConcatenatedImages')
save_directory = self._save_directory.value / save_dir
save_name = self._save_name.value
cnames = self._channel_names.value
channel_names = ast.literal_eval(cnames) if cnames else None
- self._progress_bar.label = "Concatenating files..."
+ self._progress_bar.label = 'Concatenating files...'
self._progress_bar.value = 0
self._progress_bar.max = 0
@@ -850,22 +847,22 @@ def save_files_as_ome_tiff(self) -> None:
def _on_concat_complete(self, save_path: Path) -> None:
"""Handle completion of file concatenation."""
- self._progress_bar.label = ""
+ self._progress_bar.label = ''
self._progress_bar.max = 1
self._progress_bar.value = 1
self._results.value = (
- f"Saved Concatenated Image: {save_path.name}"
- f"\nAt {time.strftime('%H:%M:%S')}"
+ f'Saved Concatenated Image: {save_path.name}'
+ f'\nAt {time.strftime("%H:%M:%S")}'
)
def _on_concat_error(self, exception: Exception) -> None:
"""Handle error during file concatenation."""
- self._progress_bar.label = "Error"
+ self._progress_bar.label = 'Error'
self._progress_bar.max = 1
self._progress_bar.value = 0
self._results.value = (
- f"Error concatenating files: {exception}"
- f"\nAt {time.strftime('%H:%M:%S')}"
+ f'Error concatenating files: {exception}'
+ f'\nAt {time.strftime("%H:%M:%S")}'
)
def _build_file_sets(self) -> list[tuple[list[Path], str]]:
@@ -879,7 +876,7 @@ def _build_file_sets(self) -> list[tuple[list[Path], str]]:
suffix = self._files.value[0].suffix
num_files = len(self._files.value)
- all_files = os_sorted(list(parent_dir.glob(f"*{suffix}")))
+ all_files = os_sorted(list(parent_dir.glob(f'*{suffix}')))
from ndevio import nImage
@@ -899,17 +896,17 @@ def batch_concatenate_files(self) -> None:
if not file_sets:
self._results.value = (
- f"No complete file sets found.\nAt {time.strftime('%H:%M:%S')}"
+ f'No complete file sets found.\nAt {time.strftime("%H:%M:%S")}'
)
return
cnames = self._channel_names.value
channel_names = ast.literal_eval(cnames) if cnames else None
- save_dir = self._determine_save_directory("ConcatenatedImages")
+ save_dir = self._determine_save_directory('ConcatenatedImages')
save_directory = self._save_directory.value / save_dir
- self._progress_bar.label = "Starting batch..."
+ self._progress_bar.label = 'Starting batch...'
self._set_batch_button_state(running=True)
self._batch_runner.run(
@@ -918,12 +915,12 @@ def batch_concatenate_files(self) -> None:
save_directory=save_directory,
channel_names=channel_names,
p_sizes=self.p_sizes,
- log_file=save_directory / "batch_concatenate.log.txt",
+ log_file=save_directory / 'batch_concatenate.log.txt',
log_header={
- "Source Directory": str(self._files.value[0].parent),
- "Save Directory": str(save_directory),
- "Files per Set": len(self._files.value),
- "Total Sets": len(file_sets),
+ 'Source Directory': str(self._files.value[0].parent),
+ 'Save Directory': str(save_directory),
+ 'Files per Set': len(self._files.value),
+ 'Total Sets': len(file_sets),
},
threaded=True,
)
@@ -940,15 +937,15 @@ def save_scenes_ome_tiff(self) -> None:
scenes = self._scenes_to_extract.value
scenes_list = ast.literal_eval(scenes) if scenes else list(img.scenes)
- save_dir = self._determine_save_directory("ExtractedScenes")
+ save_dir = self._determine_save_directory('ExtractedScenes')
save_directory = self._save_directory.value / save_dir
- base_save_name = self._save_name.value.split(".")[0]
+ base_save_name = self._save_name.value.split('.')[0]
cnames = self._channel_names.value
channel_names = ast.literal_eval(cnames) if cnames else None
- self._progress_bar.label = "Extracting Scenes"
+ self._progress_bar.label = 'Extracting Scenes'
self._progress_bar.value = 0
self._progress_bar.max = len(scenes_list)
@@ -973,41 +970,38 @@ def _on_scene_extracted(self, result: tuple[int, str]) -> None:
scene_idx, scene_name = result
self._progress_bar.value = self._progress_bar.value + 1
self._results.value = (
- f"Extracted scene {scene_idx}: {scene_name}"
- f"\nAt {time.strftime('%H:%M:%S')}"
+ f'Extracted scene {scene_idx}: {scene_name}'
+ f'\nAt {time.strftime("%H:%M:%S")}'
)
def _on_scenes_complete(self, scenes_list: list, _=None) -> None:
"""Handle completion of all scene extractions."""
- self._progress_bar.label = ""
- self._results.value = (
- f"Saved extracted scenes: {scenes_list}"
- f"\nAt {time.strftime('%H:%M:%S')}"
- )
+ self._progress_bar.label = ''
+ self._results.value = f'Saved extracted scenes: {scenes_list}\nAt {time.strftime("%H:%M:%S")}'
def _on_scene_error(self, exc: Exception) -> None:
"""Handle error during scene extraction."""
- self._progress_bar.label = "Error"
+ self._progress_bar.label = 'Error'
self._progress_bar.max = 1
self._progress_bar.value = 0
self._results.value = (
- f"Error extracting scenes: {exc}\nAt {time.strftime('%H:%M:%S')}"
+ f'Error extracting scenes: {exc}\nAt {time.strftime("%H:%M:%S")}'
)
def canvas_export_figure(self) -> None:
"""Export the current canvas figure to the save directory."""
if self._viewer.dims.ndisplay != 2:
self._results.value = (
- "Exporting Figure only works in 2D mode."
- "\nUse Screenshot for 3D figures."
- f"\nAt {time.strftime('%H:%M:%S')}"
+ 'Exporting Figure only works in 2D mode.'
+ '\nUse Screenshot for 3D figures.'
+ f'\nAt {time.strftime("%H:%M:%S")}'
)
return
- save_name = f"{self._save_name.value}_figure.png"
+ save_name = f'{self._save_name.value}_figure.png'
save_path = self._get_save_loc(
self._save_directory.value,
- "Figures",
+ 'Figures',
save_name,
)
@@ -1019,18 +1013,18 @@ def canvas_export_figure(self) -> None:
)
self._results.value = (
- f"Exported canvas figure to Figures directory."
- f"\nSaved as {save_name}"
- f"\nWith scale factor of {scale}"
- f"\nAt {time.strftime('%H:%M:%S')}"
+ f'Exported canvas figure to Figures directory.'
+ f'\nSaved as {save_name}'
+ f'\nWith scale factor of {scale}'
+ f'\nAt {time.strftime("%H:%M:%S")}'
)
return
def canvas_screenshot(self) -> None:
"""Export the current canvas screenshot to the save directory."""
- save_name = f"{self._save_name.value}_canvas.png"
+ save_name = f'{self._save_name.value}_canvas.png'
save_path = self._get_save_loc(
- self._save_directory.value, "Figures", save_name
+ self._save_directory.value, 'Figures', save_name
)
scale = self._settings.ndevio_export.canvas_scale
@@ -1047,11 +1041,11 @@ def canvas_screenshot(self) -> None:
)
self._results.value = (
- f"Exported screenshot of canvas to Figures directory."
- f"\nSaved as {save_name}"
- f"\nWith canvas dimensions of {canvas_size}"
- f"\nWith scale factor of {scale}"
- f"\nAt {time.strftime('%H:%M:%S')}"
+ f'Exported screenshot of canvas to Figures directory.'
+ f'\nSaved as {save_name}'
+ f'\nWith canvas dimensions of {canvas_size}'
+ f'\nWith scale factor of {scale}'
+ f'\nAt {time.strftime("%H:%M:%S")}'
)
return
@@ -1067,35 +1061,35 @@ def save_layers_as_ome_tiff(self) -> None:
]
layer_save_type = (
- "Layers" if len(set(layer_types)) > 1 else layer_types[0]
+ 'Layers' if len(set(layer_types)) > 1 else layer_types[0]
)
layer_save_dir = self._determine_save_directory(layer_save_type)
- layer_save_name = f"{self._save_name.value}.tiff"
+ layer_save_name = f'{self._save_name.value}.tiff'
layer_save_loc = self._get_save_loc(
self._save_directory.value, layer_save_dir, layer_save_name
)
- if layer_save_type not in ["Shapes", "Labels"]:
+ if layer_save_type not in ['Shapes', 'Labels']:
cnames = self._channel_names.value
channel_names = ast.literal_eval(cnames) if cnames else None
else:
channel_names = [layer_save_type]
- if layer_save_type == "Shapes":
+ if layer_save_type == 'Shapes':
layer_data = layer_data.astype(np.int16)
- elif layer_save_type == "Labels":
+ elif layer_save_type == 'Labels':
if layer_data.max() > 65535:
layer_data = layer_data.astype(np.int32)
else:
layer_data = layer_data.astype(np.int16)
if self._squeezed_dims_order:
- dim_order = "C" + self._squeezed_dims_order
+ dim_order = 'C' + self._squeezed_dims_order
else:
num_dims = len(layer_data.shape)
- dim_order = "C" + "".join(
- [str(d) for d in "TZYX"[-(num_dims - 1) :]]
+ dim_order = 'C' + ''.join(
+ [str(d) for d in 'TZYX'[-(num_dims - 1) :]]
)
self._layer_save_type = layer_save_type
@@ -1116,16 +1110,16 @@ def save_layers_as_ome_tiff(self) -> None:
def _on_layer_save_complete(self, result: None = None) -> None:
"""Handle successful layer save completion."""
self._results.value = (
- f"Saved {self._layer_save_type}: "
+ f'Saved {self._layer_save_type}: '
+ str(self._save_name.value)
- + f"\nAt {time.strftime('%H:%M:%S')}"
+ + f'\nAt {time.strftime("%H:%M:%S")}'
)
def _on_layer_save_error(self, exc: Exception) -> None:
"""Handle layer save error."""
- self._progress_bar.label = "Error"
+ self._progress_bar.label = 'Error'
self._progress_bar.max = 1
self._progress_bar.value = 0
self._results.value = (
- f"Error saving layers: {exc}\nAt {time.strftime('%H:%M:%S')}"
+ f'Error saving layers: {exc}\nAt {time.strftime("%H:%M:%S")}'
)
diff --git a/tests/conftest.py b/tests/conftest.py
index 3ab50d6..733661c 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -8,4 +8,4 @@
@pytest.fixture
def resources_dir() -> Path:
"""Return path to test resources directory."""
- return Path(__file__).parent / "resources"
+ return Path(__file__).parent / 'resources'
diff --git a/tests/test_bioio_plugin_utils.py b/tests/test_bioio_plugin_utils.py
index c42f198..61bb072 100644
--- a/tests/test_bioio_plugin_utils.py
+++ b/tests/test_bioio_plugin_utils.py
@@ -8,36 +8,36 @@ def test_czi_file(self):
"""Test that CZI file suggests bioio-czi."""
from ndevio._bioio_plugin_utils import suggest_plugins_for_path
- plugins = suggest_plugins_for_path("test.czi")
+ plugins = suggest_plugins_for_path('test.czi')
assert len(plugins) == 1
- assert plugins[0] == "bioio-czi"
+ assert plugins[0] == 'bioio-czi'
def test_lif_file(self):
"""Test that LIF file suggests bioio-lif."""
from ndevio._bioio_plugin_utils import suggest_plugins_for_path
- plugins = suggest_plugins_for_path("test.lif")
+ plugins = suggest_plugins_for_path('test.lif')
assert len(plugins) == 1
- assert plugins[0] == "bioio-lif"
+ assert plugins[0] == 'bioio-lif'
def test_tiff_file_suggests_all(self):
"""Test that TIFF files suggest all TIFF-compatible plugins."""
from ndevio._bioio_plugin_utils import suggest_plugins_for_path
- plugins = suggest_plugins_for_path("test.tiff")
+ plugins = suggest_plugins_for_path('test.tiff')
# Should get bioio-ome-tiff, bioio-tifffile, bioio-tiff-glob
- assert "bioio-ome-tiff" in plugins
- assert "bioio-tifffile" in plugins
- assert "bioio-tiff-glob" in plugins
+ assert 'bioio-ome-tiff' in plugins
+ assert 'bioio-tifffile' in plugins
+ assert 'bioio-tiff-glob' in plugins
def test_unsupported_extension(self):
"""Test that unsupported extensions return empty list."""
from ndevio._bioio_plugin_utils import suggest_plugins_for_path
- plugins = suggest_plugins_for_path("test.xyz")
+ plugins = suggest_plugins_for_path('test.xyz')
assert len(plugins) == 0
@@ -52,19 +52,19 @@ def test_manager_filters_installed_plugins(self):
from ndevio._plugin_manager import ReaderPluginManager
# Mock feasibility report showing bioio-czi as installed
- with patch("bioio.plugin_feasibility_report") as mock_report:
+ with patch('bioio.plugin_feasibility_report') as mock_report:
mock_report.return_value = {
- "bioio-czi": Mock(supported=False),
- "ArrayLike": Mock(supported=False),
+ 'bioio-czi': Mock(supported=False),
+ 'ArrayLike': Mock(supported=False),
}
- manager = ReaderPluginManager("test.czi")
+ manager = ReaderPluginManager('test.czi')
# bioio-czi should be in installed_plugins
- assert "bioio-czi" in manager.installed_plugins
+ assert 'bioio-czi' in manager.installed_plugins
# bioio-czi should NOT be in installable_plugins (already installed)
- assert "bioio-czi" not in manager.installable_plugins
+ assert 'bioio-czi' not in manager.installable_plugins
def test_manager_suggests_uninstalled_plugins(self):
"""Test that manager suggests uninstalled plugins."""
@@ -73,19 +73,19 @@ def test_manager_suggests_uninstalled_plugins(self):
from ndevio._plugin_manager import ReaderPluginManager
# Mock feasibility report with no bioio-lif installed
- with patch("bioio.plugin_feasibility_report") as mock_report:
+ with patch('bioio.plugin_feasibility_report') as mock_report:
mock_report.return_value = {
- "bioio-ome-tiff": Mock(supported=False),
- "ArrayLike": Mock(supported=False),
+ 'bioio-ome-tiff': Mock(supported=False),
+ 'ArrayLike': Mock(supported=False),
}
- manager = ReaderPluginManager("test.lif")
+ manager = ReaderPluginManager('test.lif')
# bioio-lif should be in suggested_plugins
- assert "bioio-lif" in manager.suggested_plugins
+ assert 'bioio-lif' in manager.suggested_plugins
# bioio-lif should also be in installable_plugins
- assert "bioio-lif" in manager.installable_plugins
+ assert 'bioio-lif' in manager.installable_plugins
def test_manager_excludes_core_plugins_from_installable(self):
"""Test that core plugins are excluded from installable list."""
@@ -94,28 +94,28 @@ def test_manager_excludes_core_plugins_from_installable(self):
from ndevio._plugin_manager import ReaderPluginManager
# Mock report showing no plugins installed
- with patch("bioio.plugin_feasibility_report") as mock_report:
+ with patch('bioio.plugin_feasibility_report') as mock_report:
mock_report.return_value = {
- "ArrayLike": Mock(supported=False),
+ 'ArrayLike': Mock(supported=False),
}
- manager = ReaderPluginManager("test.tiff")
+ manager = ReaderPluginManager('test.tiff')
# Core plugins should not be in installable
installable_plugins = manager.installable_plugins
# These are core plugins, shouldn't need installation
core_plugins = [
- "bioio-ome-tiff",
- "bioio-imageio",
- "bioio-ome-zarr",
- "bioio-tifffile",
+ 'bioio-ome-tiff',
+ 'bioio-imageio',
+ 'bioio-ome-zarr',
+ 'bioio-tifffile',
]
for core in core_plugins:
assert core not in installable_plugins
# bioio-tiff-glob is not core, should be installable
- assert "bioio-tiff-glob" in installable_plugins
+ assert 'bioio-tiff-glob' in installable_plugins
class TestFormatPluginInstallationMessage:
@@ -128,16 +128,16 @@ def test_czi_message_basic(self):
suggest_plugins_for_path,
)
- suggested = suggest_plugins_for_path("test.czi")
+ suggested = suggest_plugins_for_path('test.czi')
message = format_plugin_installation_message(
- filename="test.czi",
+ filename='test.czi',
suggested_plugins=suggested,
installed_plugins=set(),
installable_plugins=suggested,
)
- assert "bioio-czi" in message
- assert "pip install" in message or "conda install" in message
+ assert 'bioio-czi' in message
+ assert 'pip install' in message or 'conda install' in message
def test_unsupported_extension_message(self):
"""Test message for completely unsupported extension."""
@@ -146,10 +146,10 @@ def test_unsupported_extension_message(self):
)
message = format_plugin_installation_message(
- filename="test.xyz",
+ filename='test.xyz',
suggested_plugins=[],
installed_plugins=set(),
installable_plugins=[],
)
- assert "No bioio plugins found" in message or ".xyz" in message
+ assert 'No bioio plugins found' in message or '.xyz' in message
diff --git a/tests/test_helpers.py b/tests/test_helpers.py
index bfbcdb6..3347e8d 100644
--- a/tests/test_helpers.py
+++ b/tests/test_helpers.py
@@ -20,38 +20,38 @@ class TestCheckForMissingFiles:
def test_no_missing_files_path(self, tmp_path):
"""Test when all files exist using Path objects."""
- directory = tmp_path / "test_dir"
+ directory = tmp_path / 'test_dir'
directory.mkdir()
- file1 = directory / "file1.txt"
- file1.write_text("Test file 1")
- file2 = directory / "file2.txt"
- file2.write_text("Test file 2")
+ file1 = directory / 'file1.txt'
+ file1.write_text('Test file 1')
+ file2 = directory / 'file2.txt'
+ file2.write_text('Test file 2')
missing = check_for_missing_files([file1, file2], directory)
assert missing == []
def test_missing_file_path(self, tmp_path):
"""Test detecting missing files using Path objects."""
- directory = tmp_path / "test_dir"
+ directory = tmp_path / 'test_dir'
directory.mkdir()
- file1 = directory / "file1.txt"
- file1.write_text("Test file 1")
- file3 = directory / "file3.txt" # Does not exist
+ file1 = directory / 'file1.txt'
+ file1.write_text('Test file 1')
+ file3 = directory / 'file3.txt' # Does not exist
missing = check_for_missing_files([file1, file3], directory)
- assert missing == [("file3.txt", "test_dir")]
+ assert missing == [('file3.txt', 'test_dir')]
def test_missing_file_str(self, tmp_path):
"""Test with string file names."""
- directory = tmp_path / "test_dir"
+ directory = tmp_path / 'test_dir'
directory.mkdir()
- file1 = directory / "file1.txt"
- file1.write_text("Test file 1")
+ file1 = directory / 'file1.txt'
+ file1.write_text('Test file 1')
missing = check_for_missing_files(
- ["file1.txt", "file3.txt"], directory
+ ['file1.txt', 'file3.txt'], directory
)
- assert missing == [("file3.txt", "test_dir")]
+ assert missing == [('file3.txt', 'test_dir')]
class TestCreateIdString:
@@ -60,28 +60,28 @@ class TestCreateIdString:
def test_basic_id_string(self):
"""Test basic ID string creation from numpy array."""
img = nImage(np.random.random((2, 2)))
- id_string = create_id_string(img, "test_id")
- assert id_string == "test_id__0__Image:0"
+ id_string = create_id_string(img, 'test_id')
+ assert id_string == 'test_id__0__Image:0'
def test_none_identifier(self):
"""Test with None identifier."""
img = nImage(np.random.random((2, 2)))
id_string = create_id_string(img, None)
- assert id_string == "None__0__Image:0"
+ assert id_string == 'None__0__Image:0'
def test_with_ome_metadata_name(self, tmp_path):
"""Test that OmeTiffWriter image_name is used in ID string."""
OmeTiffWriter.save(
data=np.random.random((2, 2)),
- uri=tmp_path / "test.tiff",
- image_name="test_image",
+ uri=tmp_path / 'test.tiff',
+ image_name='test_image',
)
- img = nImage(tmp_path / "test.tiff")
- id_string = create_id_string(img, "test_id")
+ img = nImage(tmp_path / 'test.tiff')
+ id_string = create_id_string(img, 'test_id')
- assert img.current_scene == "Image:0"
- assert id_string == "test_id__0__test_image"
+ assert img.current_scene == 'Image:0'
+ assert id_string == 'test_id__0__test_image'
class TestGetChannelNames:
@@ -90,7 +90,7 @@ class TestGetChannelNames:
def test_multichannel_image(self, resources_dir):
"""Test getting channel names from multichannel image."""
# Use the legacy tiff which has channel names
- file = resources_dir / "cells3d2ch_legacy.tiff"
+ file = resources_dir / 'cells3d2ch_legacy.tiff'
if file.exists():
img = nImage(file)
names = get_channel_names(img)
@@ -99,12 +99,12 @@ def test_multichannel_image(self, resources_dir):
def test_rgb_image(self, resources_dir):
"""Test that RGB images return red, green, blue."""
- file = resources_dir / "RGB_bad_metadata.tiff"
+ file = resources_dir / 'RGB_bad_metadata.tiff'
if file.exists():
img = nImage(file)
- if "S" in img.dims.order:
+ if 'S' in img.dims.order:
names = get_channel_names(img)
- assert names == ["red", "green", "blue"]
+ assert names == ['red', 'green', 'blue']
class TestGetDirectoryAndFiles:
@@ -113,28 +113,28 @@ class TestGetDirectoryAndFiles:
def test_default_pattern(self, tmp_path):
"""Test with default pattern finding image files."""
# Create test files
- (tmp_path / "image1.tif").write_bytes(b"fake")
- (tmp_path / "image2.tiff").write_bytes(b"fake")
- (tmp_path / "data.csv").write_text("a,b,c")
+ (tmp_path / 'image1.tif').write_bytes(b'fake')
+ (tmp_path / 'image2.tiff').write_bytes(b'fake')
+ (tmp_path / 'data.csv').write_text('a,b,c')
directory, files = get_directory_and_files(tmp_path)
assert directory == tmp_path
# Should find tif/tiff but not csv
file_names = [f.name for f in files]
- assert "image1.tif" in file_names
- assert "image2.tiff" in file_names
- assert "data.csv" not in file_names
+ assert 'image1.tif' in file_names
+ assert 'image2.tiff' in file_names
+ assert 'data.csv' not in file_names
def test_custom_pattern(self, tmp_path):
"""Test with custom file pattern."""
- (tmp_path / "data1.csv").write_text("a,b")
- (tmp_path / "data2.csv").write_text("c,d")
- (tmp_path / "image.tif").write_bytes(b"fake")
+ (tmp_path / 'data1.csv').write_text('a,b')
+ (tmp_path / 'data2.csv').write_text('c,d')
+ (tmp_path / 'image.tif').write_bytes(b'fake')
- directory, files = get_directory_and_files(tmp_path, pattern="csv")
+ directory, files = get_directory_and_files(tmp_path, pattern='csv')
assert directory == tmp_path
assert len(files) == 2
- assert all(f.suffix == ".csv" for f in files)
+ assert all(f.suffix == '.csv' for f in files)
def test_none_directory(self):
"""Test with None directory returns empty results."""
@@ -144,7 +144,7 @@ def test_none_directory(self):
def test_nonexistent_directory(self, tmp_path):
"""Test that nonexistent directory raises FileNotFoundError."""
- nonexistent = tmp_path / "does_not_exist"
+ nonexistent = tmp_path / 'does_not_exist'
with pytest.raises(FileNotFoundError):
get_directory_and_files(nonexistent)
@@ -159,19 +159,19 @@ def test_3d_image(self):
img = nImage(data)
dims = get_squeezed_dim_order(img)
# Should return ZYX (C is skipped by default, T=1 is squeezed)
- assert "Z" in dims
- assert "Y" in dims
- assert "X" in dims
- assert "C" not in dims
- assert "T" not in dims
+ assert 'Z' in dims
+ assert 'Y' in dims
+ assert 'X' in dims
+ assert 'C' not in dims
+ assert 'T' not in dims
def test_2d_image(self):
"""Test squeezed dims for 2D image."""
data = np.random.random((10, 10))
img = nImage(data)
dims = get_squeezed_dim_order(img)
- assert "Y" in dims
- assert "X" in dims
+ assert 'Y' in dims
+ assert 'X' in dims
class TestElideString:
@@ -179,32 +179,32 @@ class TestElideString:
def test_short_string_unchanged(self):
"""Test that short strings are not modified."""
- assert elide_string("short", 10) == "short"
- assert elide_string("short", 6) == "short"
+ assert elide_string('short', 10) == 'short'
+ assert elide_string('short', 6) == 'short'
def test_exact_length_unchanged(self):
"""Test that strings at max length are not modified."""
- assert elide_string("exactly15chars", 15) == "exactly15chars"
+ assert elide_string('exactly15chars', 15) == 'exactly15chars'
def test_middle_elision(self):
"""Test middle elision (default)."""
- assert elide_string("thisisaverylongstring", 10) == "thi...ing"
- assert elide_string("thisisaverylongstring", 15) == "thisis...string"
+ assert elide_string('thisisaverylongstring', 10) == 'thi...ing'
+ assert elide_string('thisisaverylongstring', 15) == 'thisis...string'
def test_start_elision(self):
"""Test start elision."""
assert (
- elide_string("thisisaverylongstring", 10, "start") == "...gstring"
+ elide_string('thisisaverylongstring', 10, 'start') == '...gstring'
)
def test_end_elision(self):
"""Test end elision."""
- assert elide_string("thisisaverylongstring", 10, "end") == "thisisa..."
+ assert elide_string('thisisaverylongstring', 10, 'end') == 'thisisa...'
def test_very_small_max_length(self):
"""Test with max_length <= 5 truncates without ellipsis."""
- assert elide_string("thisisaverylongstring", 3) == "thi"
- assert elide_string("thisisaverylongstring", 5) == "thisi"
+ assert elide_string('thisisaverylongstring', 3) == 'thi'
+ assert elide_string('thisisaverylongstring', 5) == 'thisi'
def test_invalid_location(self):
"""Test that invalid location raises ValueError."""
@@ -212,10 +212,10 @@ def test_invalid_location(self):
ValueError,
match='Invalid location. Must be "start", "middle", or "end".',
):
- elide_string("thisisaverylongstring", 10, "invalid")
+ elide_string('thisisaverylongstring', 10, 'invalid')
def test_edge_cases(self):
"""Test edge cases."""
- assert elide_string("", 10) == ""
- assert elide_string("a", 1) == "a"
- assert elide_string("ab", 1) == "a"
+ assert elide_string('', 10) == ''
+ assert elide_string('a', 1) == 'a'
+ assert elide_string('ab', 1) == 'a'
diff --git a/tests/test_napari_reader.py b/tests/test_napari_reader.py
index 9a400ec..f2611e3 100644
--- a/tests/test_napari_reader.py
+++ b/tests/test_napari_reader.py
@@ -13,11 +13,11 @@
###############################################################################
-RGB_TIFF = "RGB_bad_metadata.tiff" # has two scenes
-MULTISCENE_CZI = r"0T-4C-0Z-7pos.czi"
-PNG_FILE = "nDev-logo-small.png"
-ND2_FILE = "ND2_dims_rgb.nd2"
-OME_TIFF = "cells3d2ch_legacy.tiff"
+RGB_TIFF = 'RGB_bad_metadata.tiff' # has two scenes
+MULTISCENE_CZI = r'0T-4C-0Z-7pos.czi'
+PNG_FILE = 'nDev-logo-small.png'
+ND2_FILE = 'ND2_dims_rgb.nd2'
+OME_TIFF = 'cells3d2ch_legacy.tiff'
###############################################################################
@@ -32,7 +32,7 @@ def test_napari_viewer_open(resources_dir: Path, make_napari_viewer) -> None:
is shimmed to DirectoryStore with a compatibility patch in nImage.
"""
viewer = make_napari_viewer()
- viewer.open(str(resources_dir / OME_TIFF), plugin="ndevio")
+ viewer.open(str(resources_dir / OME_TIFF), plugin='ndevio')
# Now channels are split into separate layers, so we should have 2 layers
assert len(viewer.layers) == 2
@@ -41,7 +41,7 @@ def test_napari_viewer_open(resources_dir: Path, make_napari_viewer) -> None:
@pytest.mark.parametrize(
- ("in_memory", "expected_dtype"),
+ ('in_memory', 'expected_dtype'),
[
(True, np.ndarray),
(False, da.core.Array),
@@ -49,10 +49,10 @@ def test_napari_viewer_open(resources_dir: Path, make_napari_viewer) -> None:
)
@pytest.mark.parametrize(
(
- "filename",
- "expected_shape",
- "expected_has_scale",
- "expected_num_layers",
+ 'filename',
+ 'expected_shape',
+ 'expected_has_scale',
+ 'expected_num_layers',
),
[
# PNG shape is (106, 243, 4) - actual dimensions of nDev-logo-small.png
@@ -101,20 +101,20 @@ def test_reader_supported_formats(
assert data.shape == expected_shape
# Check meta has expected keys
- assert "name" in meta
+ assert 'name' in meta
if expected_has_scale:
- assert "scale" in meta
+ assert 'scale' in meta
@pytest.mark.parametrize(
- ("in_memory", "expected_dtype"),
+ ('in_memory', 'expected_dtype'),
[
(True, np.ndarray),
(False, da.core.Array),
],
)
@pytest.mark.parametrize(
- ("filename", "expected_shape", "should_work"),
+ ('filename', 'expected_shape', 'should_work'),
[
# RGB_TIFF should work now that bioio-tifffile is a core dependency
(RGB_TIFF, (1440, 1920, 3), True),
@@ -158,7 +158,7 @@ def test_for_multiscene_widget(
if len(viewer.window._dock_widgets) != 0:
# Get the second scene
scene_widget = (
- viewer.window._dock_widgets[f"{Path(filename).stem} :: Scenes"]
+ viewer.window._dock_widgets[f'{Path(filename).stem} :: Scenes']
.widget()
._magic_widget
)
@@ -202,9 +202,9 @@ def test_napari_get_reader_ome_override(resources_dir: Path) -> None:
def test_napari_get_reader_unsupported(resources_dir: Path):
"""Test that unsupported file extension returns None per napari reader spec."""
# Mock the widget opener since we don't have a viewer in this test
- with patch("ndevio._napari_reader._open_plugin_installer") as mock_opener:
+ with patch('ndevio._napari_reader._open_plugin_installer') as mock_opener:
reader = napari_get_reader(
- str(resources_dir / "measure_props_Labels.abcdefg"),
+ str(resources_dir / 'measure_props_Labels.abcdefg'),
)
# Should return None for unsupported formats (per napari spec)
@@ -215,22 +215,22 @@ def test_napari_get_reader_unsupported(resources_dir: Path):
# Check the error message contains the extension
error_arg = mock_opener.call_args[0][1]
error_msg = str(error_arg)
- assert ".abcdefg" in error_msg or "abcdefg" in error_msg
+ assert '.abcdefg' in error_msg or 'abcdefg' in error_msg
def test_napari_get_reader_general_exception(caplog):
"""Test that general exceptions in determine_reader_plugin are handled correctly."""
- test_path = "non_existent_file.xyz"
+ test_path = 'non_existent_file.xyz'
# Mock determine_reader_plugin to raise an exception
- with patch("ndevio._napari_reader.determine_reader_plugin") as mock_reader:
- mock_reader.side_effect = Exception("Test exception")
+ with patch('ndevio._napari_reader.determine_reader_plugin') as mock_reader:
+ mock_reader.side_effect = Exception('Test exception')
reader = napari_get_reader(test_path)
assert reader is None
- assert "ndevio: Error reading file" in caplog.text
- assert "Test exception" in caplog.text
+ assert 'ndevio: Error reading file' in caplog.text
+ assert 'Test exception' in caplog.text
def test_napari_get_reader_png(resources_dir: Path) -> None:
@@ -262,9 +262,9 @@ def test_napari_get_reader_supported_formats_work(resources_dir: Path):
@pytest.mark.parametrize(
- ("filename", "expected_plugin_in_error"),
+ ('filename', 'expected_plugin_in_error'),
[
- (ND2_FILE, "bioio-nd2"), # ND2 needs bioio-nd2
+ (ND2_FILE, 'bioio-nd2'), # ND2 needs bioio-nd2
],
)
def test_napari_get_reader_unsupported_formats_helpful_errors(
@@ -276,7 +276,7 @@ def test_napari_get_reader_unsupported_formats_helpful_errors(
The plugin installer widget should be shown via settings if enabled.
"""
# Mock the widget opener since we don't have a viewer in this test
- with patch("ndevio._napari_reader._open_plugin_installer") as mock_opener:
+ with patch('ndevio._napari_reader._open_plugin_installer') as mock_opener:
reader = napari_get_reader(str(resources_dir / filename))
# Should return None for unsupported formats (per napari spec)
@@ -289,4 +289,4 @@ def test_napari_get_reader_unsupported_formats_helpful_errors(
error_arg = call_args[0][1] # Second argument is the exception
error_msg = str(error_arg)
assert expected_plugin_in_error in error_msg
- assert "pip install" in error_msg or "conda install" in error_msg
+ assert 'pip install' in error_msg or 'conda install' in error_msg
diff --git a/tests/test_nimage.py b/tests/test_nimage.py
index 9c86459..b4b5a46 100644
--- a/tests/test_nimage.py
+++ b/tests/test_nimage.py
@@ -17,12 +17,12 @@
from ndevio.nimage import determine_reader_plugin
RGB_TIFF = (
- "RGB_bad_metadata.tiff" # has two scenes, with really difficult metadata
+ 'RGB_bad_metadata.tiff' # has two scenes, with really difficult metadata
)
-CELLS3D2CH_OME_TIFF = "cells3d2ch_legacy.tiff" # 2 channel, 3D OME-TIFF, from old napari-ndev saving
-LOGO_PNG = "nDev-logo-small.png" # small PNG file (fix typo)
-CZI_FILE = "0T-4C-0Z-7pos.czi" # multi-scene CZI file
-ND2_FILE = "ND2_dims_rgb.nd2" # ND2 file requiring bioio-nd2
+CELLS3D2CH_OME_TIFF = 'cells3d2ch_legacy.tiff' # 2 channel, 3D OME-TIFF, from old napari-ndev saving
+LOGO_PNG = 'nDev-logo-small.png' # small PNG file (fix typo)
+CZI_FILE = '0T-4C-0Z-7pos.czi' # multi-scene CZI file
+ND2_FILE = 'ND2_dims_rgb.nd2' # ND2 file requiring bioio-nd2
def test_nImage_init(resources_dir: Path):
@@ -52,24 +52,24 @@ def test_nImage_ome_reader(resources_dir: Path):
img_path = resources_dir / CELLS3D2CH_OME_TIFF
nimg = nImage(img_path)
- assert nimg.settings.ndevio_reader.preferred_reader == "bioio-ome-tiff"
+ assert nimg.settings.ndevio_reader.preferred_reader == 'bioio-ome-tiff'
# the below only exists if 'bioio-ome-tiff' is used
- assert hasattr(nimg, "ome_metadata")
- assert nimg.channel_names == ["membrane", "nuclei"]
+ assert hasattr(nimg, 'ome_metadata')
+ assert nimg.channel_names == ['membrane', 'nuclei']
# Additional check that the reader override works when bioio_tifffile is
# available. The project does not require bioio_tifffile as a test
# dependency, so skip this part when it's missing.
if bioio_tifffile is None: # pragma: no cover - optional
pytest.skip(
- "bioio_tifffile not installed; skipping reader-override checks"
+ 'bioio_tifffile not installed; skipping reader-override checks'
)
nimg = nImage(img_path, reader=bioio_tifffile.Reader)
# check that despite preferred reader, the reader is still bioio_tifffile
# because there is no ome_metadata
- assert nimg.settings.ndevio_reader.preferred_reader == "bioio-ome-tiff"
+ assert nimg.settings.ndevio_reader.preferred_reader == 'bioio-ome-tiff'
# check that calling nimg.ome_metadata raises NotImplementedError
with pytest.raises(NotImplementedError):
_ = nimg.ome_metadata
@@ -90,23 +90,23 @@ def test_nImage_save_read(resources_dir: Path, tmp_path: Path):
img = nImage(resources_dir / CELLS3D2CH_OME_TIFF)
assert img.physical_pixel_sizes.X == 1
- img_data = img.get_image_data("CZYX")
+ img_data = img.get_image_data('CZYX')
OmeTiffWriter.save(
img_data,
- tmp_path / "test_save_read.tiff",
- dim_order="CZYX",
+ tmp_path / 'test_save_read.tiff',
+ dim_order='CZYX',
physical_pixel_sizes=PhysicalPixelSizes(1, 2, 3), # ZYX
- channel_names=["test1", "test2"],
+ channel_names=['test1', 'test2'],
)
- assert (tmp_path / "test_save_read.tiff").exists()
+ assert (tmp_path / 'test_save_read.tiff').exists()
- new_img = nImage(tmp_path / "test_save_read.tiff")
+ new_img = nImage(tmp_path / 'test_save_read.tiff')
# having the below features means it is properly read as OME-TIFF
assert new_img.physical_pixel_sizes.Z == 1
assert new_img.physical_pixel_sizes.Y == 2
assert new_img.physical_pixel_sizes.X == 3
- assert new_img.channel_names == ["test1", "test2"]
+ assert new_img.channel_names == ['test1', 'test2']
def test_determine_in_memory(resources_dir: Path):
@@ -120,11 +120,11 @@ def test_nImage_determine_in_memory_large_file(resources_dir: Path):
img = nImage(resources_dir / CELLS3D2CH_OME_TIFF)
with (
mock.patch(
- "psutil.virtual_memory", return_value=mock.Mock(available=1e9)
+ 'psutil.virtual_memory', return_value=mock.Mock(available=1e9)
),
mock.patch(
- "bioio_base.io.pathlike_to_fs",
- return_value=(mock.Mock(size=lambda x: 5e9), ""),
+ 'bioio_base.io.pathlike_to_fs',
+ return_value=(mock.Mock(size=lambda x: 5e9), ''),
),
):
assert img._determine_in_memory() is False
@@ -137,7 +137,7 @@ def test_get_layer_data(resources_dir: Path):
# napari_layer_data will be squeezed
# Original shape (1, 2, 60, 66, 85) -> (2, 60, 66, 85)
assert img.napari_layer_data.shape == (2, 60, 66, 85)
- assert img.napari_layer_data.dims == ("C", "Z", "Y", "X")
+ assert img.napari_layer_data.dims == ('C', 'Z', 'Y', 'X')
def test_get_layer_data_not_in_memory(resources_dir: Path):
@@ -158,9 +158,9 @@ def test_get_layer_data_tuples_basic(resources_dir: Path):
# With 2 channels, should get 2 tuples (one per channel)
assert len(layer_tuples) == 2
for _data, meta, layer_type in layer_tuples:
- assert "cells3d2ch_legacy" in meta["name"]
- assert meta["scale"] is not None
- assert layer_type == "image" # default layer type
+ assert 'cells3d2ch_legacy' in meta['name']
+ assert meta['scale'] is not None
+ assert layer_type == 'image' # default layer type
def test_get_layer_data_tuples_ome_validation_error_logged(
@@ -179,9 +179,9 @@ def test_get_layer_data_tuples_ome_validation_error_logged(
# Mock ome_metadata to raise a ValidationError (which inherits from ValueError)
with mock.patch.object(
type(img),
- "ome_metadata",
+ 'ome_metadata',
new_callable=mock.PropertyMock,
- side_effect=ValueError("Invalid acquisition_mode: LatticeLightsheet"),
+ side_effect=ValueError('Invalid acquisition_mode: LatticeLightsheet'),
):
caplog.clear()
layer_tuples = img.get_layer_data_tuples()
@@ -192,22 +192,22 @@ def test_get_layer_data_tuples_ome_validation_error_logged(
# Check that metadata dict exists in each tuple
for _, meta, _ in layer_tuples:
- assert "name" in meta
- assert "metadata" in meta
+ assert 'name' in meta
+ assert 'metadata' in meta
# ome_metadata should NOT be in the nested metadata dict
- assert "ome_metadata" not in meta["metadata"]
+ assert 'ome_metadata' not in meta['metadata']
# raw_image_metadata should still be available
- assert "raw_image_metadata" in meta["metadata"]
+ assert 'raw_image_metadata' in meta['metadata']
# Warning should be logged
assert len(caplog.records) == 1
- assert caplog.records[0].levelname == "WARNING"
- assert "Could not parse OME metadata" in caplog.records[0].message
- assert "LatticeLightsheet" in caplog.records[0].message
+ assert caplog.records[0].levelname == 'WARNING'
+ assert 'Could not parse OME metadata' in caplog.records[0].message
+ assert 'LatticeLightsheet' in caplog.records[0].message
assert len(caplog.records) == 1
- assert caplog.records[0].levelname == "WARNING"
- assert "Could not parse OME metadata" in caplog.records[0].message
- assert "LatticeLightsheet" in caplog.records[0].message
+ assert caplog.records[0].levelname == 'WARNING'
+ assert 'Could not parse OME metadata' in caplog.records[0].message
+ assert 'LatticeLightsheet' in caplog.records[0].message
def test_get_layer_data_tuples_ome_not_implemented_silent(
@@ -224,10 +224,10 @@ def test_get_layer_data_tuples_ome_not_implemented_silent(
# Mock ome_metadata to raise NotImplementedError
with mock.patch.object(
type(img),
- "ome_metadata",
+ 'ome_metadata',
new_callable=mock.PropertyMock,
side_effect=NotImplementedError(
- "Reader does not support OME metadata"
+ 'Reader does not support OME metadata'
),
):
caplog.clear()
@@ -238,7 +238,7 @@ def test_get_layer_data_tuples_ome_not_implemented_silent(
assert len(layer_tuples) > 0
for _, meta, _ in layer_tuples:
- assert "ome_metadata" not in meta["metadata"]
+ assert 'ome_metadata' not in meta['metadata']
# No warning should be logged for NotImplementedError
assert len(caplog.records) == 0
@@ -249,7 +249,7 @@ def test_get_layer_data_mosaic_tile_in_memory(resources_dir: Path):
import xarray as xr
from bioio_base.dimensions import DimensionNames
- with mock.patch.object(nImage, "reader", create=True) as mock_reader:
+ with mock.patch.object(nImage, 'reader', create=True) as mock_reader:
mock_reader.dims.order = [DimensionNames.MosaicTile]
mock_reader.mosaic_xarray_data.squeeze.return_value = xr.DataArray(
[1, 2, 3]
@@ -267,7 +267,7 @@ def test_get_layer_data_mosaic_tile_not_in_memory(
import xarray as xr
from bioio_base.dimensions import DimensionNames
- with mock.patch.object(nImage, "reader", create=True) as mock_reader:
+ with mock.patch.object(nImage, 'reader', create=True) as mock_reader:
mock_reader.dims.order = [DimensionNames.MosaicTile]
mock_reader.mosaic_xarray_dask_data.squeeze.return_value = (
xr.DataArray([1, 2, 3])
@@ -279,7 +279,7 @@ def test_get_layer_data_mosaic_tile_not_in_memory(
@pytest.mark.parametrize(
- ("filename", "should_work", "expected_plugin_suggestion"),
+ ('filename', 'should_work', 'expected_plugin_suggestion'),
[
(LOGO_PNG, True, None), # PNG works with bioio-imageio (core)
(
@@ -288,7 +288,7 @@ def test_get_layer_data_mosaic_tile_not_in_memory(
None,
), # OME-TIFF works with bioio-ome-tiff (core)
(CZI_FILE, True, None),
- (ND2_FILE, False, "bioio-nd2"), # ND2 needs bioio-nd2
+ (ND2_FILE, False, 'bioio-nd2'), # ND2 needs bioio-nd2
(RGB_TIFF, True, None),
],
)
@@ -322,7 +322,7 @@ def test_determine_reader_plugin_behavior(
assert filename in error_msg
if expected_plugin_suggestion:
assert expected_plugin_suggestion in error_msg
- assert "pip install" in error_msg
+ assert 'pip install' in error_msg
else: # "maybe"
# Can succeed or fail; if fails, check for helpful message
try:
@@ -332,16 +332,16 @@ def test_determine_reader_plugin_behavior(
error_msg = str(e)
if expected_plugin_suggestion:
assert expected_plugin_suggestion in error_msg
- assert "pip install" in error_msg
+ assert 'pip install' in error_msg
@pytest.mark.parametrize(
- ("filename", "should_work", "expected_error_contains"),
+ ('filename', 'should_work', 'expected_error_contains'),
[
(LOGO_PNG, True, None),
(CELLS3D2CH_OME_TIFF, True, None),
(CZI_FILE, True, None),
- (ND2_FILE, False, ["bioio-nd2", "pip install"]),
+ (ND2_FILE, False, ['bioio-nd2', 'pip install']),
(RGB_TIFF, True, None),
],
)
@@ -404,16 +404,16 @@ def test_multichannel_returns_tuple_per_channel(self, resources_dir: Path):
for data, meta, layer_type in layer_tuples:
# channel_axis should NOT be in metadata (we split ourselves)
- assert "channel_axis" not in meta
+ assert 'channel_axis' not in meta
# name should be a string (not a list)
- assert isinstance(meta["name"], str)
+ assert isinstance(meta['name'], str)
# Data shape should NOT include channel dimension
assert data.shape == (60, 66, 85) # ZYX only
# Default layer type is "image" (channel names don't match label keywords)
- assert layer_type == "image"
+ assert layer_type == 'image'
def test_layer_names_include_channel_names(self, resources_dir: Path):
"""Test that layer names include channel names from the file."""
@@ -421,11 +421,11 @@ def test_layer_names_include_channel_names(self, resources_dir: Path):
layer_tuples = img.get_layer_data_tuples()
# Extract names from the tuples
- names = [meta["name"] for _, meta, _ in layer_tuples]
+ names = [meta['name'] for _, meta, _ in layer_tuples]
# Channel names from the file are "membrane" and "nuclei"
- assert "membrane" in names[0]
- assert "nuclei" in names[1]
+ assert 'membrane' in names[0]
+ assert 'nuclei' in names[1]
def test_single_channel_image_returns_single_tuple(
self, resources_dir: Path
@@ -439,8 +439,8 @@ def test_single_channel_image_returns_single_tuple(
assert len(layer_tuples) == 1
data, meta, layer_type = layer_tuples[0]
- assert "channel_axis" not in meta
- assert layer_type == "image"
+ assert 'channel_axis' not in meta
+ assert layer_type == 'image'
def test_scale_preserved_in_tuples(self, resources_dir: Path):
"""Test that scale metadata is preserved in each tuple."""
@@ -449,9 +449,9 @@ def test_scale_preserved_in_tuples(self, resources_dir: Path):
for _, meta, _ in layer_tuples:
# Scale should be preserved in each layer
- assert "scale" in meta
+ assert 'scale' in meta
# Original has physical pixel sizes, so scale should have values
- assert len(meta["scale"]) > 0
+ assert len(meta['scale']) > 0
def test_in_memory_parameter_respected(self, resources_dir: Path):
"""Test that in_memory parameter is passed through correctly."""
@@ -478,11 +478,11 @@ def test_colormap_cycling_for_images(self, resources_dir: Path):
layer_tuples = img.get_layer_data_tuples()
# Extract colormaps from the tuples
- colormaps = [meta.get("colormap") for _, meta, _ in layer_tuples]
+ colormaps = [meta.get('colormap') for _, meta, _ in layer_tuples]
# 2 channels should use MAGENTA_GREEN
- assert colormaps[0] == "magenta"
- assert colormaps[1] == "green"
+ assert colormaps[0] == 'magenta'
+ assert colormaps[1] == 'green'
def test_colormap_single_channel_is_gray(self, resources_dir: Path):
"""Test that single channel images get gray colormap."""
@@ -494,13 +494,13 @@ def test_colormap_single_channel_is_gray(self, resources_dir: Path):
# Mock single channel data (no Channel dimension)
mock_data = xr.DataArray(
np.zeros((10, 10)),
- dims=["Y", "X"],
+ dims=['Y', 'X'],
)
img.napari_layer_data = mock_data
layer_tuples = img.get_layer_data_tuples()
assert len(layer_tuples) == 1
- assert layer_tuples[0][1]["colormap"] == "gray"
+ assert layer_tuples[0][1]['colormap'] == 'gray'
def test_colormap_three_plus_channels_uses_multi_channel_cycle(
self, resources_dir: Path
@@ -517,13 +517,13 @@ def test_colormap_three_plus_channels_uses_multi_channel_cycle(
# Mock 4 channel data
mock_data = xr.DataArray(
np.zeros((4, 10, 10)),
- dims=[DimensionNames.Channel, "Y", "X"],
- coords={DimensionNames.Channel: ["ch0", "ch1", "ch2", "ch3"]},
+ dims=[DimensionNames.Channel, 'Y', 'X'],
+ coords={DimensionNames.Channel: ['ch0', 'ch1', 'ch2', 'ch3']},
)
img.napari_layer_data = mock_data
layer_tuples = img.get_layer_data_tuples()
- colormaps = [meta.get("colormap") for _, meta, _ in layer_tuples]
+ colormaps = [meta.get('colormap') for _, meta, _ in layer_tuples]
# Should cycle through MULTI_CHANNEL_CYCLE (CMYBGR)
assert colormaps[0] == MULTI_CHANNEL_CYCLE[0] # cyan
@@ -542,8 +542,8 @@ def test_auto_detect_labels_from_channel_name(self, resources_dir: Path):
# Mock napari_layer_data with a channel named "mask"
mock_data = xr.DataArray(
np.zeros((2, 10, 10)),
- dims=[DimensionNames.Channel, "Y", "X"],
- coords={DimensionNames.Channel: ["intensity", "mask"]},
+ dims=[DimensionNames.Channel, 'Y', 'X'],
+ coords={DimensionNames.Channel: ['intensity', 'mask']},
)
img.napari_layer_data = mock_data
@@ -551,9 +551,9 @@ def test_auto_detect_labels_from_channel_name(self, resources_dir: Path):
layer_tuples = img.get_layer_data_tuples()
# First channel "intensity" should be image
- assert layer_tuples[0][2] == "image"
+ assert layer_tuples[0][2] == 'image'
# Second channel "mask" should be labels (keyword match)
- assert layer_tuples[1][2] == "labels"
+ assert layer_tuples[1][2] == 'labels'
def test_channel_types_override_auto_detection(self, resources_dir: Path):
"""Test that channel_types parameter overrides auto-detection."""
@@ -566,19 +566,19 @@ def test_channel_types_override_auto_detection(self, resources_dir: Path):
# Set up mock data
mock_data = xr.DataArray(
np.zeros((2, 10, 10)),
- dims=[DimensionNames.Channel, "Y", "X"],
- coords={DimensionNames.Channel: ["intensity", "mask"]},
+ dims=[DimensionNames.Channel, 'Y', 'X'],
+ coords={DimensionNames.Channel: ['intensity', 'mask']},
)
img.napari_layer_data = mock_data
# Override: set both channels to labels
layer_tuples = img.get_layer_data_tuples(
- channel_types={"intensity": "labels", "mask": "labels"}
+ channel_types={'intensity': 'labels', 'mask': 'labels'}
)
# Both should be labels due to override
- assert layer_tuples[0][2] == "labels"
- assert layer_tuples[1][2] == "labels"
+ assert layer_tuples[0][2] == 'labels'
+ assert layer_tuples[1][2] == 'labels'
def test_labels_do_not_get_colormap(self, resources_dir: Path):
"""Test that labels layers don't get colormap metadata."""
@@ -591,29 +591,29 @@ def test_labels_do_not_get_colormap(self, resources_dir: Path):
# Mock data with a labels channel
mock_data = xr.DataArray(
np.zeros((1, 10, 10)),
- dims=[DimensionNames.Channel, "Y", "X"],
- coords={DimensionNames.Channel: ["segmentation"]},
+ dims=[DimensionNames.Channel, 'Y', 'X'],
+ coords={DimensionNames.Channel: ['segmentation']},
)
img.napari_layer_data = mock_data
layer_tuples = img.get_layer_data_tuples()
# "segmentation" matches label keyword
- assert layer_tuples[0][2] == "labels"
+ assert layer_tuples[0][2] == 'labels'
# Labels should not have colormap
- assert "colormap" not in layer_tuples[0][1]
+ assert 'colormap' not in layer_tuples[0][1]
def test_layer_type_override_all_channels(self, resources_dir: Path):
"""Test that layer_type parameter overrides all channels."""
img = nImage(resources_dir / CELLS3D2CH_OME_TIFF)
- layer_tuples = img.get_layer_data_tuples(layer_type="labels")
+ layer_tuples = img.get_layer_data_tuples(layer_type='labels')
# All channels should be labels due to override
assert len(layer_tuples) == 2
for _, meta, layer_type in layer_tuples:
- assert layer_type == "labels"
+ assert layer_type == 'labels'
# Labels should not have colormap
- assert "colormap" not in meta
+ assert 'colormap' not in meta
def test_layer_type_overrides_channel_types(self, resources_dir: Path):
"""Test that layer_type takes precedence over channel_types."""
@@ -625,20 +625,20 @@ def test_layer_type_overrides_channel_types(self, resources_dir: Path):
mock_data = xr.DataArray(
np.zeros((2, 10, 10)),
- dims=[DimensionNames.Channel, "Y", "X"],
- coords={DimensionNames.Channel: ["intensity", "mask"]},
+ dims=[DimensionNames.Channel, 'Y', 'X'],
+ coords={DimensionNames.Channel: ['intensity', 'mask']},
)
img.napari_layer_data = mock_data
# Even though channel_types says "intensity" should be image,
# layer_type="labels" should override everything
layer_tuples = img.get_layer_data_tuples(
- layer_type="labels",
- channel_types={"intensity": "image", "mask": "image"},
+ layer_type='labels',
+ channel_types={'intensity': 'image', 'mask': 'image'},
)
# Both should be labels due to layer_type override
- assert layer_tuples[0][2] == "labels"
+ assert layer_tuples[0][2] == 'labels'
def test_channel_kwargs_override_metadata(self, resources_dir: Path):
"""Test that channel_kwargs overrides default metadata."""
@@ -646,21 +646,21 @@ def test_channel_kwargs_override_metadata(self, resources_dir: Path):
layer_tuples = img.get_layer_data_tuples(
channel_kwargs={
img.channel_names[0]: {
- "colormap": "blue",
- "contrast_limits": (0, 1000),
+ 'colormap': 'blue',
+ 'contrast_limits': (0, 1000),
},
img.channel_names[1]: {
- "opacity": 0.5,
+ 'opacity': 0.5,
},
}
)
assert len(layer_tuples) == 2
# First channel should have overridden colormap and contrast_limits
- assert layer_tuples[0][1]["colormap"] == "blue"
- assert layer_tuples[0][1]["contrast_limits"] == (0, 1000)
+ assert layer_tuples[0][1]['colormap'] == 'blue'
+ assert layer_tuples[0][1]['contrast_limits'] == (0, 1000)
# Second channel should have opacity override but default colormap
- assert layer_tuples[1][1]["opacity"] == 0.5
+ assert layer_tuples[1][1]['opacity'] == 0.5
assert (
- layer_tuples[1][1]["colormap"] == "green"
+ layer_tuples[1][1]['colormap'] == 'green'
) # default for 2-channel
diff --git a/tests/test_plugin_installer.py b/tests/test_plugin_installer.py
index d4b4c64..6a31ad4 100644
--- a/tests/test_plugin_installer.py
+++ b/tests/test_plugin_installer.py
@@ -10,52 +10,52 @@ def test_czi_file_no_plugins_installed(self):
"""Test that CZI file suggests bioio-czi plugin when nothing installed."""
from ndevio._plugin_manager import ReaderPluginManager
- with patch("bioio.plugin_feasibility_report") as mock_report:
- mock_report.return_value = {"ArrayLike": Mock(supported=False)}
+ with patch('bioio.plugin_feasibility_report') as mock_report:
+ mock_report.return_value = {'ArrayLike': Mock(supported=False)}
- manager = ReaderPluginManager("test.czi")
+ manager = ReaderPluginManager('test.czi')
plugins = manager.installable_plugins
assert len(plugins) == 1
- assert plugins[0] == "bioio-czi"
+ assert plugins[0] == 'bioio-czi'
def test_lif_file_no_plugins_installed(self):
"""Test that LIF file suggests bioio-lif plugin."""
from ndevio._plugin_manager import ReaderPluginManager
- with patch("bioio.plugin_feasibility_report") as mock_report:
- mock_report.return_value = {"ArrayLike": Mock(supported=False)}
+ with patch('bioio.plugin_feasibility_report') as mock_report:
+ mock_report.return_value = {'ArrayLike': Mock(supported=False)}
- manager = ReaderPluginManager("test.lif")
+ manager = ReaderPluginManager('test.lif')
plugins = manager.installable_plugins
assert len(plugins) == 1
- assert plugins[0] == "bioio-lif"
+ assert plugins[0] == 'bioio-lif'
def test_tiff_file_suggests_non_core_only(self):
"""Test that TIFF files only suggest non-core plugins."""
from ndevio._plugin_manager import ReaderPluginManager
- with patch("bioio.plugin_feasibility_report") as mock_report:
- mock_report.return_value = {"ArrayLike": Mock(supported=False)}
+ with patch('bioio.plugin_feasibility_report') as mock_report:
+ mock_report.return_value = {'ArrayLike': Mock(supported=False)}
- manager = ReaderPluginManager("test.tiff")
+ manager = ReaderPluginManager('test.tiff')
plugins = manager.installable_plugins
# Should only get bioio-tiff-glob (non-core)
# bioio-ome-tiff and bioio-tifffile are core and shouldn't be suggested
- assert "bioio-tiff-glob" in plugins
- assert "bioio-ome-tiff" not in plugins
- assert "bioio-tifffile" not in plugins
+ assert 'bioio-tiff-glob' in plugins
+ assert 'bioio-ome-tiff' not in plugins
+ assert 'bioio-tifffile' not in plugins
def test_no_plugins_for_unsupported_extension(self):
"""Test that unsupported extensions return empty list."""
from ndevio._plugin_manager import ReaderPluginManager
- with patch("bioio.plugin_feasibility_report") as mock_report:
- mock_report.return_value = {"ArrayLike": Mock(supported=False)}
+ with patch('bioio.plugin_feasibility_report') as mock_report:
+ mock_report.return_value = {'ArrayLike': Mock(supported=False)}
- manager = ReaderPluginManager("test.xyz")
+ manager = ReaderPluginManager('test.xyz')
plugins = manager.installable_plugins
assert len(plugins) == 0
@@ -65,13 +65,13 @@ def test_filters_installed_plugins(self):
from ndevio._plugin_manager import ReaderPluginManager
# Mock feasibility report showing bioio-czi as installed
- with patch("bioio.plugin_feasibility_report") as mock_report:
+ with patch('bioio.plugin_feasibility_report') as mock_report:
mock_report.return_value = {
- "bioio-czi": Mock(supported=True),
- "ArrayLike": Mock(supported=False),
+ 'bioio-czi': Mock(supported=True),
+ 'ArrayLike': Mock(supported=False),
}
- manager = ReaderPluginManager("test.czi")
+ manager = ReaderPluginManager('test.czi')
plugins = manager.installable_plugins
# bioio-czi should be filtered out since it's "installed"
@@ -94,7 +94,7 @@ def test_standalone_mode(self, make_napari_viewer):
assert widget.manager.path is None
# Title should be standalone message
- assert "Install BioIO Reader Plugin" in widget._title_label.value
+ assert 'Install BioIO Reader Plugin' in widget._title_label.value
# No path, so no pre-selection
assert (
@@ -107,11 +107,11 @@ def test_error_mode_with_installable_plugins(self, make_napari_viewer):
from ndevio._plugin_manager import ReaderPluginManager
from ndevio.widgets import PluginInstallerWidget
- with patch("bioio.plugin_feasibility_report") as mock_report:
+ with patch('bioio.plugin_feasibility_report') as mock_report:
# Mock report showing no plugins installed
- mock_report.return_value = {"ArrayLike": Mock(supported=False)}
+ mock_report.return_value = {'ArrayLike': Mock(supported=False)}
- manager = ReaderPluginManager("test.czi")
+ manager = ReaderPluginManager('test.czi')
widget = PluginInstallerWidget(plugin_manager=manager)
# Should have ALL plugins available
@@ -120,31 +120,31 @@ def test_error_mode_with_installable_plugins(self, make_napari_viewer):
# Should have installable plugins
installable = widget.manager.installable_plugins
assert len(installable) > 0
- assert "bioio-czi" in installable
+ assert 'bioio-czi' in installable
# First installable plugin should be pre-selected
assert widget._plugin_select.value == installable[0]
# Should have path
assert widget.manager.path is not None
- assert widget.manager.path.name == "test.czi"
+ assert widget.manager.path.name == 'test.czi'
# Title should show filename
- assert "test.czi" in widget._title_label.value
+ assert 'test.czi' in widget._title_label.value
def test_error_mode_no_installable_plugins(self, make_napari_viewer):
"""Test widget in error mode without installable plugins."""
from ndevio._plugin_manager import ReaderPluginManager
from ndevio.widgets import PluginInstallerWidget
- with patch("bioio.plugin_feasibility_report") as mock_report:
+ with patch('bioio.plugin_feasibility_report') as mock_report:
# Mock report showing all suggested plugins already installed
mock_report.return_value = {
- "bioio-imageio": Mock(supported=False), # for .xyz files
- "ArrayLike": Mock(supported=False),
+ 'bioio-imageio': Mock(supported=False), # for .xyz files
+ 'ArrayLike': Mock(supported=False),
}
- manager = ReaderPluginManager("test.png")
+ manager = ReaderPluginManager('test.png')
widget = PluginInstallerWidget(plugin_manager=manager)
# Should still have ALL plugins available
@@ -173,7 +173,7 @@ def test_returns_job_id(self):
from ndevio._plugin_installer import install_plugin
# This will queue the installation but not actually run it
- job_id = install_plugin("bioio-imageio")
+ job_id = install_plugin('bioio-imageio')
# Job ID should be an integer
assert isinstance(job_id, int)
@@ -199,7 +199,7 @@ def on_finished(event):
# Check that we got a completion event
assert len(completed) > 0
- assert "bioio-imageio" in completed[0].get("pkgs", [])
+ assert 'bioio-imageio' in completed[0].get('pkgs', [])
class TestVerifyPluginInstalled:
@@ -210,14 +210,14 @@ def test_verify_installed_plugin(self):
from ndevio._plugin_installer import verify_plugin_installed
# bioio should be installed since it's a dependency
- assert verify_plugin_installed("bioio")
+ assert verify_plugin_installed('bioio')
def test_verify_not_installed_plugin(self):
"""Test verification of a plugin that isn't installed."""
from ndevio._plugin_installer import verify_plugin_installed
# Use a plugin that definitely won't be installed
- assert not verify_plugin_installed("bioio-nonexistent-plugin-12345")
+ assert not verify_plugin_installed('bioio-nonexistent-plugin-12345')
def test_verify_converts_name_format(self):
"""Test that plugin name is correctly converted to module name."""
@@ -225,7 +225,7 @@ def test_verify_converts_name_format(self):
# Test with installed package (bioio-base should be installed)
# The function should convert bioio-base -> bioio_base
- result = verify_plugin_installed("bioio-base")
+ result = verify_plugin_installed('bioio-base')
assert isinstance(result, bool)
diff --git a/tests/test_plugin_installer_integration.py b/tests/test_plugin_installer_integration.py
index 2e6f758..daec073 100644
--- a/tests/test_plugin_installer_integration.py
+++ b/tests/test_plugin_installer_integration.py
@@ -15,16 +15,16 @@ def test_opens_widget_with_viewer(self, make_napari_viewer):
import ndevio._napari_reader as reader_module
viewer = make_napari_viewer()
- test_path = "test.czi"
+ test_path = 'test.czi'
error = UnsupportedFileFormatError(
- reader_name="test", path=test_path, msg_extra=""
+ reader_name='test', path=test_path, msg_extra=''
)
# Mock plugin_feasibility_report from bioio (not ndevio)
- with patch("bioio.plugin_feasibility_report") as mock_report:
+ with patch('bioio.plugin_feasibility_report') as mock_report:
mock_report.return_value = {
- "bioio-ome-tiff": Mock(supported=False),
- "ArrayLike": Mock(supported=False),
+ 'bioio-ome-tiff': Mock(supported=False),
+ 'ArrayLike': Mock(supported=False),
}
# Call the function
@@ -36,7 +36,7 @@ def test_opens_widget_with_viewer(self, make_napari_viewer):
# Find the plugin installer widget
plugin_widget = None
for name, widget in viewer.window.dock_widgets.items():
- if "Install BioIO Plugin" in name:
+ if 'Install BioIO Plugin' in name:
plugin_widget = widget
break
@@ -49,12 +49,12 @@ def test_widget_has_correct_path(self, make_napari_viewer):
import ndevio._napari_reader as reader_module
viewer = make_napari_viewer()
- test_path = Path("path/to/test.czi")
+ test_path = Path('path/to/test.czi')
error = UnsupportedFileFormatError(
- reader_name="test", path=str(test_path), msg_extra=""
+ reader_name='test', path=str(test_path), msg_extra=''
)
- with patch("bioio.plugin_feasibility_report") as mock_report:
+ with patch('bioio.plugin_feasibility_report') as mock_report:
mock_report.return_value = {}
reader_module._open_plugin_installer(test_path, error)
@@ -63,7 +63,7 @@ def test_widget_has_correct_path(self, make_napari_viewer):
# Find the plugin installer widget by name
widget = None
for name, docked_widget in viewer.window.dock_widgets.items():
- if "Install BioIO Plugin" in name:
+ if 'Install BioIO Plugin' in name:
widget = docked_widget
break
@@ -80,18 +80,18 @@ def test_filters_installed_plugins(self, make_napari_viewer):
import ndevio._napari_reader as reader_module
viewer = make_napari_viewer()
- test_path = "test.czi"
+ test_path = 'test.czi'
error = UnsupportedFileFormatError(
- reader_name="test", path=test_path, msg_extra=""
+ reader_name='test', path=test_path, msg_extra=''
)
# Mock feasibility report showing bioio-czi as installed
- with patch("bioio.plugin_feasibility_report") as mock_report:
+ with patch('bioio.plugin_feasibility_report') as mock_report:
mock_report.return_value = {
- "bioio-czi": Mock(
+ 'bioio-czi': Mock(
supported=False
), # Installed but can't read this file
- "ArrayLike": Mock(supported=False),
+ 'ArrayLike': Mock(supported=False),
}
reader_module._open_plugin_installer(test_path, error)
@@ -100,7 +100,7 @@ def test_filters_installed_plugins(self, make_napari_viewer):
# Find the plugin installer widget by name
widget = None
for name, docked_widget in viewer.window.dock_widgets.items():
- if "Install BioIO Plugin" in name:
+ if 'Install BioIO Plugin' in name:
widget = docked_widget
break
@@ -109,7 +109,7 @@ def test_filters_installed_plugins(self, make_napari_viewer):
# bioio-czi should be filtered out from installable_plugins
installable = widget.manager.installable_plugins
if installable:
- assert "bioio-czi" not in installable
+ assert 'bioio-czi' not in installable
def test_suggests_uninstalled_plugins(self, make_napari_viewer):
"""Test that uninstalled plugins are suggested."""
@@ -118,16 +118,16 @@ def test_suggests_uninstalled_plugins(self, make_napari_viewer):
import ndevio._napari_reader as reader_module
viewer = make_napari_viewer()
- test_path = "test.lif" # LIF files need bioio-lif
+ test_path = 'test.lif' # LIF files need bioio-lif
error = UnsupportedFileFormatError(
- reader_name="test", path=test_path, msg_extra=""
+ reader_name='test', path=test_path, msg_extra=''
)
# Mock feasibility report with no bioio-lif installed
- with patch("bioio.plugin_feasibility_report") as mock_report:
+ with patch('bioio.plugin_feasibility_report') as mock_report:
mock_report.return_value = {
- "bioio-ome-tiff": Mock(supported=False),
- "ArrayLike": Mock(supported=False),
+ 'bioio-ome-tiff': Mock(supported=False),
+ 'ArrayLike': Mock(supported=False),
}
reader_module._open_plugin_installer(test_path, error)
@@ -136,7 +136,7 @@ def test_suggests_uninstalled_plugins(self, make_napari_viewer):
# Find the plugin installer widget by name
widget = None
for name, docked_widget in viewer.window.dock_widgets.items():
- if "Install BioIO Plugin" in name:
+ if 'Install BioIO Plugin' in name:
widget = docked_widget
break
@@ -145,7 +145,7 @@ def test_suggests_uninstalled_plugins(self, make_napari_viewer):
# bioio-lif should be in installable_plugins
installable = widget.manager.installable_plugins
assert installable is not None
- assert "bioio-lif" in installable
+ assert 'bioio-lif' in installable
class TestPluginInstallerWidgetIntegration:
@@ -159,24 +159,24 @@ def test_widget_created_in_error_mode(self, make_napari_viewer):
make_napari_viewer() # Create viewer context
# Create manager for a CZI file
- with patch("bioio.plugin_feasibility_report") as mock_report:
+ with patch('bioio.plugin_feasibility_report') as mock_report:
# Mock report showing no plugins installed
mock_report.return_value = {
- "ArrayLike": Mock(supported=False),
+ 'ArrayLike': Mock(supported=False),
}
- manager = ReaderPluginManager("test.czi")
+ manager = ReaderPluginManager('test.czi')
widget = PluginInstallerWidget(plugin_manager=manager)
# Verify widget state
assert widget.manager.path is not None
- assert widget.manager.path.name == "test.czi"
- assert "test.czi" in widget._title_label.value
+ assert widget.manager.path.name == 'test.czi'
+ assert 'test.czi' in widget._title_label.value
# bioio-czi should be in installable plugins and pre-selected
installable = widget.manager.installable_plugins
- assert "bioio-czi" in installable
- assert widget._plugin_select.value == "bioio-czi"
+ assert 'bioio-czi' in installable
+ assert widget._plugin_select.value == 'bioio-czi'
def test_install_button_queues_installation(self, make_napari_viewer):
"""Test that clicking install button queues installation."""
@@ -187,17 +187,17 @@ def test_install_button_queues_installation(self, make_napari_viewer):
widget = PluginInstallerWidget()
# Select a plugin
- widget._plugin_select.value = "bioio-imageio"
+ widget._plugin_select.value = 'bioio-imageio'
# Mock the install_plugin function at the point of import
- with patch("ndevio._plugin_installer.install_plugin") as mock_install:
+ with patch('ndevio._plugin_installer.install_plugin') as mock_install:
mock_install.return_value = 123 # Mock job ID
# Click install button
widget._on_install_clicked()
# Verify install was called with correct plugin
- mock_install.assert_called_once_with("bioio-imageio")
+ mock_install.assert_called_once_with('bioio-imageio')
def test_widget_shows_all_plugins(self, make_napari_viewer):
"""Test that widget shows all available plugins."""
@@ -222,12 +222,12 @@ def test_widget_preselects_first_installable(self, make_napari_viewer):
make_napari_viewer()
# Mock report showing no plugins installed
- with patch("bioio.plugin_feasibility_report") as mock_report:
+ with patch('bioio.plugin_feasibility_report') as mock_report:
mock_report.return_value = {
- "ArrayLike": Mock(supported=False),
+ 'ArrayLike': Mock(supported=False),
}
- manager = ReaderPluginManager("test.lif")
+ manager = ReaderPluginManager('test.lif')
widget = PluginInstallerWidget(plugin_manager=manager)
# bioio-lif should be pre-selected (first installable)
@@ -242,14 +242,14 @@ def test_install_updates_status_label(self, make_napari_viewer):
make_napari_viewer()
widget = PluginInstallerWidget()
- widget._plugin_select.value = "bioio-imageio"
+ widget._plugin_select.value = 'bioio-imageio'
- with patch("ndevio._plugin_installer.install_plugin") as mock_install:
+ with patch('ndevio._plugin_installer.install_plugin') as mock_install:
mock_install.return_value = 123
# Status should update to "Installing..."
widget._on_install_clicked()
- assert "Installing" in widget._status_label.value
+ assert 'Installing' in widget._status_label.value
def test_no_plugin_selected_shows_error(self, make_napari_viewer):
"""Test that clicking install with no selection shows error."""
@@ -262,4 +262,4 @@ def test_no_plugin_selected_shows_error(self, make_napari_viewer):
widget._on_install_clicked()
- assert "No plugin selected" in widget._status_label.value
+ assert 'No plugin selected' in widget._status_label.value
diff --git a/tests/test_sampledata.py b/tests/test_sampledata.py
index 33c9aba..2fde2fb 100644
--- a/tests/test_sampledata.py
+++ b/tests/test_sampledata.py
@@ -37,7 +37,7 @@ def _validate_layer_data_tuples(
data, kwargs, layer_type = layer_tuple
# Data should be array-like with shape
- assert hasattr(data, "shape")
+ assert hasattr(data, 'shape')
assert len(data.shape) >= 2 # At minimum 2D
# kwargs should be a dict
@@ -45,7 +45,7 @@ def _validate_layer_data_tuples(
# layer_type should be a string
assert isinstance(layer_type, str)
- assert layer_type in ("image", "labels")
+ assert layer_type in ('image', 'labels')
if expected_layer_type:
assert layer_type == expected_layer_type
@@ -57,21 +57,21 @@ class TestLocalSampleData:
def test_ndev_logo(self):
"""Test loading ndev logo returns valid LayerDataTuples."""
result = ndev_logo()
- _validate_layer_data_tuples(result, expected_layer_type="image")
+ _validate_layer_data_tuples(result, expected_layer_type='image')
# Logo should be a single image layer
assert len(result) == 1
def test_neuron_labels(self):
"""Test loading neuron labels returns valid LayerDataTuples."""
result = neuron_labels()
- _validate_layer_data_tuples(result, expected_layer_type="labels")
+ _validate_layer_data_tuples(result, expected_layer_type='labels')
# Should have 4 channels as separate label layers
assert len(result) == 4
def test_neuron_labels_processed(self):
"""Test loading processed neuron labels returns valid LayerDataTuples."""
result = neuron_labels_processed()
- _validate_layer_data_tuples(result, expected_layer_type="labels")
+ _validate_layer_data_tuples(result, expected_layer_type='labels')
# Should have 4 channels as separate label layers
assert len(result) == 4
@@ -93,19 +93,19 @@ def test_scratch_assay(self):
assert len(result) == 4
# Check we have both image and labels types
layer_types = [t[2] for t in result]
- assert "image" in layer_types
- assert "labels" in layer_types
+ assert 'image' in layer_types
+ assert 'labels' in layer_types
def test_neocortex(self):
"""Test loading neocortex returns valid LayerDataTuples."""
result = neocortex()
- _validate_layer_data_tuples(result, expected_layer_type="image")
+ _validate_layer_data_tuples(result, expected_layer_type='image')
# Should have 3 channels as separate image layers
assert len(result) == 3
def test_neuron_raw(self):
"""Test loading neuron raw returns valid LayerDataTuples."""
result = neuron_raw()
- _validate_layer_data_tuples(result, expected_layer_type="image")
+ _validate_layer_data_tuples(result, expected_layer_type='image')
# Should have 4 channels as separate image layers
assert len(result) == 4
diff --git a/tests/test_utilities_container.py b/tests/test_utilities_container.py
index 328c02e..d27bfb3 100644
--- a/tests/test_utilities_container.py
+++ b/tests/test_utilities_container.py
@@ -40,8 +40,8 @@
@pytest.fixture(
params=[
- (image_2d, shapes_2d, labels_2d, "YX"),
- (image_4d, shapes_4d, labels_4d, "TZYX"),
+ (image_2d, shapes_2d, labels_2d, 'YX'),
+ (image_4d, shapes_4d, labels_4d, 'TZYX'),
]
)
def test_data(request: pytest.FixtureRequest):
@@ -61,11 +61,11 @@ def test_save_shapes_as_labels(
viewer.add_shapes(test_shape)
container = UtilitiesContainer(viewer)
- container._viewer.layers.selection.active = viewer.layers["test_shape"]
+ container._viewer.layers.selection.active = viewer.layers['test_shape']
container._save_directory.value = tmp_path
- container._save_name.value = "test"
+ container._save_name.value = 'test'
- expected_save_loc = tmp_path / "Shapes" / "test.tiff"
+ expected_save_loc = tmp_path / 'Shapes' / 'test.tiff'
container.save_layers_as_ome_tiff()
# Wait for file to exist (avoids race condition with signal timing)
@@ -74,7 +74,7 @@ def test_save_shapes_as_labels(
assert expected_save_loc.exists()
saved_img = nImage(expected_save_loc)
assert saved_img.shape[1] == 1 # single channel (C dimension is index 1)
- assert saved_img.channel_names == ["Shapes"]
+ assert saved_img.channel_names == ['Shapes']
def test_save_labels(qtbot, make_napari_viewer, tmp_path: Path, test_data):
@@ -84,13 +84,13 @@ def test_save_labels(qtbot, make_napari_viewer, tmp_path: Path, test_data):
viewer.add_labels(
test_labels
) # <- should add a way to specify this is the selected layer in the viewer
- viewer.layers.selection.active = viewer.layers["test_labels"]
+ viewer.layers.selection.active = viewer.layers['test_labels']
container = UtilitiesContainer(viewer)
container._save_directory.value = tmp_path
- container._save_name.value = "test"
+ container._save_name.value = 'test'
- expected_save_loc = tmp_path / "Labels" / "test.tiff"
+ expected_save_loc = tmp_path / 'Labels' / 'test.tiff'
container.save_layers_as_ome_tiff()
# Wait for file to exist (avoids race condition with signal timing)
@@ -99,7 +99,7 @@ def test_save_labels(qtbot, make_napari_viewer, tmp_path: Path, test_data):
assert expected_save_loc.exists()
saved_img = nImage(expected_save_loc)
assert saved_img.shape[1] == 1 # single channel (C dimension is index 1)
- assert saved_img.channel_names == ["Labels"]
+ assert saved_img.channel_names == ['Labels']
def test_save_image_layer(
@@ -110,12 +110,12 @@ def test_save_image_layer(
viewer.add_image(test_image)
container = UtilitiesContainer(viewer)
- container._viewer.layers.selection.active = viewer.layers["test_image"]
- container._channel_names.value = ["0"]
+ container._viewer.layers.selection.active = viewer.layers['test_image']
+ container._channel_names.value = ['0']
container._save_directory.value = tmp_path
- container._save_name.value = "test"
+ container._save_name.value = 'test'
- expected_save_loc = tmp_path / "Image" / "test.tiff"
+ expected_save_loc = tmp_path / 'Image' / 'test.tiff'
container.save_layers_as_ome_tiff()
# Wait for file to exist (avoids race condition with signal timing)
@@ -124,7 +124,7 @@ def test_save_image_layer(
assert expected_save_loc.exists()
saved_img = nImage(expected_save_loc)
assert saved_img.shape[1] == 1 # single channel (C dimension is index 1)
- assert saved_img.channel_names == ["0"]
+ assert saved_img.channel_names == ['0']
def test_save_multi_layer(
@@ -137,13 +137,13 @@ def test_save_multi_layer(
container = UtilitiesContainer(viewer)
container._viewer.layers.selection = [
- viewer.layers["test_labels"],
- viewer.layers["test_image"],
+ viewer.layers['test_labels'],
+ viewer.layers['test_image'],
]
container._save_directory.value = tmp_path
- container._save_name.value = "test"
+ container._save_name.value = 'test'
- expected_save_loc = tmp_path / "Layers" / "test.tiff"
+ expected_save_loc = tmp_path / 'Layers' / 'test.tiff'
container.save_layers_as_ome_tiff()
# Wait for file to exist (avoids race condition with signal timing)
@@ -156,7 +156,7 @@ def test_save_multi_layer(
@pytest.fixture
def test_rgb_image(resources_dir: Path):
- path = resources_dir / "RGB_bad_metadata.tiff"
+ path = resources_dir / 'RGB_bad_metadata.tiff'
img = nImage(path)
return path, img
@@ -169,12 +169,12 @@ def test_update_metadata_from_file(make_napari_viewer, test_rgb_image):
container._files.value = path
container.update_metadata_on_file_select()
- assert container._save_name.value == "RGB_bad_metadata"
+ assert container._save_name.value == 'RGB_bad_metadata'
assert (
container._dim_shape.value
- == "T: 1, C: 1, Z: 1, Y: 1440, X: 1920, S: 3"
+ == 'T: 1, C: 1, Z: 1, Y: 1440, X: 1920, S: 3'
)
- assert container._squeezed_dims_order == "YX"
+ assert container._squeezed_dims_order == 'YX'
assert container._channel_names.value == "['red', 'green', 'blue']"
@@ -184,18 +184,18 @@ def test_update_metadata_from_layer(make_napari_viewer, test_data):
viewer.add_image(test_image, scale=(2, 3))
container = UtilitiesContainer(viewer)
- container._viewer.layers.selection.active = viewer.layers["test_image"]
+ container._viewer.layers.selection.active = viewer.layers['test_image']
container.update_metadata_from_layer()
assert (
- "Tried to update metadata, but could only update scale"
+ 'Tried to update metadata, but could only update scale'
) in container._results.value
assert container._scale_tuple.value == (1, 2, 3)
@pytest.fixture
def test_czi_image(resources_dir: Path):
- path = resources_dir / "0T-4C-0Z-7pos.czi"
+ path = resources_dir / '0T-4C-0Z-7pos.czi'
img = nImage(path)
return path, img
@@ -205,8 +205,8 @@ def test_save_files_as_ome_tiff(test_czi_image, tmp_path: Path, qtbot):
container = UtilitiesContainer()
container._files.value = path
container._save_directory.value = tmp_path
- save_dir = tmp_path / "ConcatenatedImages"
- expected_file = save_dir / "0T-4C-0Z-7pos.tiff"
+ save_dir = tmp_path / 'ConcatenatedImages'
+ expected_file = save_dir / '0T-4C-0Z-7pos.tiff'
container.save_files_as_ome_tiff()
@@ -219,11 +219,11 @@ def test_save_files_as_ome_tiff(test_czi_image, tmp_path: Path, qtbot):
assert expected_file.exists()
-@pytest.mark.parametrize("num_files", [1, 2])
+@pytest.mark.parametrize('num_files', [1, 2])
def test_select_next_images(resources_dir: Path, num_files: int):
container = UtilitiesContainer()
- image_dir = resources_dir / "test_czis"
+ image_dir = resources_dir / 'test_czis'
# get all the files in the directory
all_image_files = list(image_dir.iterdir())
# sort the files
@@ -245,7 +245,7 @@ def test_select_next_images(resources_dir: Path, num_files: int):
def test_batch_concatenate_files(tmp_path: Path, resources_dir: Path, qtbot):
container = UtilitiesContainer()
- image_dir = resources_dir / "test_czis"
+ image_dir = resources_dir / 'test_czis'
all_image_files = list(image_dir.iterdir())
all_image_files = natsort.os_sorted(all_image_files)
@@ -253,11 +253,11 @@ def test_batch_concatenate_files(tmp_path: Path, resources_dir: Path, qtbot):
container._files.value = all_image_files[:1]
container._save_directory.value = tmp_path
- container._save_directory_prefix.value = "test"
+ container._save_directory_prefix.value = 'test'
container.batch_concatenate_files()
# Wait for threaded batch to complete
- expected_output_dir = tmp_path / "test_ConcatenatedImages"
+ expected_output_dir = tmp_path / 'test_ConcatenatedImages'
# 4 tiff files + 1 log file = 5 total
qtbot.waitUntil(
@@ -269,21 +269,21 @@ def test_batch_concatenate_files(tmp_path: Path, resources_dir: Path, qtbot):
assert expected_output_dir.exists()
output_files = list(expected_output_dir.iterdir())
- tiff_files = [f for f in output_files if f.suffix == ".tiff"]
+ tiff_files = [f for f in output_files if f.suffix == '.tiff']
assert len(tiff_files) == 4
- assert (expected_output_dir / "batch_concatenate.log.txt").exists()
+ assert (expected_output_dir / 'batch_concatenate.log.txt').exists()
def test_batch_cancel_button(tmp_path: Path, resources_dir: Path, qtbot):
"""Test that cancel button stops the batch runner."""
container = UtilitiesContainer()
- image_dir = resources_dir / "test_czis"
+ image_dir = resources_dir / 'test_czis'
all_image_files = list(image_dir.iterdir())
all_image_files = natsort.os_sorted(all_image_files)
container._files.value = tuple(all_image_files[:1])
container._save_directory.value = tmp_path
- container._save_directory_prefix.value = "test"
+ container._save_directory_prefix.value = 'test'
# Start the batch operation
container.batch_concatenate_files()
@@ -312,17 +312,17 @@ def test_batch_error_callback(qtbot):
container = UtilitiesContainer()
# Create a mock context with a file_set item (files, save_name)
- mock_files = [Path("file1.tiff"), Path("file2.tiff")]
+ mock_files = [Path('file1.tiff'), Path('file2.tiff')]
ctx = MagicMock(spec=BatchContext)
- ctx.item = (mock_files, "bad_file")
+ ctx.item = (mock_files, 'bad_file')
- test_exception = ValueError("Test error message")
+ test_exception = ValueError('Test error message')
container._on_batch_error(ctx, test_exception)
# Verify progress bar label was updated with error info
- assert "Error on bad_file" in container._progress_bar.label
- assert "Test error message" in container._progress_bar.label
+ assert 'Error on bad_file' in container._progress_bar.label
+ assert 'Test error message' in container._progress_bar.label
def test_batch_button_state_toggle(qtbot):
@@ -330,15 +330,15 @@ def test_batch_button_state_toggle(qtbot):
container = UtilitiesContainer()
# Initial state should be 'Batch Concat.'
- assert container._concatenate_batch_button.text == "Batch Concat."
+ assert container._concatenate_batch_button.text == 'Batch Concat.'
# Set to running state
container._set_batch_button_state(running=True)
- assert container._concatenate_batch_button.text == "Cancel"
+ assert container._concatenate_batch_button.text == 'Cancel'
# Set back to not running
container._set_batch_button_state(running=False)
- assert container._concatenate_batch_button.text == "Batch Concat."
+ assert container._concatenate_batch_button.text == 'Batch Concat.'
def test_save_scenes_ome_tiff(test_czi_image, tmp_path: Path, qtbot):
@@ -346,7 +346,7 @@ def test_save_scenes_ome_tiff(test_czi_image, tmp_path: Path, qtbot):
container = UtilitiesContainer()
container._files.value = path
container._save_directory.value = tmp_path
- save_dir = tmp_path / "ExtractedScenes"
+ save_dir = tmp_path / 'ExtractedScenes'
container.save_scenes_ome_tiff()
@@ -367,7 +367,7 @@ def test_extract_and_save_scenes_ome_tiff(test_czi_image, tmp_path: Path):
)
path, _ = test_czi_image
- save_dir = tmp_path / "ExtractedScenes"
+ save_dir = tmp_path / 'ExtractedScenes'
# Collect all yielded results
results = list(extract_and_save_scenes_ome_tiff(path, save_dir))
@@ -389,7 +389,7 @@ def test_extract_and_save_scenes_ome_tiff_specific_scenes(
)
path, _ = test_czi_image
- save_dir = tmp_path / "ExtractedScenes"
+ save_dir = tmp_path / 'ExtractedScenes'
# Extract only scenes 0 and 2
results = list(
@@ -410,9 +410,9 @@ def test_open_images(make_napari_viewer, test_rgb_image):
assert (
container._dim_shape.value
- == "T: 1, C: 1, Z: 1, Y: 1440, X: 1920, S: 3"
+ == 'T: 1, C: 1, Z: 1, Y: 1440, X: 1920, S: 3'
)
- assert container._squeezed_dims_order == "YX"
+ assert container._squeezed_dims_order == 'YX'
assert container._channel_names.value == "['red', 'green', 'blue']"
@@ -422,20 +422,20 @@ def test_canvas_export_figure(make_napari_viewer, tmp_path: Path):
container = UtilitiesContainer(viewer)
container._save_directory.value = tmp_path
- container._save_name.value = "test"
+ container._save_name.value = 'test'
container.canvas_export_figure()
- expected_save_loc = tmp_path / "Figures" / "test_figure.png"
+ expected_save_loc = tmp_path / 'Figures' / 'test_figure.png'
- assert "Exported canvas" in container._results.value
+ assert 'Exported canvas' in container._results.value
assert expected_save_loc.exists()
assert expected_save_loc.stat().st_size > 0
# make sure properly detects 3D mode doesn't work
viewer.dims.ndisplay = 3
container.canvas_export_figure()
- assert "Exporting Figure only works in 2D mode" in container._results.value
+ assert 'Exporting Figure only works in 2D mode' in container._results.value
def test_canvas_screenshot(make_napari_viewer, tmp_path: Path):
@@ -444,13 +444,13 @@ def test_canvas_screenshot(make_napari_viewer, tmp_path: Path):
container = UtilitiesContainer(viewer)
container._save_directory.value = tmp_path
- container._save_name.value = "test"
+ container._save_name.value = 'test'
container.canvas_screenshot()
- expected_save_loc = tmp_path / "Figures" / "test_canvas.png"
+ expected_save_loc = tmp_path / 'Figures' / 'test_canvas.png'
- assert "Exported screenshot of canvas" in container._results.value
+ assert 'Exported screenshot of canvas' in container._results.value
assert expected_save_loc.exists()
assert expected_save_loc.stat().st_size > 0
@@ -479,7 +479,7 @@ def test_rescale_by(make_napari_viewer):
def test_get_dims_for_shape_layer():
container = UtilitiesContainer()
- container._squeezed_dims_order = "YX"
+ container._squeezed_dims_order = 'YX'
container._squeezed_dims = (20, 30)
dims = container._get_dims_for_shape_layer()