Repository: mir-dataset-loaders/mirdata

Issue:
`module.load_*` should raise `IOError`, not return `None`
raised by @rabitt in a meeting today

Many of the module-specific loaders, e.g. `orchset.load_melody`, return `None` if the argument does not exist. This can be confusing in the sense that it may suggest that the file exists but that there is no annotation for this file, or that the annotation is not applicable (non-melodic music, following my example above).

She now recommends raising an `IOError`. 

This is a breaking change so i wouldn't recommend it for v0.2
But we can deprecate it in v0.2 and change the behavior in v0.3

@rabitt what should we do for people providing new loaders? Should we tell them to work with the old or the new convention? 
This affects `new_loader.md` (excerpt below):

```
# -- Write any necessary loader functions for loading the dataset's data
def load_annotation(annotation_path):
    if not os.path.exists(annotation_path):
        return None
```

Discussion:
codecov[bot]: # [Codecov](https://codecov.io/gh/mir-dataset-loaders/mirdata/pull/240?src=pr&el=h1) Report
> Merging [#240](https://codecov.io/gh/mir-dataset-loaders/mirdata/pull/240?src=pr&el=desc) into [master](https://codecov.io/gh/mir-dataset-loaders/mirdata/commit/19a477d1cdda296ba872a8a479a9d5d523e303d0&el=desc) will **decrease** coverage by `2.89%`.
> The diff coverage is `98.73%`.


```diff
@@            Coverage Diff             @@
##           master     #240      +/-   ##
==========================================
- Coverage   95.92%   93.02%   -2.90%     
==========================================
  Files          20       20              
  Lines        2085     2137      +52     
==========================================
- Hits         2000     1988      -12     
- Misses         85      149      +64     
```


Patch:
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 9f7e1bb..72888f7 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -255,6 +255,8 @@ def load_audio(audio_path):
     # -- for example, the code below. This should be dataset specific!
     # -- By default we load to mono
     # -- change this if it doesn't make sense for your dataset.
+    if not os.path.exists(audio_path):
+        raise IOError("audio_path {} does not exist".format(audio_path))
     return librosa.load(audio_path, sr=None, mono=True)
 
 
@@ -342,8 +344,15 @@ def load(data_home=None):
 
 # -- Write any necessary loader functions for loading the dataset's data
 def load_annotation(annotation_path):
+
+    # -- if there are some file paths for this annotation type in this dataset's
+    # -- index that are None/null, uncomment the lines below.
+    # if annotation_path is None:
+    #     return None
+
     if not os.path.exists(annotation_path):
-        return None
+        raise IOError("annotation_path {} does not exist".format(annotation_path))
+
     with open(annotation_path, 'r') as fhandle:
         reader = csv.reader(fhandle, delimiter=' ')
         start_times = []
diff --git a/mirdata/beatles.py b/mirdata/beatles.py
index 20f30f0..a09ac7b 100644
--- a/mirdata/beatles.py
+++ b/mirdata/beatles.py
@@ -123,6 +123,8 @@ def load_audio(audio_path):
         sr (float): The sample rate of the audio file
 
     """
+    if not os.path.exists(audio_path):
+        raise IOError("audio_path {} does not exist".format(audio_path))
     return librosa.load(audio_path, sr=None, mono=True)
 
 
@@ -223,9 +225,12 @@ def load_beats(beats_path):
         (utils.BeatData): loaded beat data
 
     """
-    if beats_path is None or not os.path.exists(beats_path):
+    if beats_path is None:
         return None
 
+    if not os.path.exists(beats_path):
+        raise IOError("beats_path {} does not exist".format(beats_path))
+
     beat_times, beat_positions = [], []
     with open(beats_path, 'r') as fhandle:
         dialect = csv.Sniffer().sniff(fhandle.read(1024))
@@ -254,9 +259,12 @@ def load_chords(chords_path):
         (utils.ChordData): loaded chord data
 
     """
-    if chords_path is None or not os.path.exists(chords_path):
+    if chords_path is None:
         return None
 
+    if not os.path.exists(chords_path):
+        raise IOError("chords_path {} does not exist".format(chords_path))
+
     start_times, end_times, chords = [], [], []
     with open(chords_path, 'r') as f:
         dialect = csv.Sniffer().sniff(f.read(1024))
@@ -282,9 +290,12 @@ def load_key(keys_path):
         (utils.KeyData): loaded key data
 
     """
-    if keys_path is None or not os.path.exists(keys_path):
+    if keys_path is None:
         return None
 
+    if not os.path.exists(keys_path):
+        raise IOError("keys_path {} does not exist".format(keys_path))
+
     start_times, end_times, keys = [], [], []
     with open(keys_path, 'r') as fhandle:
         reader = csv.reader(fhandle, delimiter='\t')
@@ -309,9 +320,12 @@ def load_sections(sections_path):
         (utils.SectionData): loaded section data
 
     """
-    if sections_path is None or not os.path.exists(sections_path):
+    if sections_path is None:
         return None
 
+    if not os.path.exists(sections_path):
+        raise IOError("sections_path {} does not exist".format(sections_path))
+
     start_times, end_times, sections = [], [], []
     with open(sections_path, 'r') as fhandle:
         reader = csv.reader(fhandle, delimiter='\t')
diff --git a/mirdata/dali.py b/mirdata/dali.py
index 58b2499..a032ae8 100644
--- a/mirdata/dali.py
+++ b/mirdata/dali.py
@@ -36,9 +36,6 @@ except ImportError as E:
     )
     raise
 
-import mirdata.track as track
-import mirdata.utils as utils
-
 DATASET_DIR = 'DALI'
 
 METADATA_REMOTE = download_utils.RemoteFileMetadata(
@@ -200,6 +197,8 @@ def load_audio(audio_path):
         sr (float): The sample rate of the audio file
 
     """
+    if not os.path.exists(audio_path):
+        raise IOError("audio_path {} does not exist".format(audio_path))
     return librosa.load(audio_path, sr=None, mono=True)
 
 
@@ -303,7 +302,8 @@ def load_annotations_granularity(annotations_path, granularity):
 
     """
     if not os.path.exists(annotations_path):
-        return None
+        raise IOError("annotations_path {} does not exist".format(annotations_path))
+
     try:
         with gzip.open(annotations_path, 'rb') as f:
             output = pickle.load(f)
@@ -339,7 +339,8 @@ def load_annotations_class(annotations_path):
 
     """
     if not os.path.exists(annotations_path):
-        return None
+        raise IOError("annotations_path {} does not exist".format(annotations_path))
+
     try:
         with gzip.open(annotations_path, 'rb') as f:
             output = pickle.load(f)
diff --git a/mirdata/groove_midi.py b/mirdata/groove_midi.py
index 9d4a528..05d9282 100644
--- a/mirdata/groove_midi.py
+++ b/mirdata/groove_midi.py
@@ -51,10 +51,7 @@ import os
 import pretty_midi
 import shutil
 
-import mirdata.download_utils as download_utils
-import mirdata.jams_utils as jams_utils
-import mirdata.track as track
-import mirdata.utils as utils
+from mirdata import download_utils, jams_utils, track, utils
 
 
 DATASET_DIR = 'Groove-MIDI'
@@ -350,6 +347,12 @@ def load_audio(audio_path):
         sr (float): The sample rate of the audio file
 
     """
+    if audio_path is None:
+        return None, None
+
+    if not os.path.exists(audio_path):
+        raise IOError("audio_path {} does not exist".format(audio_path))
+
     return librosa.load(audio_path, sr=22050, mono=True)
 
 
@@ -363,6 +366,9 @@ def load_midi(midi_path):
         midi_data (obj): pretty_midi object
 
     """
+    if not os.path.exists(midi_path):
+        raise IOError("midi_path {} does not exist".format(midi_path))
+
     return pretty_midi.PrettyMIDI(midi_path)
 
 
diff --git a/mirdata/gtzan_genre.py b/mirdata/gtzan_genre.py
index 8df3c44..3635652 100644
--- a/mirdata/gtzan_genre.py
+++ b/mirdata/gtzan_genre.py
@@ -66,7 +66,7 @@ class Track(track.Track):
     @property
     def audio(self):
         """(np.ndarray, float): audio signal, sample rate"""
-        return load_audio(self.audio_path, sample_rate=22050)
+        return load_audio(self.audio_path)
 
     def to_jams(self):
         """Jams: the track's data in jams format"""
@@ -82,19 +82,20 @@ class Track(track.Track):
         )
 
 
