# Buggy code
def spd_reader(
    filename,
    endianess="<",
    nav_units=None,
    spc_fname=None,
    ipr_fname=None,
    load_all_spc=False,
    **kwargs,
):
    """
    Read data from an SPD spectral map specified by filename.

    Parameters
    ----------
    filename : str
        Name of SPD file to read
    endianess : char
        Byte-order of data to read
    nav_units : 'nm', 'um', or None
        Default navigation units for EDAX data is in microns, so this is the
        default unit to save in the signal. Can also be specified as 'nm',
        which will output a signal with nm scale instead.
    spc_fname : None or str
        Name of file from which to read the spectral calibration. If data
        was exported fully from EDAX TEAM software, an .spc file with the
        same name as the .spd should be present.
        If `None`, the default filename will be searched for.
        Otherwise, the name of the .spc file to use for calibration can
        be explicitly given as a string.
    ipr_fname : None or str
        Name of file from which to read the spatial calibration. If data
        was exported fully from EDAX TEAM software, an .ipr file with the
        same name as the .spd (plus a \"_Img\" suffix) should be present.
        If `None`, the default filename will be searched for.
        Otherwise, the name of the .ipr file to use for spatial calibration
        can be explicitly given as a string.
    load_all_spc : bool
        Switch to control if all of the .spc header is read, or just the
        important parts for import into HyperSpy
    kwargs**
        Remaining arguments are passed to the Numpy ``memmap`` function

    Returns
    -------
    list
        list with dictionary of signal information to be passed back to
        hyperspy.io.load_with_reader
    """
    with open(filename, "rb") as f:
        spd_header = np.fromfile(f, dtype=get_spd_dtype_list(endianess), count=1)

        original_metadata = {"spd_header": sarray2dict(spd_header)}

        # dimensions of map data:
        nx = original_metadata["spd_header"]["nPoints"]
        ny = original_metadata["spd_header"]["nLines"]
        nz = original_metadata["spd_header"]["nChannels"]
        data_offset = original_metadata["spd_header"]["dataOffset"]
        data_type = {"1": "u1", "2": "u2", "4": "u4"}[
            str(original_metadata["spd_header"]["countBytes"])
        ]
        lazy = kwargs.pop("lazy", False)
        mode = kwargs.pop("mode", "c")
        if lazy:
            mode = "r"

        # Read data from file into a numpy memmap object
        data = (
            np.memmap(f, mode=mode, offset=data_offset, dtype=data_type, **kwargs)
            .squeeze()
            .reshape((nz, nx, ny), order="F")
            .T
        )

    # Convert char arrays to strings:
    original_metadata["spd_header"]["tag"] = spd_header["tag"][0].view("S16")[0]
    # fName is the name of the .bmp (and .ipr) file of the map
    original_metadata["spd_header"]["fName"] = spd_header["fName"][0].view("S120")[0]

    # Get name of .spc file from the .spd map (if not explicitly given):
    if spc_fname is None:
        spc_path = os.path.dirname(filename)
        spc_basename = os.path.splitext(os.path.basename(filename))[0] + ".spc"
        spc_fname = os.path.join(spc_path, spc_basename)

    # Get name of .ipr file from bitmap image (if not explicitly given):
    if ipr_fname is None:
        ipr_basename = (
            os.path.splitext(
                os.path.basename(original_metadata["spd_header"]["fName"])
            )[0].decode()
            + ".ipr"
        )
        ipr_path = os.path.dirname(filename)
        ipr_fname = os.path.join(ipr_path, ipr_basename)

    # Flags to control reading of files
    read_spc = os.path.isfile(spc_fname)
    read_ipr = os.path.isfile(ipr_fname)

    # Read the .ipr header (if possible)
    if read_ipr:
        with open(ipr_fname, "rb") as f:
            _logger.debug(" From .spd reader - reading .ipr {}".format(ipr_fname))
            ipr_header = __get_ipr_header(f, endianess)
            original_metadata["ipr_header"] = sarray2dict(ipr_header)
    else:
        _logger.warning(
            "Could not find .ipr file named {}.\n"
            "No spatial calibration will be loaded."
            "\n".format(ipr_fname)
        )

    # Read the .spc header (if possible)
    if read_spc:
        with open(spc_fname, "rb") as f:
            _logger.debug(" From .spd reader - reading .spc {}".format(spc_fname))
            spc_header = __get_spc_header(f, endianess, load_all_spc)
            spc_dict = sarray2dict(spc_header)
            original_metadata["spc_header"] = spc_dict
    else:
        _logger.warning(
            "Could not find .spc file named {}.\n"
            "No spectral metadata will be loaded."
            "\n".format(spc_fname)
        )

    # create the energy axis dictionary:
    energy_axis = {
        "size": data.shape[2],
        "index_in_array": 2,
        "name": "Energy",
        "scale": original_metadata["spc_header"]["evPerChan"] / 1000.0
        if read_spc
        else 1,
        "offset": original_metadata["spc_header"]["startEnergy"] if read_spc else 1,
        "units": "keV" if read_spc else t.Undefined,
    }

    # Handle navigation units input:
    scale = 1000 if nav_units == "nm" else 1
    if nav_units is not "nm":
        if nav_units not in [None, "um"]:
            _logger.warning(
                'Did not understand nav_units input "{}". '
                "Defaulting to microns.\n".format(nav_units)
            )
        nav_units = r"$\mu m$"

    # Create navigation axes dictionaries:
    x_axis = {
        "size": data.shape[1],
        "index_in_array": 1,
        "name": "x",
        "scale": original_metadata["ipr_header"]["mppX"] * scale if read_ipr else 1,
        "offset": 0,
        "units": nav_units if read_ipr else t.Undefined,
    }

    y_axis = {
        "size": data.shape[0],
        "index_in_array": 0,
        "name": "y",
        "scale": original_metadata["ipr_header"]["mppY"] * scale if read_ipr else 1,
        "offset": 0,
        "units": nav_units if read_ipr else t.Undefined,
    }

    # Assign metadata for spectrum image:
    metadata = {
        "General": {
            "original_filename": os.path.split(filename)[1],
            "title": "EDS Spectrum Image",
        },
        "Signal": {
            "signal_type": "EDS_SEM",
            "record_by": "spectrum",
        },
    }

    # Add spectral calibration and elements (if present):
    if read_spc:
        metadata = _add_spc_metadata(metadata, spc_dict)

    # Define navigation and signal axes:
    axes = [y_axis, x_axis, energy_axis]

    dictionary = {
        "data": data,
        "axes": axes,
        "metadata": metadata,
        "original_metadata": original_metadata,
    }

    return [
        dictionary,
    ]


