Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support only latest PyNWB #510

Merged
merged 7 commits into from
Sep 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
### Improvements
* Removed the `robust_ros3_read` utility helper. [#506](https://github.com/NeurodataWithoutBorders/nwbinspector/pull/506)
* Simplified the `nwbinspector.testing` configuration framework. [#509](https://github.com/NeurodataWithoutBorders/nwbinspector/pull/509)
* Cleaned old references to non-recent PyNWB and HDMF versions. Current policy is that latest NWB Inspector releases should only support compatibility with latest PyNWB and HDMF. [#510](https://github.com/NeurodataWithoutBorders/nwbinspector/pull/510)



# v0.5.2
Expand Down
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
pynwb
pynwb>=2.8 # NWB Inspector should always be used with most recent minor versions of PyNWB
hdmf-zarr
stephprince marked this conversation as resolved.
Show resolved Hide resolved
fsspec
s3fs
Expand Down
3 changes: 1 addition & 2 deletions src/nwbinspector/_dandi_inspection.py
Original file line number Diff line number Diff line change
Expand Up @@ -241,9 +241,8 @@ def inspect_url(
h5py.File(name=byte_stream) as file,
pynwb.NWBHDF5IO(file=file) as io,
):
if not skip_validate:
if skip_validate is False:
validation_errors = pynwb.validate(io=io)

for validation_error in validation_errors:
yield InspectorMessage(
message=validation_error.reason,
Expand Down
25 changes: 6 additions & 19 deletions src/nwbinspector/_nwb_inspection.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@

import pynwb
from natsort import natsorted
from packaging.version import Version
from tqdm import tqdm

from . import available_checks, configure_checks
Expand All @@ -20,7 +19,6 @@
OptionalListOfStrings,
PathType,
calculate_number_of_cpu,
get_package_version,
)


Expand Down Expand Up @@ -73,8 +71,8 @@ def inspect_all(
all available CPUs minus x.
Set to 1 (also the default) to disable.
skip_validate : bool, optional
Skip the PyNWB validation step. This may be desired for older NWBFiles (< schema version v2.10).
stephprince marked this conversation as resolved.
Show resolved Hide resolved
The default is False, which is also recommended.
Skip the PyNWB validation step.
The default is False, which is recommended.
progress_bar : bool, optional
Display a progress bar while scanning NWBFiles.
Defaults to True.
Expand Down Expand Up @@ -232,8 +230,8 @@ def inspect_nwbfile(
nwbfile_path : FilePathType
Path to the NWB file on disk or on S3.
skip_validate : bool
Skip the PyNWB validation step. This may be desired for older NWBFiles (< schema version v2.10).
The default is False, which is also recommended.
Skip the PyNWB validation step.
The default is False, which is recommended.
checks : list, optional
List of checks to run.
config : dict
Expand Down Expand Up @@ -269,7 +267,7 @@ def inspect_nwbfile(
filterwarnings(action="ignore", message="No cached namespaces found in .*")
filterwarnings(action="ignore", message="Ignoring cached namespace .*")

if not skip_validate and get_package_version("pynwb") >= Version("2.2.0"):
if not skip_validate:
validation_error_list, _ = pynwb.validate(paths=[nwbfile_path])
for validation_namespace_errors in validation_error_list:
for validation_error in validation_namespace_errors:
Expand All @@ -282,17 +280,6 @@ def inspect_nwbfile(
)

with pynwb.NWBHDF5IO(path=nwbfile_path, mode="r", load_namespaces=True) as io:
if not skip_validate and get_package_version("pynwb") < Version("2.2.0"):
validation_errors = pynwb.validate(io=io)
for validation_error in validation_errors:
yield InspectorMessage(
message=validation_error.reason,
importance=Importance.PYNWB_VALIDATION,
check_function_name=validation_error.name,
location=validation_error.location,
file_path=nwbfile_path,
)

try:
in_memory_nwbfile = io.read()

Expand Down Expand Up @@ -321,7 +308,7 @@ def _intercept_in_vitro_protein(nwbfile_object: pynwb.NWBFile, checks: Optional[
If the special 'protein' subject_id is specified, return a truncated list of checks to run.

This is a temporary method for allowing upload of certain in vitro data to DANDI and
is expected to replaced in future versions.
is expected to be replaced in future versions.
"""
subject_related_check_names = [
"check_subject_exists",
Expand Down
64 changes: 31 additions & 33 deletions src/nwbinspector/checks/_images.py
Original file line number Diff line number Diff line change
@@ -1,38 +1,36 @@
"""Checks specific to the Images neurodata type."""

from packaging.version import Version
from pynwb.base import Images
from pynwb.image import IndexSeries

from .._registration import Importance, InspectorMessage, register_check
from ..utils import get_package_version

# The Images neurodata type was unavailable prior to PyNWB v.2.1.0
if get_package_version(name="pynwb") >= Version("2.1.0"):
from pynwb.base import Images

@register_check(importance=Importance.BEST_PRACTICE_VIOLATION, neurodata_type=Images)
def check_order_of_images_unique(images: Images):
"""Check that all the values in the order_of_images field of an Images object are unique."""
if images.order_of_images is None:
return
if not len(set(images.order_of_images)) == len(images.order_of_images):
return InspectorMessage(message="order_of_images should have unique values.")

@register_check(importance=Importance.BEST_PRACTICE_VIOLATION, neurodata_type=Images)
def check_order_of_images_len(images: Images):
"""Check that all the values in the order_of_images field of an Images object are unique."""
if images.order_of_images is None:
return
if not len(images.order_of_images) == len(images.images):
return InspectorMessage(
message=f"Length of order_of_images ({len(images.order_of_images)}) does not match the number of "
f"images ({len(images.images)})."
)

@register_check(importance=Importance.BEST_PRACTICE_VIOLATION, neurodata_type=IndexSeries)
def check_index_series_points_to_image(index_series: IndexSeries):
if index_series.indexed_timeseries is not None:
return InspectorMessage(
message="Pointing an IndexSeries to a TimeSeries will be deprecated. Please point to an Images "
"container instead."
)


@register_check(importance=Importance.BEST_PRACTICE_VIOLATION, neurodata_type=Images)
def check_order_of_images_unique(images: Images):
"""Check that all the values in the order_of_images field of an Images object are unique."""
if images.order_of_images is None:
return
if not len(set(images.order_of_images)) == len(images.order_of_images):
return InspectorMessage(message="order_of_images should have unique values.")


@register_check(importance=Importance.BEST_PRACTICE_VIOLATION, neurodata_type=Images)
def check_order_of_images_len(images: Images):
"""Check that all the values in the order_of_images field of an Images object are unique."""
if images.order_of_images is None:
return
if not len(images.order_of_images) == len(images.images):
return InspectorMessage(
message=f"Length of order_of_images ({len(images.order_of_images)}) does not match the number of "
f"images ({len(images.images)})."
)


@register_check(importance=Importance.BEST_PRACTICE_VIOLATION, neurodata_type=IndexSeries)
def check_index_series_points_to_image(index_series: IndexSeries):
if index_series.indexed_timeseries is not None:
return InspectorMessage(
message="Pointing an IndexSeries to a TimeSeries will be deprecated. Please point to an Images "
"container instead."
)
15 changes: 0 additions & 15 deletions tests/unit_tests/test_icephys.py
Original file line number Diff line number Diff line change
@@ -1,24 +1,16 @@
import pytest
from packaging.version import Version
from pynwb.device import Device
from pynwb.icephys import IntracellularElectrode

from nwbinspector import Importance, InspectorMessage
from nwbinspector.checks import check_intracellular_electrode_cell_id_exists
from nwbinspector.utils import get_package_version

PYNWB_VERSION_LOWER_2_1_0 = get_package_version(name="pynwb") < Version("2.1.0")
PYNWB_VERSION_LOWER_SKIP_REASON = "This test requires PyNWB>=2.1.0"


@pytest.mark.skipif(PYNWB_VERSION_LOWER_2_1_0, reason=PYNWB_VERSION_LOWER_SKIP_REASON)
def test_pass_check_intracellular_electrode_cell_id_exists():
device = Device(name="device")
ielec = IntracellularElectrode(name="ielec", cell_id="123", device=device, description="an intracellular electrode")
assert check_intracellular_electrode_cell_id_exists(ielec) is None


@pytest.mark.skipif(PYNWB_VERSION_LOWER_2_1_0, reason=PYNWB_VERSION_LOWER_SKIP_REASON)
def test_fail_check_intracellular_electrode_cell_id_exists():
device = Device(name="device")
ielec = IntracellularElectrode(name="ielec", device=device, description="an intracellular electrode")
Expand All @@ -30,10 +22,3 @@ def test_fail_check_intracellular_electrode_cell_id_exists():
object_name="ielec",
location="/",
)


@pytest.mark.skipif(not PYNWB_VERSION_LOWER_2_1_0, reason="This test requires PyNWB<2.1.0")
def test_skip_check_for_lower_versions():
device = Device(name="device")
ielec = IntracellularElectrode(name="ielec", device=device, description="an intracellular electrode")
assert check_intracellular_electrode_cell_id_exists(ielec) is None
15 changes: 1 addition & 14 deletions tests/unit_tests/test_images.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import numpy as np
import pytest
from packaging.version import Version
from pynwb import TimeSeries
from pynwb.base import ImageReferences, Images
from pynwb.image import GrayscaleImage, IndexSeries

from nwbinspector import Importance, InspectorMessage
Expand All @@ -10,15 +9,8 @@
check_order_of_images_len,
check_order_of_images_unique,
)
from nwbinspector.utils import get_package_version

HAVE_IMAGES = get_package_version(name="pynwb") >= Version("2.1.0")
skip_reason = "You must have PyNWB>=v2.1.0 to run these tests!"
if HAVE_IMAGES:
from pynwb.base import ImageReferences, Images


@pytest.mark.skipif(not HAVE_IMAGES, reason=skip_reason)
def test_check_order_of_images_unique():
imgs = [GrayscaleImage(name=f"image{i}", data=np.random.randn(10, 10)) for i in range(5)]
img_refs = ImageReferences(name="order_of_images", data=imgs + [imgs[0]])
Expand All @@ -34,7 +26,6 @@ def test_check_order_of_images_unique():
)


@pytest.mark.skipif(not HAVE_IMAGES, reason=skip_reason)
def test_pass_check_order_of_images_unique():
imgs = [GrayscaleImage(name=f"image{i}", data=np.random.randn(10, 10)) for i in range(5)]
img_refs = ImageReferences(name="order_of_images", data=imgs)
Expand All @@ -43,7 +34,6 @@ def test_pass_check_order_of_images_unique():
assert check_order_of_images_unique(images) is None


@pytest.mark.skipif(not HAVE_IMAGES, reason=skip_reason)
def test_check_order_of_images_len():
imgs = [GrayscaleImage(name=f"image{i}", data=np.random.randn(10, 10)) for i in range(5)]
img_refs = ImageReferences(name="order_of_images", data=imgs + [imgs[0]])
Expand All @@ -59,7 +49,6 @@ def test_check_order_of_images_len():
)


@pytest.mark.skipif(not HAVE_IMAGES, reason=skip_reason)
def test_pass_check_order_of_images_len():
imgs = [GrayscaleImage(name=f"image{i}", data=np.random.randn(10, 10)) for i in range(5)]
img_refs = ImageReferences(name="order_of_images", data=imgs)
Expand All @@ -68,7 +57,6 @@ def test_pass_check_order_of_images_len():
assert check_order_of_images_len(images) is None


@pytest.mark.skipif(not HAVE_IMAGES, reason=skip_reason)
def test_pass_check_index_series_points_to_image():
gs_img = GrayscaleImage(
name="random grayscale",
Expand All @@ -95,7 +83,6 @@ def test_pass_check_index_series_points_to_image():
assert check_index_series_points_to_image(idx_series) is None


@pytest.mark.skipif(not HAVE_IMAGES, reason=skip_reason)
def test_fail_check_index_series_points_to_image():
time_series = TimeSeries(
name="TimeSeries",
Expand Down
Loading
Loading