-def load_audio(audio_path, sample_rate=22050):
+def load_audio(audio_path):
     """Load a GTZAN audio file.
 
     Args:
         audio_path (str): path to audio file
-        sample_rate: Requested sample rate (optional, default 22050)
 
     Returns:
         y (np.ndarray): the mono audio signal
         sr (float): The sample rate of the audio file
 
     """
-    audio, sr = librosa.load(audio_path, sr=sample_rate, mono=True)
+    if not os.path.exists(audio_path):
+        raise IOError("audio_path {} does not exist".format(audio_path))
+    audio, sr = librosa.load(audio_path, sr=22050, mono=True)
     return audio, sr
 
 
diff --git a/mirdata/guitarset.py b/mirdata/guitarset.py
index 4a2cc3d..999f053 100644
--- a/mirdata/guitarset.py
+++ b/mirdata/guitarset.py
@@ -50,7 +50,6 @@ import numpy as np
 import os
 
 from mirdata import download_utils
-from mirdata import jams_utils
 from mirdata import track
 from mirdata import utils
 
@@ -215,7 +214,7 @@ class Track(track.Track):
         notes = {}
         # iterate over 6 strings
         for i in range(6):
-            notes[_GUITAR_STRINGS[i]] = _load_note_ann(self.jams_path, i)
+            notes[_GUITAR_STRINGS[i]] = load_note_ann(self.jams_path, i)
         return notes
 
     @property
