Repository: mila-udem/fuel
Pull request #174 (2015-06-18 14:53:10)

Make missing directories when downloading datasets
Makes missing directories when downloading datasets.


Patch:
diff --git a/bin/fuel-convert b/bin/fuel-convert
index bf2aab0..a6f5e33 100755
--- a/bin/fuel-convert
+++ b/bin/fuel-convert
@@ -10,14 +10,6 @@ from fuel.converters.base import MissingInputFiles
 from fuel.datasets import H5PYDataset
 
 
-class CheckDirectoryAction(argparse.Action):
-    def __call__(self, parser, namespace, values, option_string=None):
-        if os.path.isdir(values):
-            setattr(namespace, self.dest, values)
-        else:
-            raise ValueError('{} is not a existing directory'.format(values))
-
-
 if __name__ == "__main__":
     built_in_datasets = dict(converters.all_converters)
     parser = argparse.ArgumentParser(
@@ -32,10 +24,9 @@ if __name__ == "__main__":
             name, parents=[parent_parser],
             help='Convert the {} dataset'.format(name))
         subparser.add_argument(
-            "-o", "--output-directory", help="where to save the dataset",
-            type=str, default=os.getcwd(), action=CheckDirectoryAction)
+            "-o", "--output-file", help="where to save the dataset", type=str,
+            default=os.path.join(os.getcwd(), '{}.hdf5'.format(name)))
         subparser_fn(subparser)
-
     args = parser.parse_args()
     args_dict = vars(args)
     try:
@@ -44,22 +35,21 @@ if __name__ == "__main__":
         parser.print_usage()
         parser.exit()
     try:
-        output_paths = func(**args_dict)
+        func(**args_dict)
     except MissingInputFiles as e:
         intro = "The following required files were not found:\n"
         message = "\n".join([intro] + ["   * " + f for f in e.filenames])
         message += "\n\nDid you forget to run fuel-download?"
         parser.error(message)
 
-    # Tag the newly-created file(s) with H5PYDataset version and command-line
+    # Tag the newly-created file with H5PYDataset version and command-line
     # options
-    for output_path in output_paths:
-        h5file = h5py.File(output_path, 'a')
-        interface_version = H5PYDataset.interface_version.encode('utf-8')
-        h5file.attrs['h5py_interface_version'] = interface_version
-        fuel_convert_version = converters.__version__.encode('utf-8')
-        h5file.attrs['fuel_convert_version'] = fuel_convert_version
-        command = [os.path.basename(sys.argv[0])] + sys.argv[1:]
-        h5file.attrs['fuel_convert_command'] = ' '.join(command).encode('utf-8')
-        h5file.flush()
-        h5file.close()
+    h5file = h5py.File(args.output_file, 'a')
+    interface_version = H5PYDataset.interface_version.encode('utf-8')
+    h5file.attrs['h5py_interface_version'] = interface_version
+    fuel_convert_version = converters.__version__.encode('utf-8')
+    h5file.attrs['fuel_convert_version'] = fuel_convert_version
+    command = [os.path.basename(sys.argv[0])] + sys.argv[1:]
+    h5file.attrs['fuel_convert_command'] = ' '.join(command).encode('utf-8')
+    h5file.flush()
+    h5file.close()
diff --git a/docs/new_dataset.rst b/docs/new_dataset.rst
index fdab37e..f018f21 100644
--- a/docs/new_dataset.rst
+++ b/docs/new_dataset.rst
@@ -115,10 +115,7 @@ very similar to ``fuel-download``. The arguments to be aware of in the subparser
 are
 
 * ``directory`` : in which directory the input files reside