# Traceback (TypeError)
s = hs.load('pyrite.hspy')
crop = s.inav[:,:,::5].isig[:2.6]
crop.compute()
crop.save('pyrite_50frames_2_6keV.hspy')
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
c:\users\jat\git_repos\hyperspy\hyperspy\io_plugins\hspy.py in overwrite_dataset(group, data, key, signal_axes, **kwds)
548
--> 549             dset = group.require_dataset(key, **these_kwds)
550             got_data = True

C:\anaconda3\envs\hyperspy\lib\site-packages\h5py\_hl\group.py in require_dataset(self, name, shape, dtype, exact, **kwds)
126             if not name in self:
--> 127                 return self.create_dataset(name, *(shape, dtype), **kwds)
128

C:\anaconda3\envs\hyperspy\lib\site-packages\h5py\_hl\group.py in create_dataset(self, name, shape, dtype, data, **kwds)
105         with phil:
--> 106             dsid = dataset.make_new_dset(self, shape, dtype, data, **kwds)
107             dset = dataset.Dataset(dsid)

C:\anaconda3\envs\hyperspy\lib\site-packages\h5py\_hl\dataset.py in make_new_dset(parent, shape, dtype, data, chunks, compression, shuffle, fletcher32, maxshape, compression_opts, fillvalue, scaleoffset, track_times)
99             dtype = numpy.dtype(dtype)
--> 100         tid = h5t.py_create(dtype, logical=1)
101