@@ -258,6 +257,8 @@ def load_audio(audio_path):
         sr (float): The sample rate of the audio file
 
     """
+    if not os.path.exists(audio_path):
+        raise IOError("audio_path {} does not exist".format(audio_path))
     return librosa.load(audio_path, sr=None, mono=True)
 
 
@@ -272,6 +273,8 @@ def load_multitrack_audio(audio_path):
         sr (float): The sample rate of the audio file
 
     """
+    if not os.path.exists(audio_path):
+        raise IOError("audio_path {} does not exist".format(audio_path))
     return librosa.load(audio_path, sr=None, mono=False)
 
 
@@ -349,6 +352,8 @@ def load(data_home=None):
 
 
 def load_beats(jams_path):
+    if not os.path.exists(jams_path):
+        raise IOError("jams_path {} does not exist".format(jams_path))
     jam = jams.load(jams_path)
     anno = jam.search(namespace='beat_position')[0]
     times, values = anno.to_event_values()
@@ -367,6 +372,8 @@ def load_chords(jams_path, leadsheet_version=True):
     Returns:
         (ChordData): Chord data
     """
+    if not os.path.exists(jams_path):
+        raise IOError("jams_path {} does not exist".format(jams_path))
     jam = jams.load(jams_path)
     if leadsheet_version:
         anno = jam.search(namespace='chord')[0]
@@ -377,6 +384,8 @@ def load_chords(jams_path, leadsheet_version=True):
 
 
 def load_key_mode(jams_path):
+    if not os.path.exists(jams_path):
+        raise IOError("jams_path {} does not exist".format(jams_path))
     jam = jams.load(jams_path)
     anno = jam.search(namespace='key_mode')[0]
     intervals, values = anno.to_interval_values()
@@ -391,6 +400,8 @@ def load_pitch_contour(jams_path, string_num):
     string_num (int), in range(6): Which string to load.
         0 is the Low E string, 5 is the high e string.
     """
+    if not os.path.exists(jams_path):
+        raise IOError("jams_path {} does not exist".format(jams_path))
     jam = jams.load(jams_path)
     anno_arr = jam.search(namespace='pitch_contour')
     anno = anno_arr.search(data_source=str(string_num))[0]
@@ -399,7 +410,7 @@ def load_pitch_contour(jams_path, string_num):
     return utils.F0Data(times, frequencies, np.ones_like(times))
 
 
-def _load_note_ann(jams_path, string_num):
+def load_note_ann(jams_path, string_num):
     """
     Args:
         jams_path (str): Path of the jams annotation file
@@ -407,6 +418,8 @@ def _load_note_ann(jams_path, string_num):
     string_num (int), in range(6): Which string to load.
         0 is the Low E string, 5 is the high e string.
     """
+    if not os.path.exists(jams_path):
+        raise IOError("jams_path {} does not exist".format(jams_path))
     jam = jams.load(jams_path)
     anno_arr = jam.search(namespace='note_midi')
     anno = anno_arr.search(data_source=str(string_num))[0]
diff --git a/mirdata/ikala.py b/mirdata/ikala.py
index faf8dec..5b1f9d8 100644
--- a/mirdata/ikala.py
+++ b/mirdata/ikala.py
@@ -156,6 +156,9 @@ def load_vocal_audio(audio_path):
         sr (float): The sample rate of the audio file
 
     """
+    if not os.path.exists(audio_path):
+        raise IOError("audio_path {} does not exist".format(audio_path))
+
     audio, sr = librosa.load(audio_path, sr=None, mono=False)
     vocal_channel = audio[1, :]
     return vocal_channel, sr
@@ -172,6 +175,9 @@ def load_instrumental_audio(audio_path):
         sr (float): The sample rate of the audio file
 
     """