-* ``output-directory`` : where to save the converted dataset
-
-The converter function should return a tuple containing path(s) to the converted
-dataset(s).
+* ``output-file`` : where to save the converted dataset
 
 Put the following piece of code inside the ``fuel.converters.iris``
 module (you'll have to create it):
@@ -134,9 +131,8 @@ module (you'll have to create it):
     from fuel.converters.base import fill_hdf5_file
 
 
-    def convert_iris(directory, output_directory, output_filename='iris.hdf5'):
-        output_path = os.path.join(output_directory, output_filename)
-        h5file = h5py.File(output_path, mode='w')
+    def convert_iris(directory, output_file):
+        h5file = h5py.File(output_file, mode='w')
         classes = {'Iris-setosa': 0, 'Iris-versicolor': 1, 'Iris-virginica': 2}
         data = numpy.loadtxt(
             os.path.join(directory, 'iris.data'),
@@ -164,8 +160,6 @@ module (you'll have to create it):
         h5file.flush()
         h5file.close()
 
-        return (output_path,)
-
     def fill_subparser(subparser):
         subparser.set_defaults(func=convert_iris)
 
@@ -294,9 +288,8 @@ You can now use the Iris dataset like you would use any other built-in dataset:
     >>> import h5py
     >>> import numpy
     >>> from fuel.converters.base import fill_hdf5_file
-    >>> def iris_converter(directory, output_directory, output_filename='iris.hdf5'):
-    ...     output_path = os.path.join(output_directory, output_filename)
-    ...     h5file = h5py.File(output_path, mode='w')
+    >>> def iris_converter(directory, output_file):
+    ...     h5file = h5py.File(output_file, mode='w')
     ...     classes = {b'Iris-setosa': 0, b'Iris-versicolor': 1, b'Iris-virginica': 2}
     ...     data = numpy.loadtxt(
     ...         os.path.join(directory, 'iris.data'),
@@ -322,19 +315,17 @@ You can now use the Iris dataset like you would use any other built-in dataset:
     ...     h5file['targets'].dims[1].label = 'index'
     ...     h5file.flush()
     ...     h5file.close()
-    ...     return (output_path,)
     >>> def fill_converter_subparser(subparser):
     ...     subparser.set_defaults(func=iris_converter)
     >>> parser = argparse.ArgumentParser()
     >>> __ = parser.add_argument("--directory", type=str, default=os.getcwd())
-    >>> __ = parser.add_argument("--output-directory", type=str,
-    ...                          default=os.getcwd())
+    >>> __ = parser.add_argument("--output-file", type=str, default='iris.hdf5')
     >>> subparsers = parser.add_subparsers()
     >>> fill_converter_subparser(subparsers.add_parser('iris'))
     >>> args = parser.parse_args(['iris'])
     >>> args_dict = vars(args)
     >>> func = args_dict.pop('func')
-    >>> output_paths = func(**args_dict)
+    >>> func(**args_dict)
     >>> os.remove('iris.data')
 
 .. doctest::
diff --git a/fuel/converters/__init__.py b/fuel/converters/__init__.py
index 259aceb..27866ac 100644
--- a/fuel/converters/__init__.py
+++ b/fuel/converters/__init__.py
@@ -16,7 +16,7 @@ from fuel.converters import cifar100
 from fuel.converters import mnist
 from fuel.converters import svhn
 
-__version__ = '0.2'
+__version__ = '0.1'
 all_converters = (
     ('binarized_mnist', binarized_mnist.fill_subparser),
     ('cifar10', cifar10.fill_subparser),
diff --git a/fuel/converters/binarized_mnist.py b/fuel/converters/binarized_mnist.py
index 7a77292..087a0f4 100644
--- a/fuel/converters/binarized_mnist.py
+++ b/fuel/converters/binarized_mnist.py
@@ -14,8 +14,7 @@ ALL_FILES = [TRAIN_FILE, VALID_FILE, TEST_FILE]
 
 
 @check_exists(required_files=ALL_FILES)
-def convert_binarized_mnist(directory, output_directory,
-                            output_filename='binarized_mnist.hdf5'):
+def convert_binarized_mnist(directory, output_file):
     """Converts the binarized MNIST dataset to HDF5.
 
     Converts the binarized MNIST dataset used in R. Salakhutdinov's DBN
@@ -38,19 +37,11 @@ def convert_binarized_mnist(directory, output_directory,
     ----------
     directory : str
         Directory in which input files reside.
-    output_directory : str
-        Directory in which to save the converted dataset.
-    output_filename : str, optional
-        Name of the saved dataset. Defaults to 'binarized_mnist.hdf5'.
-
-    Returns
-    -------
-    output_paths : tuple of str
-        Single-element tuple containing the path to the converted dataset.
+    output_file : str
+        Where to save the converted dataset.
 
     """
-    output_path = os.path.join(output_directory, output_filename)
-    h5file = h5py.File(output_path, mode='w')
+    h5file = h5py.File(output_file, mode='w')
 
     train_set = numpy.loadtxt(
         os.path.join(directory, TRAIN_FILE)).reshape(
@@ -71,8 +62,6 @@ def convert_binarized_mnist(directory, output_directory,
     h5file.flush()
     h5file.close()
 
-    return (output_path,)
-
 
 def fill_subparser(subparser):
     """Sets up a subparser to convert the binarized MNIST dataset files.
diff --git a/fuel/converters/cifar10.py b/fuel/converters/cifar10.py
index 9aaa177..3c567ba 100644
--- a/fuel/converters/cifar10.py
+++ b/fuel/converters/cifar10.py
@@ -12,8 +12,7 @@ DISTRIBUTION_FILE = 'cifar-10-python.tar.gz'
 
 
 @check_exists(required_files=[DISTRIBUTION_FILE])
-def convert_cifar10(directory, output_directory,
-                    output_filename='cifar10.hdf5'):
+def convert_cifar10(directory, output_file):
     """Converts the CIFAR-10 dataset to HDF5.
 
     Converts the CIFAR-10 dataset to an HDF5 dataset compatible with
@@ -28,19 +27,11 @@ def convert_cifar10(directory, output_directory,
     ----------
     directory : str
         Directory in which input files reside.
-    output_directory : str
-        Directory in which to save the converted dataset.
-    output_filename : str, optional
-        Name of the saved dataset. Defaults to 'cifar10.hdf5'.
-
-    Returns
-    -------
-    output_paths : tuple of str
-        Single-element tuple containing the path to the converted dataset.
+    output_file : str
+        Where to save the converted dataset.
 
     """
-    output_path = os.path.join(output_directory, output_filename)
-    h5file = h5py.File(output_path, mode='w')
+    h5file = h5py.File(output_file, mode='w')
     input_file = os.path.join(directory, DISTRIBUTION_FILE)
     tar_file = tarfile.open(input_file, 'r:gz')
 
@@ -94,8 +85,6 @@ def convert_cifar10(directory, output_directory,
     h5file.flush()
     h5file.close()
 
-    return (output_path,)
-
 
 def fill_subparser(subparser):
     """Sets up a subparser to convert the CIFAR10 dataset files.
diff --git a/fuel/converters/cifar100.py b/fuel/converters/cifar100.py
index 23bcbc1..4ff549d 100644
--- a/fuel/converters/cifar100.py
+++ b/fuel/converters/cifar100.py
@@ -12,8 +12,7 @@ DISTRIBUTION_FILE = 'cifar-100-python.tar.gz'
 
 
 @check_exists(required_files=[DISTRIBUTION_FILE])
-def convert_cifar100(directory, output_directory,
-                     output_filename='cifar100.hdf5'):
+def convert_cifar100(directory, output_file):
     """Converts the CIFAR-100 dataset to HDF5.
 
     Converts the CIFAR-100 dataset to an HDF5 dataset compatible with
@@ -27,19 +26,11 @@ def convert_cifar100(directory, output_directory,
     ----------
     directory : str
         Directory in which the required input files reside.
-    output_directory : str
-        Directory in which to save the converted dataset.
-    output_filename : str, optional
-        Name of the saved dataset. Defaults to 'cifar100.hdf5'.
-
-    Returns
-    -------
-    output_paths : tuple of str
-        Single-element tuple containing the path to the converted dataset.
+    output_file : str
+        Where to save the converted dataset.
 
     """
-    output_path = os.path.join(output_directory, output_filename)
-    h5file = h5py.File(output_path, mode="w")
+    h5file = h5py.File(output_file, mode="w")
     input_file = os.path.join(directory, 'cifar-100-python.tar.gz')
     tar_file = tarfile.open(input_file, 'r:gz')
 
@@ -92,8 +83,6 @@ def convert_cifar100(directory, output_directory,
     h5file.flush()
     h5file.close()
 
-    return (output_path,)
-
 
 def fill_subparser(subparser):
     """Sets up a subparser to convert the CIFAR100 dataset files.
diff --git a/fuel/converters/mnist.py b/fuel/converters/mnist.py
index 4f4d3ab..d5e83dd 100644
--- a/fuel/converters/mnist.py
+++ b/fuel/converters/mnist.py
@@ -19,8 +19,7 @@ ALL_FILES = [TRAIN_IMAGES, TRAIN_LABELS, TEST_IMAGES, TEST_LABELS]
 
 
 @check_exists(required_files=ALL_FILES)
-def convert_mnist(directory, output_directory, output_filename=None,
-                  dtype=None):
+def convert_mnist(directory, output_file, dtype=None):
     """Converts the MNIST dataset to HDF5.
 
     Converts the MNIST dataset to an HDF5 dataset compatible with
@@ -42,29 +41,15 @@ def convert_mnist(directory, output_directory, output_filename=None,
     ----------
     directory : str
         Directory in which input files reside.
-    output_directory : str
-        Directory in which to save the converted dataset.
-    output_filename : str, optional
-        Name of the saved dataset. Defaults to `None`, in which case a name
-        based on `dtype` will be used.
+    output_file : str
+        Where to save the converted dataset.
     dtype : str, optional
         Either 'float32', 'float64', or 'bool'. Defaults to `None`,
         in which case images will be returned in their original
         unsigned byte format.
 
-    Returns
-    -------
-    output_paths : tuple of str
-        Single-element tuple containing the path to the converted dataset.
-
     """
-    if not output_filename:
-        if dtype:
-            output_filename = 'mnist_{}.hdf5'.format(dtype)
-        else:
-            output_filename = 'mnist.hdf5'
-    output_path = os.path.join(output_directory, output_filename)
-    h5file = h5py.File(output_path, mode='w')
+    h5file = h5py.File(output_file, mode='w')
 
     train_feat_path = os.path.join(directory, TRAIN_IMAGES)
     train_features = read_mnist_images(train_feat_path, dtype)
@@ -89,8 +74,6 @@ def convert_mnist(directory, output_directory, output_filename=None,
     h5file.flush()
     h5file.close()
 
-    return (output_path,)
-
 
 def fill_subparser(subparser):
     """Sets up a subparser to convert the MNIST dataset files.
diff --git a/fuel/converters/svhn.py b/fuel/converters/svhn.py
index 1aa9e5d..605fbf8 100644
--- a/fuel/converters/svhn.py
+++ b/fuel/converters/svhn.py
@@ -22,8 +22,7 @@ FORMAT_2_TRAIN_FILE, FORMAT_2_TEST_FILE, FORMAT_2_EXTRA_FILE = FORMAT_2_FILES
 
 
 @check_exists(required_files=FORMAT_1_FILES)
-def convert_svhn_format_1(directory, output_directory,
-                          output_filename='svhn_format_1.hdf5'):
+def convert_svhn_format_1(directory, output_file):
     """Converts the SVHN dataset (format 1) to HDF5.
 
     This method assumes the existence of the files
@@ -36,20 +35,12 @@ def convert_svhn_format_1(directory, output_directory,
     ----------
     directory : str
         Directory in which input files reside.
-    output_directory : str
-        Directory in which to save the converted dataset.
-    output_filename : str, optional
-        Name of the saved dataset. Defaults to 'svhn_format_1.hdf5'.
-
-    Returns
-    -------
-    output_paths : tuple of str
-        Single-element tuple containing the path to the converted dataset.
+    output_file : str
+        Where to save the converted dataset.
 
     """
     try:
-        output_path = os.path.join(output_directory, output_filename)
-        h5file = h5py.File(output_path, mode='w')
+        h5file = h5py.File(output_file, mode='w')
         TMPDIR = tempfile.mkdtemp()
 
         # Every image has three channels (RGB) and variable height and width.
@@ -258,12 +249,9 @@ def convert_svhn_format_1(directory, output_directory,
         h5file.flush()
         h5file.close()
 
-    return (output_path,)
-
 
 @check_exists(required_files=FORMAT_2_FILES)
-def convert_svhn_format_2(directory, output_directory,
-                          output_filename='svhn_format_2.hdf5'):
+def convert_svhn_format_2(directory, output_file):
     """Converts the SVHN dataset (format 2) to HDF5.
 
     This method assumes the existence of the files
@@ -276,19 +264,11 @@ def convert_svhn_format_2(directory, output_directory,
     ----------
     directory : str
         Directory in which input files reside.
-    output_directory : str
-        Directory in which to save the converted dataset.
-    output_filename : str, optional
-        Name of the saved dataset. Defaults to 'svhn_format_2.hdf5'.
-
-    Returns
-    -------
-    output_paths : tuple of str
-        Single-element tuple containing the path to the converted dataset.
+    output_file : str
+        Where to save the converted dataset.
 
     """
-    output_path = os.path.join(output_directory, output_filename)
-    h5file = h5py.File(output_path, mode='w')
+    h5file = h5py.File(output_file, mode='w')
 
     train_set = loadmat(os.path.join(directory, FORMAT_2_TRAIN_FILE))
     train_features = train_set['X'].transpose(3, 2, 0, 1)
@@ -317,11 +297,8 @@ def convert_svhn_format_2(directory, output_directory,
     h5file.flush()
     h5file.close()
 
-    return (output_path,)
 
-
-def convert_svhn(which_format, directory, output_directory,
-                 output_filename=None):
+def convert_svhn(which_format, directory, output_file):
     """Converts the SVHN dataset to HDF5.
 
     Converts the SVHN dataset [SVHN] to an HDF5 dataset compatible
@@ -341,28 +318,17 @@ def convert_svhn(which_format, directory, output_directory,
         or format 2: cropped digits) to convert.
     directory : str
         Directory in which input files reside.
-    output_directory : str
-        Directory in which to save the converted dataset.
-    output_filename : str, optional
-        Name of the saved dataset. Defaults to 'svhn_format_1.hdf5' or
-        'svhn_format_2.hdf5', depending on `which_format`.
-
-    Returns
-    -------
-    output_paths : tuple of str
-        Single-element tuple containing the path to the converted dataset.
+    output_file : str
+        Where to save the converted dataset.
 
     """
     if which_format not in (1, 2):
         raise ValueError("SVHN format needs to be either 1 or 2.")
-    if not output_filename:
-        output_filename = 'svhn_format_{}.hdf5'.format(which_format)
+    output_file = output_file.format(which_format)
     if which_format == 1:
-        return convert_svhn_format_1(
-            directory, output_directory, output_filename)
+        convert_svhn_format_1(directory, output_file)
     else:
-        return convert_svhn_format_2(
-            directory, output_directory, output_filename)
+        convert_svhn_format_2(directory, output_file)
 
 
 def fill_subparser(subparser):
@@ -376,4 +342,6 @@ def fill_subparser(subparser):
     """
     subparser.add_argument(
         "which_format", help="which dataset format", type=int, choices=(1, 2))
-    subparser.set_defaults(func=convert_svhn)
+    subparser.set_defaults(
+        func=convert_svhn,
+        output_file=os.path.join(os.getcwd(), 'svhn_format_{}.hdf5'))
diff --git a/fuel/downloaders/base.py b/fuel/downloaders/base.py
index 2bbdc8b..e3236c1 100644
--- a/fuel/downloaders/base.py
+++ b/fuel/downloaders/base.py
@@ -81,6 +81,22 @@ def download(url, file_handle, chunk_size=1024):
             file_handle.write(chunk)
 
 
+def ensure_directory_exists(directory):
+    """Create directory (with parents) if does not exist, raise on failure.
+
+    Parameters
+    ----------
+    directory : str
+        The directory to create
+
+    """
+    try:
+        os.makedirs(directory)
+    except OSError as e:
+        if e.errno != os.errno.EEXIST:
+            raise
+
+
 def default_downloader(directory, urls, filenames, url_prefix=None,
                        clear=False):
     """Downloads or clears files from URLs and filenames.
@@ -117,6 +133,8 @@ def default_downloader(directory, urls, filenames, url_prefix=None,
                 os.remove(f)
     else:
         print('Downloading ' + ', '.join(filenames) + '\n')
+        ensure_directory_exists(directory)
+
         for url, f, n in zip(urls, files, filenames):
             if not url:
                 if url_prefix is None:


Test patch:
diff --git a/tests/test_converters.py b/tests/test_converters.py
index 0e94617..f558c35 100644
--- a/tests/test_converters.py
+++ b/tests/test_converters.py
@@ -132,17 +132,17 @@ class TestMNIST(object):
         shutil.rmtree(self.tempdir)
 
     def test_converter(self):
+        filename = os.path.join(self.tempdir, 'mock_mnist.hdf5')
         parser = argparse.ArgumentParser()
         subparsers = parser.add_subparsers()
         subparser = subparsers.add_parser('mnist')
         subparser.set_defaults(
-            directory=self.tempdir, output_directory=self.tempdir,
-            output_filename='mock_mnist.hdf5')
+            directory=self.tempdir, output_file=filename)
         mnist.fill_subparser(subparser)
         args = parser.parse_args(['mnist'])
         args_dict = vars(args)
         func = args_dict.pop('func')
-        filename, = func(**args_dict)
+        func(**args_dict)
         h5file = h5py.File(filename, mode='r')
         assert_equal(
             h5file['features'][...],
@@ -158,32 +158,6 @@ class TestMNIST(object):
         assert_equal(tuple(dim.label for dim in h5file['targets'].dims),
                      ('batch', 'index'))
 
-    def test_converter_no_filename_no_dtype(self):
-        parser = argparse.ArgumentParser()
-        subparsers = parser.add_subparsers()
-        subparser = subparsers.add_parser('mnist')
-        subparser.set_defaults(
-            directory=self.tempdir, output_directory=self.tempdir)
-        mnist.fill_subparser(subparser)
-        args = parser.parse_args(['mnist'])
-        args_dict = vars(args)
-        func = args_dict.pop('func')
-        filename, = func(**args_dict)
-        assert_equal(os.path.basename(filename), 'mnist.hdf5')
-
-    def test_converter_no_filename(self):
-        parser = argparse.ArgumentParser()
-        subparsers = parser.add_subparsers()
-        subparser = subparsers.add_parser('mnist')
-        subparser.set_defaults(
-            directory=self.tempdir, output_directory=self.tempdir)
-        mnist.fill_subparser(subparser)
-        args = parser.parse_args(['mnist', '--dtype', 'bool'])
-        args_dict = vars(args)
-        func = args_dict.pop('func')
-        filename, = func(**args_dict)
-        assert_equal(os.path.basename(filename), 'mnist_bool.hdf5')
-
     def test_wrong_image_magic(self):
         assert_raises(
             ValueError, mnist.read_mnist_images, self.wrong_images_path)
@@ -227,17 +201,16 @@ class TestBinarizedMNIST(object):
         shutil.rmtree(self.tempdir)
 
     def test_converter(self):
+        filename = os.path.join(self.tempdir, 'mock_binarized_mnist.hdf5')
         parser = argparse.ArgumentParser()
         subparsers = parser.add_subparsers()
         subparser = subparsers.add_parser('binarized_mnist')
-        subparser.set_defaults(
-            directory=self.tempdir, output_directory=self.tempdir,
-            output_filename='mock_binarized_mnist.hdf5')
+        subparser.set_defaults(directory=self.tempdir, output_file=filename)
         binarized_mnist.fill_subparser(subparser)
         args = parser.parse_args(['binarized_mnist'])
         args_dict = vars(args)
         func = args_dict.pop('func')
-        filename, = func(**args_dict)
+        func(**args_dict)
         h5file = h5py.File(filename, mode='r')
         assert_equal(h5file['features'][...],
                      numpy.vstack([self.train_mock, self.valid_mock,
@@ -283,17 +256,16 @@ class TestCIFAR10(object):
         shutil.rmtree(self.tempdir)
 
     def test_converter(self):
+        filename = os.path.join(self.tempdir, 'mock_cifar10.hdf5')
         parser = argparse.ArgumentParser()
         subparsers = parser.add_subparsers()
         subparser = subparsers.add_parser('cifar10')
-        subparser.set_defaults(
-            directory=self.tempdir, output_directory=self.tempdir,
-            output_filename='mock_cifar10.hdf5')
+        subparser.set_defaults(directory=self.tempdir, output_file=filename)
         cifar10.fill_subparser(subparser)
         args = parser.parse_args(['cifar10'])
         args_dict = vars(args)
         func = args_dict.pop('func')
-        filename, = func(**args_dict)
+        func(**args_dict)
         h5file = h5py.File(filename, mode='r')
         assert_equal(
             h5file['features'][...],
@@ -348,17 +320,16 @@ class TestCIFAR100(object):
         shutil.rmtree(self.tempdir)
 
     def test_converter(self):
+        filename = os.path.join(self.tempdir, 'mock_cifar100.hdf5')
         parser = argparse.ArgumentParser()
         subparsers = parser.add_subparsers()
         subparser = subparsers.add_parser('cifar100')
-        subparser.set_defaults(
-            directory=self.tempdir, output_directory=self.tempdir,
-            output_filename='mock_cifar100.hdf5')
+        subparser.set_defaults(directory=self.tempdir, output_file=filename)
         cifar100.fill_subparser(subparser)
         args = parser.parse_args(['cifar100'])
         args_dict = vars(args)
         func = args_dict.pop('func')
-        filename, = func(**args_dict)
+        func(**args_dict)
         h5file = h5py.File(filename, mode='r')
         assert_equal(
             h5file['features'][...],
@@ -456,17 +427,16 @@ class TestSVHN(object):
         shutil.rmtree(self.tempdir)
 
     def test_format_1_converter(self):
+        filename = os.path.join(self.tempdir, 'svhn_format_1.hdf5')
         parser = argparse.ArgumentParser()
         subparsers = parser.add_subparsers()
         subparser = subparsers.add_parser('svhn')
         svhn.fill_subparser(subparser)
-        subparser.set_defaults(
-            directory=self.tempdir, output_directory=self.tempdir,
-            output_filename='svhn_format_1.hdf5')
+        subparser.set_defaults(directory=self.tempdir, output_file=filename)
         args = parser.parse_args(['svhn', '1'])
         args_dict = vars(args)
         func = args_dict.pop('func')
-        filename, = func(**args_dict)
+        func(**args_dict)
         h5file = h5py.File(filename, mode='r')
 
         expected_features = sum((self.f1_mock[split]['image']
@@ -486,17 +456,16 @@ class TestSVHN(object):
             assert_equal(val, truth)
 
     def test_format_2_converter(self):
+        filename = os.path.join(self.tempdir, 'svhn_format_2.hdf5')
         parser = argparse.ArgumentParser()
         subparsers = parser.add_subparsers()
         subparser = subparsers.add_parser('svhn')
         svhn.fill_subparser(subparser)
-        subparser.set_defaults(
-            directory=self.tempdir, output_directory=self.tempdir,
-            output_filename='svhn_format_2.hdf5')
+        subparser.set_defaults(directory=self.tempdir, output_file=filename)
         args = parser.parse_args(['svhn', '2'])
         args_dict = vars(args)
         func = args_dict.pop('func')
-        filename, = func(**args_dict)
+        func(**args_dict)
         h5file = h5py.File(filename, mode='r')
         assert_equal(
             h5file['features'][...],
@@ -516,10 +485,14 @@ class TestSVHN(object):
                      ('batch', 'index'))
 
     @mock.patch('fuel.converters.svhn.convert_svhn_format_1')
-    def test_converter_default_filename(self, mock_converter_format_1):
-        svhn.convert_svhn(1, './', './')
-        mock_converter_format_1.assert_called_with(
-            './', './', 'svhn_format_1.hdf5')
+    def test_converter_call_format_1(self, mock_converter_format_1):
+        svhn.convert_svhn(1, './', 'svhn_format_{}.hdf5')
+        mock_converter_format_1.assert_called_with('./', 'svhn_format_1.hdf5')
+
+    @mock.patch('fuel.converters.svhn.convert_svhn_format_2')
+    def test_converter_call_format_2(self, mock_converter_format_2):
+        svhn.convert_svhn(2, './', 'svhn_format_{}.hdf5')
+        mock_converter_format_2.assert_called_with('./', 'svhn_format_2.hdf5')
 
     def test_converter_error_wrong_format(self):
         assert_raises(ValueError, svhn.convert_svhn, 3, './', 'mock.hdf5')
diff --git a/tests/test_downloaders.py b/tests/test_downloaders.py
index f7607c3..06baa84 100644
--- a/tests/test_downloaders.py
+++ b/tests/test_downloaders.py
@@ -9,7 +9,8 @@ from numpy.testing import assert_equal, assert_raises
 
 from fuel.downloaders import mnist, binarized_mnist, cifar10, cifar100, svhn
 from fuel.downloaders.base import (download, default_downloader,
-                                   filename_from_url, NeedURLPrefix)
+                                   filename_from_url, NeedURLPrefix,
+                                   ensure_directory_exists)
 from picklable_itertools import chain
 from six.moves import range
 
@@ -70,6 +71,25 @@ class TestDownload(object):
             assert_equal(f.read(), mock_content)
 
 
+def test_ensure_directory_exists():
+    parent = tempfile.mkdtemp()
+    dirpath = os.path.join(parent, 'a', 'b')
+    filepath = os.path.join(dirpath, 'f')
+
+    # multiple checks for the same dir are fine
+    ensure_directory_exists(dirpath)
+    ensure_directory_exists(dirpath)
+
+    assert os.path.exists(dirpath)
+
+    with open(filepath, 'w') as f:
+        f.write(' ')
+
+    assert_raises(ensure_directory_exists(filepath))
+
+    shutil.rmtree(dirpath, ignore_errors=True)
+
+
 def test_mnist():
     parser = argparse.ArgumentParser()
     subparsers = parser.add_subparsers()