h5py\h5t.pyx in h5py.h5t.py_create()

h5py\h5t.pyx in h5py.h5t.py_create()

h5py\h5t.pyx in h5py.h5t.py_create()

TypeError: No conversion path for dtype: dtype('<U32')

During handling of the above exception, another exception occurred:

KeyError                                  Traceback (most recent call last)
<ipython-input-12-c5592dab8e1f> in <module>()
----> 1 small_crop.save('pyrite_50frames_2_6keV.hspy')

c:\users\jat\git_repos\hyperspy\hyperspy\signal.py in save(self, filename, overwrite, extension, **kwds)
2133             basename, ext = os.path.splitext(filename)
2134             filename = basename + '.' + extension
-> 2135         io.save(filename, self, overwrite=overwrite, **kwds)
2136
2137     def _replot(self):

c:\users\jat\git_repos\hyperspy\hyperspy\io.py in save(filename, signal, overwrite, **kwds)
480             overwrite = overwrite_method(filename)
481         if overwrite is True:
--> 482             writer.file_writer(filename, signal, **kwds)
483             _logger.info('The %s file was created' % filename)
484             folder, filename = os.path.split(os.path.abspath(filename))

c:\users\jat\git_repos\hyperspy\hyperspy\io_plugins\hspy.py in file_writer(filename, signal, *args, **kwds)
729             smd.record_by = ""
730         try:
--> 731             write_signal(signal, expg, **kwds)
732         except BaseException:
733             raise