+    if not os.path.exists(audio_path):
+        raise IOError("audio_path {} does not exist".format(audio_path))
+
     audio, sr = librosa.load(audio_path, sr=None, mono=False)
     instrumental_channel = audio[0, :]
     return instrumental_channel, sr
@@ -188,6 +194,9 @@ def load_mix_audio(audio_path):
         sr (float): The sample rate of the audio file
 
     """
+    if not os.path.exists(audio_path):
+        raise IOError("audio_path {} does not exist".format(audio_path))
+
     mixed_audio, sr = librosa.load(audio_path, sr=None, mono=True)
     # multipy by 2 because librosa averages the left and right channel.
     return 2.0 * mixed_audio, sr
@@ -283,7 +292,7 @@ def load(data_home=None):
 
 def load_f0(f0_path):
     if not os.path.exists(f0_path):
-        return None
+        raise IOError("f0_path {} does not exist".format(f0_path))
 
     with open(f0_path) as fhandle:
         lines = fhandle.readlines()
@@ -297,7 +306,8 @@ def load_f0(f0_path):
 
 def load_lyrics(lyrics_path):
     if not os.path.exists(lyrics_path):
-        return None
+        raise IOError("lyrics_path {} does not exist".format(lyrics_path))
+
     # input: start time (ms), end time (ms), lyric, [pronunciation]
     with open(lyrics_path, 'r') as fhandle:
         reader = csv.reader(fhandle, delimiter=' ')
diff --git a/mirdata/medley_solos_db.py b/mirdata/medley_solos_db.py
index 52cc956..6159583 100644
--- a/mirdata/medley_solos_db.py
+++ b/mirdata/medley_solos_db.py
@@ -151,6 +151,9 @@ def load_audio(audio_path):
         sr (float): The sample rate of the audio file
 
     """
+    if not os.path.exists(audio_path):
+        raise IOError("audio_path {} does not exist".format(audio_path))
+
     return librosa.load(audio_path, sr=22050, mono=True)
 
 
diff --git a/mirdata/medleydb_melody.py b/mirdata/medleydb_melody.py
index 73b22e7..a6da538 100644
--- a/mirdata/medleydb_melody.py
+++ b/mirdata/medleydb_melody.py
@@ -152,6 +152,9 @@ def load_audio(audio_path):
         sr (float): The sample rate of the audio file
 
     """
+    if not os.path.exists(audio_path):
+        raise IOError("audio_path {} does not exist".format(audio_path))
+
     return librosa.load(audio_path, sr=None, mono=True)
 
 
@@ -237,7 +240,8 @@ def load(data_home=None):
 
 def load_melody(melody_path):
     if not os.path.exists(melody_path):
-        return None
+        raise IOError("melody_path {} does not exist".format(melody_path))
+
     times = []
     freqs = []
     with open(melody_path, 'r') as fhandle:
@@ -255,7 +259,8 @@ def load_melody(melody_path):
 
 def load_melody3(melody_path):
     if not os.path.exists(melody_path):
-        return None
+        raise IOError("melody_path {} does not exist".format(melody_path))
+
     times = []
     freqs_list = []
     conf_list = []
diff --git a/mirdata/medleydb_pitch.py b/mirdata/medleydb_pitch.py
index 6fc3c5a..c071cfb 100644
--- a/mirdata/medleydb_pitch.py
+++ b/mirdata/medleydb_pitch.py
@@ -124,6 +124,9 @@ def load_audio(audio_path):
         sr (float): The sample rate of the audio file
 
     """
+    if not os.path.exists(audio_path):
+        raise IOError("audio_path {} does not exist".format(audio_path))
+
     return librosa.load(audio_path, sr=None, mono=True)
 
 
@@ -210,7 +213,8 @@ def load(data_home=None):
 
 def load_pitch(pitch_path):
     if not os.path.exists(pitch_path):
-        return None
+        raise IOError("pitch_path {} does not exist".format(pitch_path))
+
     times = []
     freqs = []
     with open(pitch_path, 'r') as fhandle:
diff --git a/mirdata/orchset.py b/mirdata/orchset.py
index 5a78453..90fc018 100644
--- a/mirdata/orchset.py
+++ b/mirdata/orchset.py
@@ -213,6 +213,9 @@ def load_audio_mono(audio_path):
         sr (float): The sample rate of the audio file
 
     """
+    if not os.path.exists(audio_path):
+        raise IOError("audio_path {} does not exist".format(audio_path))
+
     return librosa.load(audio_path, sr=None, mono=True)
 
 
@@ -227,6 +230,9 @@ def load_audio_stereo(audio_path):
         sr (float): The sample rate of the audio file
 
     """
