diff --git a/CHANGELOG.md b/CHANGELOG.md index beee4e54b..6d61f7762 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,17 +6,19 @@ ### Features * Added `get_stream_names` to `OpenEphysRecordingInterface`: [PR #1039](https://github.com/catalystneuro/neuroconv/pull/1039) -### Bug fixes - ### Improvements * Using ruff to enforce existence of public classes' docstrings [PR #1034](https://github.com/catalystneuro/neuroconv/pull/1034) * Separated tests that use external data by modality [PR #1049](https://github.com/catalystneuro/neuroconv/pull/1049) + + ## v0.6.1 (August 30, 2024) ### Bug fixes * Fixed the JSON schema inference warning on excluded fields; also improved error message reporting of which method triggered the error. [PR #1037](https://github.com/catalystneuro/neuroconv/pull/1037) + + ## v0.6.0 (August 27, 2024) ### Deprecations @@ -38,6 +40,7 @@ * Added helper function `neuroconv.tools.data_transfers.submit_aws_batch_job` for basic automated submission of AWS batch jobs. [PR #384](https://github.com/catalystneuro/neuroconv/pull/384) * Data interfaces `run_conversion` method now performs metadata validation before running the conversion. [PR #949](https://github.com/catalystneuro/neuroconv/pull/949) * Introduced `null_values_for_properties` to `add_units_table` to give user control over null values behavior [PR #989](https://github.com/catalystneuro/neuroconv/pull/989) +* Most data interfaces and converters now use Pydantic to validate their inputs, including existence of file and folder paths. [PR #1022](https://github.com/catalystneuro/neuroconv/pull/1022) ### Bug fixes * Fixed the default naming of multiple electrical series in the `SpikeGLXConverterPipe`. [PR #957](https://github.com/catalystneuro/neuroconv/pull/957) diff --git a/src/neuroconv/basedatainterface.py b/src/neuroconv/basedatainterface.py index f8fce62b0..59415b043 100644 --- a/src/neuroconv/basedatainterface.py +++ b/src/neuroconv/basedatainterface.py @@ -6,7 +6,7 @@ from typing import Literal, Optional, Union from jsonschema.validators import validate -from pydantic import FilePath +from pydantic import FilePath, validate_call from pynwb import NWBFile from .tools.nwb_helpers import ( @@ -39,6 +39,7 @@ def get_source_schema(cls) -> dict: """Infer the JSON schema for the source_data from the method signature (annotation typing).""" return get_json_schema_from_method_signature(cls, exclude=["source_data"]) + @validate_call def __init__(self, verbose: bool = False, **source_data): self.verbose = verbose self.source_data = source_data diff --git a/src/neuroconv/datainterfaces/behavior/audio/audiointerface.py b/src/neuroconv/datainterfaces/behavior/audio/audiointerface.py index d61cdf18b..fc3f08fb8 100644 --- a/src/neuroconv/datainterfaces/behavior/audio/audiointerface.py +++ b/src/neuroconv/datainterfaces/behavior/audio/audiointerface.py @@ -4,7 +4,7 @@ import numpy as np import scipy -from pydantic import FilePath +from pydantic import FilePath, validate_call from pynwb import NWBFile from ....basetemporalalignmentinterface import BaseTemporalAlignmentInterface @@ -28,6 +28,7 @@ class AudioInterface(BaseTemporalAlignmentInterface): associated_suffixes = (".wav",) info = "Interface for writing audio recordings to an NWB file." + @validate_call def __init__(self, file_paths: list[FilePath], verbose: bool = False): """ Data interface for writing acoustic recordings to an NWB file. diff --git a/src/neuroconv/datainterfaces/behavior/deeplabcut/deeplabcutdatainterface.py b/src/neuroconv/datainterfaces/behavior/deeplabcut/deeplabcutdatainterface.py index c6850b555..a0719470b 100644 --- a/src/neuroconv/datainterfaces/behavior/deeplabcut/deeplabcutdatainterface.py +++ b/src/neuroconv/datainterfaces/behavior/deeplabcut/deeplabcutdatainterface.py @@ -2,7 +2,7 @@ from typing import Optional, Union import numpy as np -from pydantic import FilePath +from pydantic import FilePath, validate_call from pynwb.file import NWBFile from ....basetemporalalignmentinterface import BaseTemporalAlignmentInterface @@ -25,6 +25,7 @@ def get_source_schema(cls) -> dict: source_schema["properties"]["config_file_path"]["description"] = "Path to .yml config file" return source_schema + @validate_call def __init__( self, file_path: FilePath, diff --git a/src/neuroconv/datainterfaces/behavior/fictrac/fictracdatainterface.py b/src/neuroconv/datainterfaces/behavior/fictrac/fictracdatainterface.py index 13136b691..1b9686fd1 100644 --- a/src/neuroconv/datainterfaces/behavior/fictrac/fictracdatainterface.py +++ b/src/neuroconv/datainterfaces/behavior/fictrac/fictracdatainterface.py @@ -6,7 +6,7 @@ from typing import Optional, Union import numpy as np -from pydantic import FilePath +from pydantic import FilePath, validate_call from pynwb.behavior import Position, SpatialSeries from pynwb.file import NWBFile @@ -154,6 +154,7 @@ def get_source_schema(cls) -> dict: source_schema["properties"]["file_path"]["description"] = "Path to the .dat file (the output of fictrac)" return source_schema + @validate_call def __init__( self, file_path: FilePath, diff --git a/src/neuroconv/datainterfaces/behavior/lightningpose/lightningposeconverter.py b/src/neuroconv/datainterfaces/behavior/lightningpose/lightningposeconverter.py index 114cff0dd..dee848f19 100644 --- a/src/neuroconv/datainterfaces/behavior/lightningpose/lightningposeconverter.py +++ b/src/neuroconv/datainterfaces/behavior/lightningpose/lightningposeconverter.py @@ -1,7 +1,7 @@ from copy import deepcopy from typing import Optional -from pydantic import FilePath +from pydantic import FilePath, validate_call from pynwb import NWBFile from neuroconv import NWBConverter @@ -26,6 +26,7 @@ class LightningPoseConverter(NWBConverter): def get_source_schema(cls): return get_schema_from_method_signature(cls) + @validate_call def __init__( self, file_path: FilePath, diff --git a/src/neuroconv/datainterfaces/behavior/lightningpose/lightningposedatainterface.py b/src/neuroconv/datainterfaces/behavior/lightningpose/lightningposedatainterface.py index b87366109..f103b7c9a 100644 --- a/src/neuroconv/datainterfaces/behavior/lightningpose/lightningposedatainterface.py +++ b/src/neuroconv/datainterfaces/behavior/lightningpose/lightningposedatainterface.py @@ -5,7 +5,7 @@ from typing import Optional import numpy as np -from pydantic import FilePath +from pydantic import FilePath, validate_call from pynwb import NWBFile from ....basetemporalalignmentinterface import BaseTemporalAlignmentInterface @@ -58,6 +58,7 @@ def get_metadata_schema(self) -> dict: return metadata_schema + @validate_call def __init__( self, file_path: FilePath, diff --git a/src/neuroconv/datainterfaces/behavior/medpc/medpcdatainterface.py b/src/neuroconv/datainterfaces/behavior/medpc/medpcdatainterface.py index 0c109f559..6a4127663 100644 --- a/src/neuroconv/datainterfaces/behavior/medpc/medpcdatainterface.py +++ b/src/neuroconv/datainterfaces/behavior/medpc/medpcdatainterface.py @@ -1,7 +1,7 @@ from typing import Optional import numpy as np -from pydantic import FilePath +from pydantic import FilePath, validate_call from pynwb.behavior import BehavioralEpochs, IntervalSeries from pynwb.file import NWBFile @@ -41,6 +41,7 @@ class MedPCInterface(BaseTemporalAlignmentInterface): info = "Interface for handling MedPC output files." associated_suffixes = (".txt",) + @validate_call def __init__( self, file_path: FilePath, diff --git a/src/neuroconv/datainterfaces/behavior/miniscope/miniscopedatainterface.py b/src/neuroconv/datainterfaces/behavior/miniscope/miniscopedatainterface.py index 6a46e84fe..ecb763523 100644 --- a/src/neuroconv/datainterfaces/behavior/miniscope/miniscopedatainterface.py +++ b/src/neuroconv/datainterfaces/behavior/miniscope/miniscopedatainterface.py @@ -1,6 +1,6 @@ from pathlib import Path -from pydantic import DirectoryPath +from pydantic import DirectoryPath, validate_call from pynwb import NWBFile from .... import BaseDataInterface @@ -24,6 +24,7 @@ def get_source_schema(cls) -> dict: ] = "The main Miniscope folder. The movie files are expected to be in sub folders within the main folder." return source_schema + @validate_call def __init__(self, folder_path: DirectoryPath): """ Initialize reading recordings from the Miniscope behavioral camera. diff --git a/src/neuroconv/datainterfaces/behavior/neuralynx/neuralynx_nvt_interface.py b/src/neuroconv/datainterfaces/behavior/neuralynx/neuralynx_nvt_interface.py index f01d11e53..e161387f0 100644 --- a/src/neuroconv/datainterfaces/behavior/neuralynx/neuralynx_nvt_interface.py +++ b/src/neuroconv/datainterfaces/behavior/neuralynx/neuralynx_nvt_interface.py @@ -2,7 +2,7 @@ from typing import Optional import numpy as np -from pydantic import FilePath +from pydantic import FilePath, validate_call from pynwb import NWBFile from pynwb.behavior import CompassDirection, Position, SpatialSeries @@ -20,6 +20,7 @@ class NeuralynxNvtInterface(BaseTemporalAlignmentInterface): associated_suffixes = (".nvt",) info = "Interface for writing Neuralynx position tracking .nvt files to NWB." + @validate_call def __init__(self, file_path: FilePath, verbose: bool = True): """ Interface for writing Neuralynx .nvt files to nwb. diff --git a/src/neuroconv/datainterfaces/behavior/sleap/sleapdatainterface.py b/src/neuroconv/datainterfaces/behavior/sleap/sleapdatainterface.py index a74b90a67..713b21c98 100644 --- a/src/neuroconv/datainterfaces/behavior/sleap/sleapdatainterface.py +++ b/src/neuroconv/datainterfaces/behavior/sleap/sleapdatainterface.py @@ -2,7 +2,7 @@ from typing import Optional import numpy as np -from pydantic import FilePath +from pydantic import FilePath, validate_call from pynwb.file import NWBFile from .sleap_utils import extract_timestamps @@ -27,6 +27,7 @@ def get_source_schema(cls) -> dict: ] = "Path of the video for extracting timestamps (optional)." return source_schema + @validate_call def __init__( self, file_path: FilePath, diff --git a/src/neuroconv/datainterfaces/behavior/video/videodatainterface.py b/src/neuroconv/datainterfaces/behavior/video/videodatainterface.py index dfb22deba..7a28c2d2f 100644 --- a/src/neuroconv/datainterfaces/behavior/video/videodatainterface.py +++ b/src/neuroconv/datainterfaces/behavior/video/videodatainterface.py @@ -6,7 +6,7 @@ import numpy as np import psutil from hdmf.data_utils import DataChunkIterator -from pydantic import FilePath +from pydantic import FilePath, validate_call from pynwb import NWBFile from pynwb.image import ImageSeries from tqdm import tqdm @@ -28,6 +28,7 @@ class VideoInterface(BaseDataInterface): # Other suffixes, while they can be opened by OpenCV, are not supported by DANDI so should probably not list here info = "Interface for handling standard video file formats." + @validate_call def __init__( self, file_paths: list[FilePath], diff --git a/src/neuroconv/datainterfaces/ecephys/openephys/openephyslegacydatainterface.py b/src/neuroconv/datainterfaces/ecephys/openephys/openephyslegacydatainterface.py index 0818257a7..b3392d2db 100644 --- a/src/neuroconv/datainterfaces/ecephys/openephys/openephyslegacydatainterface.py +++ b/src/neuroconv/datainterfaces/ecephys/openephys/openephyslegacydatainterface.py @@ -2,7 +2,7 @@ from typing import Optional from warnings import warn -from pydantic import DirectoryPath +from pydantic import DirectoryPath, validate_call from ..baserecordingextractorinterface import BaseRecordingExtractorInterface @@ -35,6 +35,7 @@ def get_source_schema(cls): return source_schema + @validate_call def __init__( self, folder_path: DirectoryPath, diff --git a/src/neuroconv/datainterfaces/ecephys/openephys/openephyssortingdatainterface.py b/src/neuroconv/datainterfaces/ecephys/openephys/openephyssortingdatainterface.py index 20f292c9a..2d53e6331 100644 --- a/src/neuroconv/datainterfaces/ecephys/openephys/openephyssortingdatainterface.py +++ b/src/neuroconv/datainterfaces/ecephys/openephys/openephyssortingdatainterface.py @@ -1,4 +1,4 @@ -from pydantic import DirectoryPath +from pydantic import DirectoryPath, validate_call from ..basesortingextractorinterface import BaseSortingExtractorInterface from ....utils import get_schema_from_method_signature @@ -23,6 +23,7 @@ def get_source_schema(cls) -> dict: metadata_schema["additionalProperties"] = False return metadata_schema + @validate_call def __init__(self, folder_path: DirectoryPath, experiment_id: int = 0, recording_id: int = 0): from spikeextractors import OpenEphysSortingExtractor diff --git a/src/neuroconv/datainterfaces/ecephys/phy/phydatainterface.py b/src/neuroconv/datainterfaces/ecephys/phy/phydatainterface.py index da5ebbfc2..07324b602 100644 --- a/src/neuroconv/datainterfaces/ecephys/phy/phydatainterface.py +++ b/src/neuroconv/datainterfaces/ecephys/phy/phydatainterface.py @@ -1,6 +1,6 @@ from typing import Optional -from pydantic import DirectoryPath +from pydantic import DirectoryPath, validate_call from ..basesortingextractorinterface import BaseSortingExtractorInterface @@ -24,6 +24,7 @@ def get_source_schema(cls) -> dict: ] = "Path to the output Phy folder (containing the params.py)." return source_schema + @validate_call def __init__( self, folder_path: DirectoryPath, diff --git a/src/neuroconv/datainterfaces/ecephys/plexon/plexondatainterface.py b/src/neuroconv/datainterfaces/ecephys/plexon/plexondatainterface.py index 8b2e53338..4a6a50fa5 100644 --- a/src/neuroconv/datainterfaces/ecephys/plexon/plexondatainterface.py +++ b/src/neuroconv/datainterfaces/ecephys/plexon/plexondatainterface.py @@ -1,6 +1,6 @@ from pathlib import Path -from pydantic import FilePath +from pydantic import FilePath, validate_call from ..baserecordingextractorinterface import BaseRecordingExtractorInterface from ..basesortingextractorinterface import BaseSortingExtractorInterface @@ -24,6 +24,7 @@ def get_source_schema(cls) -> dict: source_schema["properties"]["file_path"]["description"] = "Path to the .plx file." return source_schema + @validate_call def __init__(self, file_path: FilePath, verbose: bool = True, es_key: str = "ElectricalSeries"): """ Load and prepare data for Plexon. @@ -68,6 +69,7 @@ def get_source_schema(cls) -> dict: source_schema["properties"]["file_path"]["description"] = "Path to the .pl2 file." return source_schema + @validate_call def __init__(self, file_path: FilePath, verbose: bool = True, es_key: str = "ElectricalSeries"): """ Load and prepare data for Plexon. @@ -119,6 +121,7 @@ def get_source_schema(cls) -> dict: source_schema["properties"]["file_path"]["description"] = "Path to the plexon spiking data (.plx file)." return source_schema + @validate_call def __init__(self, file_path: FilePath, verbose: bool = True): """ Load and prepare data for Plexon. diff --git a/src/neuroconv/datainterfaces/ecephys/spike2/spike2datainterface.py b/src/neuroconv/datainterfaces/ecephys/spike2/spike2datainterface.py index 5895dfe3c..ccd98a369 100644 --- a/src/neuroconv/datainterfaces/ecephys/spike2/spike2datainterface.py +++ b/src/neuroconv/datainterfaces/ecephys/spike2/spike2datainterface.py @@ -1,6 +1,6 @@ from pathlib import Path -from pydantic import FilePath +from pydantic import FilePath, validate_call from ..baserecordingextractorinterface import BaseRecordingExtractorInterface from ....tools import get_package @@ -40,6 +40,7 @@ def get_all_channels_info(cls, file_path: FilePath): _test_sonpy_installation() return cls.get_extractor().get_all_channels_info(file_path=file_path) + @validate_call def __init__(self, file_path: FilePath, verbose: bool = True, es_key: str = "ElectricalSeries"): """ Initialize reading of Spike2 file. diff --git a/src/neuroconv/datainterfaces/ecephys/spikegadgets/spikegadgetsdatainterface.py b/src/neuroconv/datainterfaces/ecephys/spikegadgets/spikegadgetsdatainterface.py index a5ad98d52..4a63dc237 100644 --- a/src/neuroconv/datainterfaces/ecephys/spikegadgets/spikegadgetsdatainterface.py +++ b/src/neuroconv/datainterfaces/ecephys/spikegadgets/spikegadgetsdatainterface.py @@ -22,6 +22,7 @@ def get_source_schema(cls) -> dict: source_schema["properties"]["file_path"].update(description="Path to SpikeGadgets (.rec) file.") return source_schema + # @validate_call def __init__( self, file_path: FilePath, diff --git a/src/neuroconv/datainterfaces/ecephys/spikeglx/spikeglxconverter.py b/src/neuroconv/datainterfaces/ecephys/spikeglx/spikeglxconverter.py index 9d40cde3d..6aeb36cec 100644 --- a/src/neuroconv/datainterfaces/ecephys/spikeglx/spikeglxconverter.py +++ b/src/neuroconv/datainterfaces/ecephys/spikeglx/spikeglxconverter.py @@ -1,7 +1,7 @@ from pathlib import Path from typing import Optional -from pydantic import DirectoryPath +from pydantic import DirectoryPath, validate_call from .spikeglxdatainterface import SpikeGLXRecordingInterface from .spikeglxnidqinterface import SpikeGLXNIDQInterface @@ -33,6 +33,7 @@ def get_streams(cls, folder_path: DirectoryPath) -> list[str]: return SpikeGLXRecordingExtractor.get_streams(folder_path=folder_path)[0] + @validate_call def __init__( self, folder_path: DirectoryPath, diff --git a/src/neuroconv/datainterfaces/ecephys/spikeglx/spikeglxdatainterface.py b/src/neuroconv/datainterfaces/ecephys/spikeglx/spikeglxdatainterface.py index 7d97f7d25..d45a7f946 100644 --- a/src/neuroconv/datainterfaces/ecephys/spikeglx/spikeglxdatainterface.py +++ b/src/neuroconv/datainterfaces/ecephys/spikeglx/spikeglxdatainterface.py @@ -4,7 +4,7 @@ from typing import Optional import numpy as np -from pydantic import FilePath +from pydantic import FilePath, validate_call from .spikeglx_utils import ( add_recording_extractor_properties, @@ -42,6 +42,7 @@ def get_source_schema(cls) -> dict: source_schema["properties"]["file_path"]["description"] = "Path to SpikeGLX ap.bin or lf.bin file." return source_schema + @validate_call def __init__( self, file_path: FilePath, diff --git a/src/neuroconv/datainterfaces/ecephys/spikeglx/spikeglxnidqinterface.py b/src/neuroconv/datainterfaces/ecephys/spikeglx/spikeglxnidqinterface.py index 3c0b886ec..9e903f3f1 100644 --- a/src/neuroconv/datainterfaces/ecephys/spikeglx/spikeglxnidqinterface.py +++ b/src/neuroconv/datainterfaces/ecephys/spikeglx/spikeglxnidqinterface.py @@ -25,6 +25,7 @@ def get_source_schema(cls) -> dict: source_schema["properties"]["file_path"]["description"] = "Path to SpikeGLX .nidq file." return source_schema + # @validate_call def __init__( self, file_path: FilePath, diff --git a/src/neuroconv/datainterfaces/ecephys/tdt/tdtdatainterface.py b/src/neuroconv/datainterfaces/ecephys/tdt/tdtdatainterface.py index 420d0f242..d01e63c5d 100644 --- a/src/neuroconv/datainterfaces/ecephys/tdt/tdtdatainterface.py +++ b/src/neuroconv/datainterfaces/ecephys/tdt/tdtdatainterface.py @@ -1,4 +1,4 @@ -from pydantic import DirectoryPath +from pydantic import DirectoryPath, validate_call from ..baserecordingextractorinterface import BaseRecordingExtractorInterface @@ -10,6 +10,7 @@ class TdtRecordingInterface(BaseRecordingExtractorInterface): associated_suffixes = (".tbk", ".tbx", ".tev", ".tsq") info = "Interface for TDT recording data." + @validate_call def __init__( self, folder_path: DirectoryPath, diff --git a/src/neuroconv/datainterfaces/icephys/abf/abfdatainterface.py b/src/neuroconv/datainterfaces/icephys/abf/abfdatainterface.py index 5336b6116..535381466 100644 --- a/src/neuroconv/datainterfaces/icephys/abf/abfdatainterface.py +++ b/src/neuroconv/datainterfaces/icephys/abf/abfdatainterface.py @@ -1,9 +1,10 @@ import json from datetime import datetime, timedelta from pathlib import Path +from typing import Optional from warnings import warn -from pydantic import FilePath +from pydantic import FilePath, validate_call from ..baseicephysinterface import BaseIcephysInterface @@ -50,8 +51,12 @@ def get_source_schema(cls) -> dict: ) return source_schema + @validate_call def __init__( - self, file_paths: list[FilePath], icephys_metadata: dict = None, icephys_metadata_file_path: FilePath = None + self, + file_paths: list[FilePath], + icephys_metadata: Optional[dict] = None, + icephys_metadata_file_path: Optional[FilePath] = None, ): """ ABF IcephysInterface based on Neo AxonIO. diff --git a/src/neuroconv/datainterfaces/icephys/baseicephysinterface.py b/src/neuroconv/datainterfaces/icephys/baseicephysinterface.py index ff82ba391..76c9bf840 100644 --- a/src/neuroconv/datainterfaces/icephys/baseicephysinterface.py +++ b/src/neuroconv/datainterfaces/icephys/baseicephysinterface.py @@ -1,6 +1,7 @@ import importlib.util import numpy as np +from pydantic import FilePath, validate_call from pynwb import NWBFile from ...baseextractorinterface import BaseExtractorInterface @@ -24,7 +25,8 @@ def get_source_schema(cls) -> dict: source_schema = get_schema_from_method_signature(method=cls.__init__, exclude=[]) return source_schema - def __init__(self, file_paths: list): + @validate_call + def __init__(self, file_paths: list[FilePath]): # Check if the ndx_dandi_icephys module is available dandi_icephys_spec = importlib.util.find_spec("ndx_dandi_icephys") if dandi_icephys_spec is not None: diff --git a/src/neuroconv/datainterfaces/ophys/hdf5/hdf5datainterface.py b/src/neuroconv/datainterfaces/ophys/hdf5/hdf5datainterface.py index c980fcf41..2526f7fb3 100644 --- a/src/neuroconv/datainterfaces/ophys/hdf5/hdf5datainterface.py +++ b/src/neuroconv/datainterfaces/ophys/hdf5/hdf5datainterface.py @@ -13,6 +13,7 @@ class Hdf5ImagingInterface(BaseImagingExtractorInterface): associated_suffixes = (".h5", ".hdf5") info = "Interface for HDF5 imaging data." + # @validate_call def __init__( self, file_path: FilePath, diff --git a/src/neuroconv/datainterfaces/ophys/micromanagertiff/micromanagertiffdatainterface.py b/src/neuroconv/datainterfaces/ophys/micromanagertiff/micromanagertiffdatainterface.py index 4e758e742..17cbc95ed 100644 --- a/src/neuroconv/datainterfaces/ophys/micromanagertiff/micromanagertiffdatainterface.py +++ b/src/neuroconv/datainterfaces/ophys/micromanagertiff/micromanagertiffdatainterface.py @@ -1,5 +1,5 @@ from dateutil.parser import parse -from pydantic import DirectoryPath +from pydantic import DirectoryPath, validate_call from ..baseimagingextractorinterface import BaseImagingExtractorInterface @@ -18,6 +18,7 @@ def get_source_schema(cls) -> dict: source_schema["properties"]["folder_path"]["description"] = "The folder containing the OME-TIF image files." return source_schema + @validate_call def __init__(self, folder_path: DirectoryPath, verbose: bool = True): """ Data Interface for MicroManagerTiffImagingExtractor. diff --git a/src/neuroconv/datainterfaces/ophys/miniscope/miniscopeconverter.py b/src/neuroconv/datainterfaces/ophys/miniscope/miniscopeconverter.py index 09aba9f0e..cfee8f027 100644 --- a/src/neuroconv/datainterfaces/ophys/miniscope/miniscopeconverter.py +++ b/src/neuroconv/datainterfaces/ophys/miniscope/miniscopeconverter.py @@ -1,6 +1,6 @@ from typing import Optional -from pydantic import DirectoryPath +from pydantic import DirectoryPath, validate_call from pynwb import NWBFile from ... import MiniscopeBehaviorInterface, MiniscopeImagingInterface @@ -23,6 +23,7 @@ def get_source_schema(cls): source_schema["properties"]["folder_path"]["description"] = "The path to the main Miniscope folder." return source_schema + @validate_call def __init__(self, folder_path: DirectoryPath, verbose: bool = True): """ Initializes the data interfaces for the Miniscope recording and behavioral data stream. diff --git a/src/neuroconv/datainterfaces/ophys/miniscope/miniscopeimagingdatainterface.py b/src/neuroconv/datainterfaces/ophys/miniscope/miniscopeimagingdatainterface.py index 594bf00dc..64a180c46 100644 --- a/src/neuroconv/datainterfaces/ophys/miniscope/miniscopeimagingdatainterface.py +++ b/src/neuroconv/datainterfaces/ophys/miniscope/miniscopeimagingdatainterface.py @@ -3,7 +3,7 @@ from typing import Literal, Optional import numpy as np -from pydantic import DirectoryPath +from pydantic import DirectoryPath, validate_call from pynwb import NWBFile from ..baseimagingextractorinterface import BaseImagingExtractorInterface @@ -26,6 +26,7 @@ def get_source_schema(cls) -> dict: return source_schema + @validate_call def __init__(self, folder_path: DirectoryPath): """ Initialize reading the Miniscope imaging data. diff --git a/src/neuroconv/datainterfaces/ophys/sbx/sbxdatainterface.py b/src/neuroconv/datainterfaces/ophys/sbx/sbxdatainterface.py index 2fa90a2bb..5b921f6f3 100644 --- a/src/neuroconv/datainterfaces/ophys/sbx/sbxdatainterface.py +++ b/src/neuroconv/datainterfaces/ophys/sbx/sbxdatainterface.py @@ -1,6 +1,6 @@ from typing import Literal -from pydantic import FilePath +from pydantic import FilePath, validate_call from ..baseimagingextractorinterface import BaseImagingExtractorInterface @@ -12,6 +12,7 @@ class SbxImagingInterface(BaseImagingExtractorInterface): associated_suffixes = (".sbx",) info = "Interface for Scanbox imaging data." + @validate_call def __init__( self, file_path: FilePath, diff --git a/src/neuroconv/datainterfaces/ophys/scanimage/scanimageimaginginterfaces.py b/src/neuroconv/datainterfaces/ophys/scanimage/scanimageimaginginterfaces.py index 76a67ef9b..09e8f86d3 100644 --- a/src/neuroconv/datainterfaces/ophys/scanimage/scanimageimaginginterfaces.py +++ b/src/neuroconv/datainterfaces/ophys/scanimage/scanimageimaginginterfaces.py @@ -4,7 +4,7 @@ from typing import Optional from dateutil.parser import parse as dateparse -from pydantic import DirectoryPath, FilePath +from pydantic import DirectoryPath, FilePath, validate_call from ..baseimagingextractorinterface import BaseImagingExtractorInterface @@ -32,6 +32,7 @@ def get_source_schema(cls) -> dict: source_schema["properties"]["file_path"]["description"] = "Path to Tiff file." return source_schema + @validate_call def __new__( cls, file_path: FilePath, @@ -90,6 +91,7 @@ def get_source_schema(cls) -> dict: source_schema["properties"]["file_path"]["description"] = "Path to Tiff file." return source_schema + @validate_call def __init__( self, file_path: FilePath, @@ -168,6 +170,7 @@ def get_source_schema(cls) -> dict: source_schema["properties"]["folder_path"]["description"] = "Path to the folder containing the TIFF files." return source_schema + @validate_call def __new__( cls, folder_path: DirectoryPath, @@ -226,6 +229,7 @@ class ScanImageMultiPlaneImagingInterface(BaseImagingExtractorInterface): ExtractorName = "ScanImageTiffMultiPlaneImagingExtractor" + @validate_call def __init__( self, file_path: FilePath, @@ -327,6 +331,7 @@ class ScanImageMultiPlaneMultiFileImagingInterface(BaseImagingExtractorInterface ExtractorName = "ScanImageTiffMultiPlaneMultiFileImagingExtractor" + @validate_call def __init__( self, folder_path: DirectoryPath, @@ -443,6 +448,7 @@ class ScanImageSinglePlaneImagingInterface(BaseImagingExtractorInterface): ExtractorName = "ScanImageTiffSinglePlaneImagingExtractor" + @validate_call def __init__( self, file_path: FilePath, @@ -560,6 +566,7 @@ class ScanImageSinglePlaneMultiFileImagingInterface(BaseImagingExtractorInterfac ExtractorName = "ScanImageTiffSinglePlaneMultiFileImagingExtractor" + @validate_call def __init__( self, folder_path: DirectoryPath, diff --git a/src/neuroconv/datainterfaces/ophys/sima/simadatainterface.py b/src/neuroconv/datainterfaces/ophys/sima/simadatainterface.py index 8bf34cc04..570e2cfe3 100644 --- a/src/neuroconv/datainterfaces/ophys/sima/simadatainterface.py +++ b/src/neuroconv/datainterfaces/ophys/sima/simadatainterface.py @@ -1,4 +1,4 @@ -from pydantic import FilePath +from pydantic import FilePath, validate_call from ..basesegmentationextractorinterface import BaseSegmentationExtractorInterface @@ -10,6 +10,7 @@ class SimaSegmentationInterface(BaseSegmentationExtractorInterface): associated_suffixes = (".sima",) info = "Interface for SIMA segmentation." + @validate_call def __init__(self, file_path: FilePath, sima_segmentation_label: str = "auto_ROIs"): """ diff --git a/src/neuroconv/datainterfaces/ophys/suite2p/suite2pdatainterface.py b/src/neuroconv/datainterfaces/ophys/suite2p/suite2pdatainterface.py index 359dea25f..056616ce5 100644 --- a/src/neuroconv/datainterfaces/ophys/suite2p/suite2pdatainterface.py +++ b/src/neuroconv/datainterfaces/ophys/suite2p/suite2pdatainterface.py @@ -1,7 +1,7 @@ from copy import deepcopy from typing import Optional -from pydantic import DirectoryPath +from pydantic import DirectoryPath, validate_call from pynwb import NWBFile from ..basesegmentationextractorinterface import BaseSegmentationExtractorInterface @@ -72,6 +72,7 @@ def get_available_channels(cls, folder_path: DirectoryPath) -> dict: return Suite2pSegmentationExtractor.get_available_channels(folder_path=folder_path) + @validate_call def __init__( self, folder_path: DirectoryPath, diff --git a/src/neuroconv/datainterfaces/ophys/tdt_fp/tdtfiberphotometrydatainterface.py b/src/neuroconv/datainterfaces/ophys/tdt_fp/tdtfiberphotometrydatainterface.py index 72c854634..aa58f6ae4 100644 --- a/src/neuroconv/datainterfaces/ophys/tdt_fp/tdtfiberphotometrydatainterface.py +++ b/src/neuroconv/datainterfaces/ophys/tdt_fp/tdtfiberphotometrydatainterface.py @@ -6,7 +6,7 @@ import numpy as np import pytz -from pydantic import FilePath +from pydantic import DirectoryPath, validate_call from pynwb.file import NWBFile from neuroconv.basetemporalalignmentinterface import BaseTemporalAlignmentInterface @@ -28,7 +28,8 @@ class TDTFiberPhotometryInterface(BaseTemporalAlignmentInterface): info = "Data Interface for converting fiber photometry data from TDT files." associated_suffixes = ("Tbk", "Tdx", "tev", "tin", "tsq") - def __init__(self, folder_path: FilePath, verbose: bool = True): + @validate_call + def __init__(self, folder_path: DirectoryPath, verbose: bool = True): """Initialize the TDTFiberPhotometryInterface. Parameters diff --git a/src/neuroconv/datainterfaces/ophys/tiff/tiffdatainterface.py b/src/neuroconv/datainterfaces/ophys/tiff/tiffdatainterface.py index 017aa2e98..1eaa3b55e 100644 --- a/src/neuroconv/datainterfaces/ophys/tiff/tiffdatainterface.py +++ b/src/neuroconv/datainterfaces/ophys/tiff/tiffdatainterface.py @@ -1,6 +1,6 @@ from typing import Literal -from pydantic import FilePath +from pydantic import FilePath, validate_call from ..baseimagingextractorinterface import BaseImagingExtractorInterface @@ -18,6 +18,7 @@ def get_source_schema(cls) -> dict: source_schema["properties"]["file_path"]["description"] = "Path to Tiff file." return source_schema + @validate_call def __init__( self, file_path: FilePath, diff --git a/src/neuroconv/datainterfaces/text/excel/exceltimeintervalsinterface.py b/src/neuroconv/datainterfaces/text/excel/exceltimeintervalsinterface.py index cb4ae2330..92fbbbaa7 100644 --- a/src/neuroconv/datainterfaces/text/excel/exceltimeintervalsinterface.py +++ b/src/neuroconv/datainterfaces/text/excel/exceltimeintervalsinterface.py @@ -1,7 +1,7 @@ from typing import Optional import pandas as pd -from pydantic import FilePath +from pydantic import FilePath, validate_call from ..timeintervalsinterface import TimeIntervalsInterface @@ -13,6 +13,7 @@ class ExcelTimeIntervalsInterface(TimeIntervalsInterface): associated_suffixes = (".xlsx", ".xls", ".xlsm") info = "Interface for writing a time intervals table from an excel file." + @validate_call def __init__( self, file_path: FilePath, diff --git a/src/neuroconv/datainterfaces/text/timeintervalsinterface.py b/src/neuroconv/datainterfaces/text/timeintervalsinterface.py index b00779cb0..5f5b1107d 100644 --- a/src/neuroconv/datainterfaces/text/timeintervalsinterface.py +++ b/src/neuroconv/datainterfaces/text/timeintervalsinterface.py @@ -3,7 +3,7 @@ from typing import Optional import numpy as np -from pydantic import FilePath +from pydantic import FilePath, validate_call from pynwb import NWBFile from ...basedatainterface import BaseDataInterface @@ -16,6 +16,7 @@ class TimeIntervalsInterface(BaseDataInterface): keywords = ("table", "trials", "epochs", "time intervals") + @validate_call def __init__( self, file_path: FilePath, diff --git a/src/neuroconv/nwbconverter.py b/src/neuroconv/nwbconverter.py index eabf6d772..1f3e7c9f8 100644 --- a/src/neuroconv/nwbconverter.py +++ b/src/neuroconv/nwbconverter.py @@ -7,7 +7,7 @@ from typing import Literal, Optional, Union from jsonschema import validate -from pydantic import FilePath +from pydantic import FilePath, validate_call from pynwb import NWBFile from .basedatainterface import BaseDataInterface @@ -73,6 +73,7 @@ def _validate_source_data(self, source_data: dict[str, dict], verbose: bool = Tr if verbose: print("Source data is valid!") + @validate_call def __init__(self, source_data: dict[str, dict], verbose: bool = True): """Validate source_data against source_schema and initialize all data interfaces.""" self.verbose = verbose diff --git a/tests/test_behavior/test_audio_interface.py b/tests/test_behavior/test_audio_interface.py index 4fad4fe3d..50331d744 100644 --- a/tests/test_behavior/test_audio_interface.py +++ b/tests/test_behavior/test_audio_interface.py @@ -80,11 +80,6 @@ class AudioTestNWBConverter(NWBConverter): aligned_segment_starting_times=self.aligned_segment_starting_times ) - def test_unsupported_format(self): - exc_msg = "The currently supported file format for audio is WAV file. Some of the provided files does not match this format: ['.test']." - with pytest.raises(ValueError, match=re.escape(exc_msg)): - AudioInterface(file_paths=["test.test"]) - def test_get_metadata(self): audio_interface = AudioInterface(file_paths=self.file_paths) metadata = audio_interface.get_metadata() diff --git a/tests/test_on_data/ophys/test_fiber_photometry_interfaces.py b/tests/test_on_data/ophys/test_fiber_photometry_interfaces.py index b9121f08b..82cbb0e4a 100644 --- a/tests/test_on_data/ophys/test_fiber_photometry_interfaces.py +++ b/tests/test_on_data/ophys/test_fiber_photometry_interfaces.py @@ -400,8 +400,3 @@ def test_load_invalid_evtype(self): interface = self.data_interface_cls(**self.interface_kwargs) with self.assertRaises(AssertionError): interface.load(t2=1.0, evtype=["invalid"]) - - def test_load_invalid_folder_path(self): - interface = self.data_interface_cls(folder_path="invalid") - with self.assertRaises(AssertionError): - interface.load(t2=1.0)