c:\users\jat\git_repos\hyperspy\hyperspy\io_plugins\hspy.py in write_signal(signal, group, **kwds)
687     original_par = group.create_group(original_metadata)
688     dict2hdfgroup(signal.original_metadata.as_dictionary(), original_par,
--> 689                   **kwds)
690     learning_results = group.create_group('learning_results')
691     dict2hdfgroup(signal.learning_results.__dict__,

c:\users\jat\git_repos\hyperspy\hyperspy\io_plugins\hspy.py in dict2hdfgroup(dictionary, group, **kwds)
434         if isinstance(value, dict):
435             dict2hdfgroup(value, group.create_group(key),
--> 436                           **kwds)
437         elif isinstance(value, DictionaryTreeBrowser):
438             dict2hdfgroup(value.as_dictionary(),

c:\users\jat\git_repos\hyperspy\hyperspy\io_plugins\hspy.py in dict2hdfgroup(dictionary, group, **kwds)
434         if isinstance(value, dict):
435             dict2hdfgroup(value, group.create_group(key),
--> 436                           **kwds)
437         elif isinstance(value, DictionaryTreeBrowser):
438             dict2hdfgroup(value.as_dictionary(),

c:\users\jat\git_repos\hyperspy\hyperspy\io_plugins\hspy.py in dict2hdfgroup(dictionary, group, **kwds)
434         if isinstance(value, dict):
435             dict2hdfgroup(value, group.create_group(key),
--> 436                           **kwds)
437         elif isinstance(value, DictionaryTreeBrowser):
438             dict2hdfgroup(value.as_dictionary(),

c:\users\jat\git_repos\hyperspy\hyperspy\io_plugins\hspy.py in dict2hdfgroup(dictionary, group, **kwds)
434         if isinstance(value, dict):
435             dict2hdfgroup(value, group.create_group(key),
--> 436                           **kwds)
437         elif isinstance(value, DictionaryTreeBrowser):
438             dict2hdfgroup(value.as_dictionary(),

c:\users\jat\git_repos\hyperspy\hyperspy\io_plugins\hspy.py in dict2hdfgroup(dictionary, group, **kwds)
443             write_signal(value, group.require_group(kn))
444         elif isinstance(value, (np.ndarray, h5py.Dataset, da.Array)):
--> 445             overwrite_dataset(group, value, key, **kwds)
446         elif value is None:
447             group.attrs[key] = '_None_'

c:\users\jat\git_repos\hyperspy\hyperspy\io_plugins\hspy.py in overwrite_dataset(group, data, key, signal_axes, **kwds)
552             # if the shape or dtype/etc do not match,
553             # we delete the old one and create new in the next loop run
--> 554             del group[key]
555     if dset == data:
556         # just a reference to already created thing

h5py\_objects.pyx in h5py._objects.with_phil.wrapper()

h5py\_objects.pyx in h5py._objects.with_phil.wrapper()

C:\anaconda3\envs\hyperspy\lib\site-packages\h5py\_hl\group.py in __delitem__(self, name)
301     def __delitem__(self, name):
302         """ Delete (unlink) an item from this group. """
--> 303         self.id.unlink(self._e(name))
304
305     @with_phil

h5py\_objects.pyx in h5py._objects.with_phil.wrapper()

h5py\_objects.pyx in h5py._objects.with_phil.wrapper()

h5py\h5g.pyx in h5py.h5g.GroupID.unlink()

KeyError: "Couldn't delete link (callback link pointer is NULL (specified link may be '.' or not exist))"

# Fixed code
def spd_reader(
    filename,
    endianess="<",
    nav_units=None,
    spc_fname=None,
    ipr_fname=None,
    load_all_spc=False,
    **kwargs,
):
    """
    Read data from an SPD spectral map specified by filename.

    Parameters
    ----------
    filename : str
        Name of SPD file to read
    endianess : char
        Byte-order of data to read
    nav_units : 'nm', 'um', or None
        Default navigation units for EDAX data is in microns, so this is the
        default unit to save in the signal. Can also be specified as 'nm',
        which will output a signal with nm scale instead.
    spc_fname : None or str
        Name of file from which to read the spectral calibration. If data
        was exported fully from EDAX TEAM software, an .spc file with the
        same name as the .spd should be present.
        If `None`, the default filename will be searched for.
        Otherwise, the name of the .spc file to use for calibration can
        be explicitly given as a string.
    ipr_fname : None or str
        Name of file from which to read the spatial calibration. If data
        was exported fully from EDAX TEAM software, an .ipr file with the
        same name as the .spd (plus a \"_Img\" suffix) should be present.
        If `None`, the default filename will be searched for.
        Otherwise, the name of the .ipr file to use for spatial calibration
        can be explicitly given as a string.
    load_all_spc : bool
        Switch to control if all of the .spc header is read, or just the
        important parts for import into HyperSpy
    kwargs**
        Remaining arguments are passed to the Numpy ``memmap`` function

    Returns
    -------
    list
        list with dictionary of signal information to be passed back to
        hyperspy.io.load_with_reader
    """
    with open(filename, "rb") as f:
        spd_header = np.fromfile(f, dtype=get_spd_dtype_list(endianess), count=1)

        original_metadata = {"spd_header": sarray2dict(spd_header)}

        # dimensions of map data:
        nx = original_metadata["spd_header"]["nPoints"]
        ny = original_metadata["spd_header"]["nLines"]
        nz = original_metadata["spd_header"]["nChannels"]
        data_offset = original_metadata["spd_header"]["dataOffset"]
        data_type = {"1": "u1", "2": "u2", "4": "u4"}[
            str(original_metadata["spd_header"]["countBytes"])
        ]
        lazy = kwargs.pop("lazy", False)
        mode = kwargs.pop("mode", "c")
        if lazy:
            mode = "r"

        # Read data from file into a numpy memmap object
        data = (
            np.memmap(f, mode=mode, offset=data_offset, dtype=data_type, **kwargs)
            .squeeze()
            .reshape((nz, nx, ny), order="F")
            .T
        )

    # Convert char arrays to strings:
    original_metadata["spd_header"]["tag"] = spd_header["tag"][0].view("S16")[0]
    # fName is the name of the .bmp (and .ipr) file of the map
    original_metadata["spd_header"]["fName"] = spd_header["fName"][0].view("S120")[0]

    # Get name of .spc file from the .spd map (if not explicitly given):
    if spc_fname is None:
        spc_path = os.path.dirname(filename)
        spc_basename = os.path.splitext(os.path.basename(filename))[0] + ".spc"
        spc_fname = os.path.join(spc_path, spc_basename)

    # Get name of .ipr file from bitmap image (if not explicitly given):
    if ipr_fname is None:
        ipr_basename = (
            os.path.splitext(
                os.path.basename(original_metadata["spd_header"]["fName"])
            )[0].decode()
            + ".ipr"
        )
        ipr_path = os.path.dirname(filename)
        ipr_fname = os.path.join(ipr_path, ipr_basename)

    # Flags to control reading of files
    read_spc = os.path.isfile(spc_fname)
    read_ipr = os.path.isfile(ipr_fname)

    # Read the .ipr header (if possible)
    if read_ipr:
        with open(ipr_fname, "rb") as f:
            _logger.debug(" From .spd reader - reading .ipr {}".format(ipr_fname))
            ipr_header = __get_ipr_header(f, endianess)
            original_metadata["ipr_header"] = sarray2dict(ipr_header)

            # Workaround for type error when saving hdf5:
            # save as list of strings instead of numpy unicode array
            # see https://github.com/hyperspy/hyperspy/pull/2007 and
            #     https://github.com/h5py/h5py/issues/289 for context
            original_metadata["ipr_header"]["charText"] = [
                np.string_(i) for i in original_metadata["ipr_header"]["charText"]
            ]
    else:
        _logger.warning(
            "Could not find .ipr file named {}.\n"
            "No spatial calibration will be loaded."
            "\n".format(ipr_fname)
        )

    # Read the .spc header (if possible)
    if read_spc:
        with open(spc_fname, "rb") as f:
            _logger.debug(" From .spd reader - reading .spc {}".format(spc_fname))
            spc_header = __get_spc_header(f, endianess, load_all_spc)
            spc_dict = sarray2dict(spc_header)
            original_metadata["spc_header"] = spc_dict
    else:
        _logger.warning(
            "Could not find .spc file named {}.\n"
            "No spectral metadata will be loaded."
            "\n".format(spc_fname)
        )

    # create the energy axis dictionary:
    energy_axis = {
        "size": data.shape[2],
        "index_in_array": 2,
        "name": "Energy",
        "scale": original_metadata["spc_header"]["evPerChan"] / 1000.0
        if read_spc
        else 1,
        "offset": original_metadata["spc_header"]["startEnergy"] if read_spc else 1,
        "units": "keV" if read_spc else t.Undefined,
    }

    # Handle navigation units input:
    scale = 1000 if nav_units == "nm" else 1
    if nav_units is not "nm":
        if nav_units not in [None, "um"]:
            _logger.warning(
                'Did not understand nav_units input "{}". '
                "Defaulting to microns.\n".format(nav_units)
            )
        nav_units = r"$\mu m$"

    # Create navigation axes dictionaries:
    x_axis = {
        "size": data.shape[1],
        "index_in_array": 1,
        "name": "x",
        "scale": original_metadata["ipr_header"]["mppX"] * scale if read_ipr else 1,
        "offset": 0,
        "units": nav_units if read_ipr else t.Undefined,
    }

    y_axis = {
        "size": data.shape[0],
        "index_in_array": 0,
        "name": "y",
        "scale": original_metadata["ipr_header"]["mppY"] * scale if read_ipr else 1,
        "offset": 0,
        "units": nav_units if read_ipr else t.Undefined,
    }

    # Assign metadata for spectrum image:
    metadata = {
        "General": {
            "original_filename": os.path.split(filename)[1],
            "title": "EDS Spectrum Image",
        },
        "Signal": {
            "signal_type": "EDS_SEM",
            "record_by": "spectrum",
        },
    }

    # Add spectral calibration and elements (if present):
    if read_spc:
        metadata = _add_spc_metadata(metadata, spc_dict)

    # Define navigation and signal axes:
    axes = [y_axis, x_axis, energy_axis]

    dictionary = {
        "data": data,
        "axes": axes,
        "metadata": metadata,
        "original_metadata": original_metadata,
    }

    return [
        dictionary,
    ]