+    if not os.path.exists(audio_path):
+        raise IOError("audio_path {} does not exist".format(audio_path))
+
     return librosa.load(audio_path, sr=None, mono=False)
 
 
@@ -313,7 +319,7 @@ def load(data_home=None):
 
 def load_melody(melody_path):
     if not os.path.exists(melody_path):
-        return None
+        raise IOError("melody_path {} does not exist".format(melody_path))
 
     times = []
     freqs = []
diff --git a/mirdata/rwc_classical.py b/mirdata/rwc_classical.py
index 1aea7c1..a7795a8 100644
--- a/mirdata/rwc_classical.py
+++ b/mirdata/rwc_classical.py
@@ -195,6 +195,9 @@ def load_audio(audio_path):
         sr (float): The sample rate of the audio file
 
     """
+    if not os.path.exists(audio_path):
+        raise IOError("audio_path {} does not exist".format(audio_path))
+
     return librosa.load(audio_path, sr=None, mono=True)
 
 
@@ -278,7 +281,8 @@ def load(data_home=None):
 
 def load_sections(sections_path):
     if not os.path.exists(sections_path):
-        return None
+        raise IOError("sections_path {} does not exist".format(sections_path))
+
     begs = []  # timestamps of section beginnings
     ends = []  # timestamps of section endings
     secs = []  # section labels
@@ -324,7 +328,8 @@ def _position_in_bar(beat_positions, beat_times):
 
 def load_beats(beats_path):
     if not os.path.exists(beats_path):
-        return None
+        raise IOError("beats_path {} does not exist".format(beats_path))
+
     beat_times = []  # timestamps of beat interval beginnings
     beat_positions = []  # beat position inside the bar
 
diff --git a/mirdata/rwc_popular.py b/mirdata/rwc_popular.py
index e63d871..b4d0b7a 100644
--- a/mirdata/rwc_popular.py
+++ b/mirdata/rwc_popular.py
@@ -314,7 +314,8 @@ def load(data_home=None):
 
 def load_chords(chords_path):
     if not os.path.exists(chords_path):
-        return None
+        raise IOError("chords_path {} does not exist".format(chords_path))
+
     begs = []  # timestamps of chord beginnings
     ends = []  # timestamps of chord endings
     chords = []  # chord labels
@@ -332,7 +333,8 @@ def load_chords(chords_path):
 
 def load_voca_inst(voca_inst_path):
     if not os.path.exists(voca_inst_path):
-        return None
+        raise IOError("voca_inst_path {} does not exist".format(voca_inst_path))
+
     begs = []  # timestamps of vocal-instrument activity beginnings
     ends = []  # timestamps of vocal-instrument activity endings
     events = []  # vocal-instrument activity labels
diff --git a/mirdata/salami.py b/mirdata/salami.py
index 020abc8..7948b96 100644
--- a/mirdata/salami.py
+++ b/mirdata/salami.py
@@ -228,6 +228,9 @@ def load_audio(audio_path):
         sr (float): The sample rate of the audio file
 
     """
+    if not os.path.exists(audio_path):
+        raise IOError("audio_path {} does not exist".format(audio_path))
+
     return librosa.load(audio_path, sr=None, mono=True)
 
 
@@ -318,9 +321,12 @@ def load(data_home=None):
 
 
 def load_sections(sections_path):
-    if sections_path is None or not os.path.exists(sections_path):
+    if sections_path is None:
         return None
 
+    if not os.path.exists(sections_path):
+        raise IOError("sections_path {} does not exist".format(sections_path))
+
     times = []
     secs = []
     with open(sections_path, 'r') as fhandle:
diff --git a/mirdata/tinysol.py b/mirdata/tinysol.py
index 1842902..59cf6eb 100644
--- a/mirdata/tinysol.py
+++ b/mirdata/tinysol.py
@@ -206,6 +206,9 @@ def load_audio(audio_path):
         sr (float): The sample rate of the audio file
 
     """
+    if not os.path.exists(audio_path):
+        raise IOError("audio_path {} does not exist".format(audio_path))
+
     return librosa.load(audio_path, sr=None, mono=True)
 
 


Test patch:
diff --git a/tests/test_beatles.py b/tests/test_beatles.py
index 778218c..23fd256 100644
--- a/tests/test_beatles.py
+++ b/tests/test_beatles.py
@@ -115,9 +115,7 @@ def test_load_beats():
     )
     assert np.array_equal(beat_data.beat_positions, np.array([2, 3, 4, 1, 2, 3, 4]))
 
-    # load a file which doesn't exist
-    beat_none = beatles.load_beats('fake/file/path')
-    assert beat_none is None
+    assert beatles.load_beats(None) is None
 
 
 def test_load_chords():
@@ -139,9 +137,7 @@ def test_load_chords():
     )
     assert np.array_equal(chord_data.labels, np.array(['N', 'E:min', 'G']))
 
-    # load a file which doesn't exist
-    chord_none = beatles.load_chords('fake/file/path')
-    assert chord_none is None
+    assert beatles.load_chords(None) is None
 
 
 def test_load_key():
@@ -158,9 +154,7 @@ def test_load_key():
     assert np.array_equal(key_data.end_times, np.array([119.333]))
     assert np.array_equal(key_data.keys, np.array(['E']))
 
-    # load a file which doesn't exist
-    key_none = beatles.load_key('fake/file/path')
-    assert key_none is None
+    assert beatles.load_key(None) is None
 
 
 def test_load_sections():
@@ -178,9 +172,7 @@ def test_load_sections():
     assert np.array_equal(section_data.intervals[:, 1], np.array([0.465, 14.931]))
     assert np.array_equal(section_data.labels, np.array(['silence', 'intro']))
 
-    # load a file which doesn't exist
-    section_none = beatles.load_sections('fake/file/path')
-    assert section_none is None
+    assert beatles.load_sections(None) is None
 
 
 def test_fix_newpoint():
diff --git a/tests/test_dali.py b/tests/test_dali.py
index 2901174..43f41b0 100644
--- a/tests/test_dali.py
+++ b/tests/test_dali.py
@@ -64,10 +64,6 @@ def test_load_notes():
     assert np.array_equal(note_data.intervals[:, 1], np.array([24.273, 24.420, 24.568]))
     assert np.array_equal(note_data.notes, np.array([1108.731, 1108.731, 1108.731]))
 
-    # load a file which doesn't exist
-    notes_none = dali.load_annotations_granularity('fake/file/path', 'notes')
-    assert notes_none is None
-
 
 def test_load_words():
     data_path = (
@@ -85,10 +81,6 @@ def test_load_words():
     assert np.array_equal(word_data.end_times, np.array([24.273, 24.42, 24.568]))
     assert np.array_equal(word_data.lyrics, np.array(['why', 'do', 'they']))
 
-    # load a file which doesn't exist
-    words_none = dali.load_annotations_granularity('fake/file/path', 'words')
-    assert words_none is None
-
 
 def test_load_lines():
     data_path = (
@@ -110,10 +102,6 @@ def test_load_lines():
     assert np.array_equal(line_data.end_times, np.array([24.42, 24.568]))
     assert np.array_equal(line_data.lyrics, np.array(['why do', 'they']))
 
-    # load a file which doesn't exist
-    line_none = dali.load_annotations_granularity('fake/file/path', 'lines')
-    assert line_none is None
-
 
 def test_load_paragraphs():
     data_path = (
@@ -131,10 +119,6 @@ def test_load_paragraphs():
     assert np.array_equal(par_data.end_times, np.array([24.420, 24.568]))
     assert np.array_equal(par_data.lyrics, np.array(['why do', 'they']))
 
-    # load a file which doesn't exist
-    pars_none = dali.load_annotations_granularity('fake/file/path', 'paragraphs')
-    assert pars_none is None
-
 
 def test_load_dali_object():
     data_path = (
@@ -210,7 +194,3 @@ def test_load_dali_object():
             'time': [24.42030564587644, 24.568103458468812],
         },
     ]
-
-    # load a file which doesn't exist
-    dali_none = dali.load_annotations_class('fake/file/path')
-    assert dali_none is None
diff --git a/tests/test_groove_midi.py b/tests/test_groove_midi.py
index ce347ea..f04d62e 100644
--- a/tests/test_groove_midi.py
+++ b/tests/test_groove_midi.py
@@ -82,6 +82,12 @@ def test_load_metadata():
     assert metadata_none is None
 
 
+def test_load_audio():
+    audio_none, sr_none = groove_midi.load_audio(None)
+    assert audio_none is None
+    assert sr_none is None
+
+
 def test_download(httpserver):
     data_home = 'tests/resources/mir_datasets/Groove-MIDI_download'
     if os.path.exists(data_home):
diff --git a/tests/test_gtzan_genre.py b/tests/test_gtzan_genre.py
index be9eeea..615654f 100644
--- a/tests/test_gtzan_genre.py
+++ b/tests/test_gtzan_genre.py
@@ -1,6 +1,5 @@
 # -*- coding: utf-8 -*-
 
-
 import os
 
 from tests.test_utils import run_track_tests
diff --git a/tests/test_ikala.py b/tests/test_ikala.py
index 71e5c37..795322a 100644
--- a/tests/test_ikala.py
+++ b/tests/test_ikala.py
@@ -89,10 +89,6 @@ def test_load_f0():
     assert np.array_equal(f0_data.frequencies, np.array([0.0, 260.946404518887]))
     assert np.array_equal(f0_data.confidence, np.array([0.0, 1.0]))
 
-    # load a file which doesn't exist
-    f0_data_none = ikala.load_f0('fake/file/path')
-    assert f0_data_none is None
-
 
 def test_load_lyrics():
     # load a file without pronunciations
@@ -129,10 +125,6 @@ def test_load_lyrics():
     assert np.array_equal(lyrics_data_pronun.lyrics, np.array(['ASDF', 'EVERYBODY']))
     assert np.array_equal(lyrics_data_pronun.pronunciations, np.array(['t i au', None]))
 
-    # load a file which doesn't exist
-    lyrics_data_none = ikala.load_lyrics('fake/path')
-    assert lyrics_data_none is None
-
 
 def test_load_metadata():
     data_home = 'tests/resources/mir_datasets/iKala'
diff --git a/tests/test_loaders.py b/tests/test_loaders.py
index 7698c74..e258106 100644
--- a/tests/test_loaders.py
+++ b/tests/test_loaders.py
@@ -1,6 +1,7 @@
 # -*- coding: utf-8 -*-
 
 import importlib
+import inspect
 from inspect import signature
 import io
 import os
@@ -106,3 +107,41 @@ def test_track():
 
         track_custom = dataset.Track(trackid, data_home='casa/de/data')
         assert track_custom._data_home == 'casa/de/data'
+
+
+# for load_* functions which require more than one argument
+# module_name : {function_name: {parameter2: value, parameter3: value}}
+EXCEPTIONS = {
+    'dali': {'load_annotations_granularity': {'granularity': 'notes'}},
+    'guitarset': {
+        'load_pitch_contour': {'string_num': 1},
+        'load_note_ann': {'string_num': 1}
+    },
+}
+
+
+def test_load_methods():
+    for dataset in DATASETS:
+        dataset_name = dataset.__name__.split('.')[1]
+
+        all_methods = dir(dataset)
+        load_methods = [
+            getattr(dataset, m) for m in all_methods if m.startswith('load_')
+        ]
+        for load_method in load_methods:
+            method_name = load_method.__name__
+            params = [
+                p
+                for p in signature(load_method).parameters.values()
+                if p.default == inspect._empty
+            ]  # get list of parameters that don't have defaults
+
+            # add to the EXCEPTIONS dictionary above if your load_* function needs
+            # more than one argument.
+            if dataset_name in EXCEPTIONS and method_name in EXCEPTIONS[dataset_name]:
+                extra_params = EXCEPTIONS[dataset_name][method_name]
+                with pytest.raises(IOError):
+                    load_method("a/fake/filepath", **extra_params)
+            else:
+                with pytest.raises(IOError):
+                    load_method("a/fake/filepath")
diff --git a/tests/test_medleydb_melody.py b/tests/test_medleydb_melody.py
index 1ddf87b..0d3d472 100644
--- a/tests/test_medleydb_melody.py
+++ b/tests/test_medleydb_melody.py
@@ -82,10 +82,6 @@ def test_load_melody():
     assert np.array_equal(melody_data.frequencies, np.array([0.0, 965.99199999999996]))
     assert np.array_equal(melody_data.confidence, np.array([0.0, 1.0]))
 
-    # load a file which doesn't exist
-    melody_data_none = medleydb_melody.load_melody('fake/file/path')
-    assert melody_data_none is None
-
 
 def test_load_melody3():
     # load a file which exists
@@ -124,10 +120,6 @@ def test_load_melody3():
         [1.0, 1.0, 1.0, 1.0, 1.0],
     ]
 
-    # load a file which doesn't exist
-    melody_data_none = medleydb_melody.load_melody3('fake/file/path')
-    assert melody_data_none is None
-
 
 def test_load_metadata():
     data_home = 'tests/resources/mir_datasets/MedleyDB-Melody'
diff --git a/tests/test_medleydb_pitch.py b/tests/test_medleydb_pitch.py
index f06c82e..6baf6d8 100644
--- a/tests/test_medleydb_pitch.py
+++ b/tests/test_medleydb_pitch.py
@@ -74,10 +74,6 @@ def test_load_pitch():
     assert np.array_equal(pitch_data.frequencies, np.array([0.0, 191.877]))
     assert np.array_equal(pitch_data.confidence, np.array([0.0, 1.0]))
 
-    # load a file which doesn't exist
-    pitch_data_none = medleydb_pitch.load_pitch('fake/file/path')
-    assert pitch_data_none is None
-
 
 def test_load_metadata():
     data_home = 'tests/resources/mir_datasets/MedleyDB-Pitch'
@@ -91,6 +87,3 @@ def test_load_metadata():
         'title': 'NightOwl',
         'genre': 'Singer/Songwriter',
     }
-
-    metadata_none = medleydb_pitch._load_metadata('asdf/asdf')
-    assert metadata_none is None
diff --git a/tests/test_orchset.py b/tests/test_orchset.py
index d76c26f..6686081 100644
--- a/tests/test_orchset.py
+++ b/tests/test_orchset.py
@@ -80,10 +80,6 @@ def test_load_melody():
     assert np.array_equal(melody_data.frequencies, np.array([0.0, 0.0, 622.254]))
     assert np.array_equal(melody_data.confidence, np.array([0.0, 0.0, 1.0]))
 
-    # load a file which doesn't exist
-    melody_data_none = orchset.load_melody('fake/file/path')
-    assert melody_data_none is None
-
 
 def test_load_metadata():
     data_home = 'tests/resources/mir_datasets/Orchset'
diff --git a/tests/test_rwc_classical.py b/tests/test_rwc_classical.py
index 73ec00d..cfe6aff 100644
--- a/tests/test_rwc_classical.py
+++ b/tests/test_rwc_classical.py
@@ -97,10 +97,6 @@ def test_load_sections():
     assert np.array_equal(section_data.intervals[:, 1], np.array([46.14, 433.71]))
     assert np.array_equal(section_data.labels, np.array(['chorus A', 'ending']))
 
-    # load a file which doesn't exist
-    section_data_none = rwc_classical.load_sections('fake/file/path')
-    assert section_data_none is None
-
 
 def test_position_in_bar():
     beat_positions1 = np.array([48, 384, 48, 384, 48, 384, 48, 384])
@@ -172,10 +168,6 @@ def test_load_beats():
     )
     assert np.array_equal(beat_data.beat_positions, np.array([2, 1, 2, 1, 2, 1, 2, 1]))
 
-    # load a file which doesn't exist
-    beats_data_none = rwc_classical.load_beats('fake/path')
-    assert beats_data_none is None
-
 
 def test_load_metadata():
     data_home = 'tests/resources/mir_datasets/RWC-Classical'
@@ -191,6 +183,3 @@ def test_load_metadata():
         'duration': 435,
         'category': 'Symphony',
     }
-
-    metadata_none = rwc_classical._load_metadata('asdf/asdf')
-    assert metadata_none is None
diff --git a/tests/test_rwc_popular.py b/tests/test_rwc_popular.py
index f1bd0d2..81baedc 100644
--- a/tests/test_rwc_popular.py
+++ b/tests/test_rwc_popular.py
@@ -142,10 +142,6 @@ def test_load_chords():
         chord_data.labels, ['N', 'Ab:min', 'E:maj', 'Bb:maj(*3)', 'C:min7']
     )
 
-    # load a file which doesn't exist
-    chord_data_none = rwc_popular.load_chords('fake/path')
-    assert chord_data_none is None
-
 
 def test_load_voca_inst():
     vocinst_path = (
@@ -198,10 +194,6 @@ def test_load_voca_inst():
         ),
     )
 
-    # load a file which doesn't exist
-    vocainst_data_none = rwc_popular.load_voca_inst('fake/path')
-    assert vocainst_data_none is None
-
 
 def test_load_metadata():
     data_home = 'tests/resources/mir_datasets/RWC-Popular'
@@ -219,6 +211,3 @@ def test_load_metadata():
         'instruments': 'Gt',
         'drum_information': 'Drum sequences',
     }
-
-    metadata_none = rwc_popular._load_metadata('asdf/asdf')
-    assert metadata_none is None
diff --git a/tests/test_salami.py b/tests/test_salami.py
index a867ea4..c65c6b0 100644
--- a/tests/test_salami.py
+++ b/tests/test_salami.py
@@ -195,13 +195,9 @@ def test_load_sections():
         section_data.labels, np.array(['Silence', 'A', 'B', 'Silence'])
     )
 
-    # load a file which doesn't exist
-    section_data_none = salami.load_sections('fake/file/path')
-    assert section_data_none is None
-
     # load none
-    section_data_none2 = salami.load_sections('asdf/asdf')
-    assert section_data_none2 is None
+    section_data_none = salami.load_sections(None)
+    assert section_data_none is None
 
 
 def test_load_metadata():