# Buggy code
def export_to_dictionary(target, whitelist, dic, fullcopy=True):
    """Exports attributes of target from whitelist.keys() to dictionary dic
    All values are references only by default.

    Parameters
    ----------
        target : object
            must contain the (nested) attributes of the whitelist.keys()
        whitelist : dictionary
            A dictionary, keys of which are used as attributes for exporting.
            Key 'self' is only available with tag 'id', when the id of the
            target is saved. The values are either None, or a tuple, where:
                - the first item a string, which containts flags, separated by
                commas.
                - the second item is None if no 'init' flag is given, otherwise
                the object required for the initialization.
            The flag conventions are as follows:
            * 'init':
                object used for initialization of the target. The object is
                saved in the tuple in whitelist
            * 'fn':
                the targeted attribute is a function, and may be pickled
                (preferably with dill package). A tuple of (thing, value) will
                be exported to the dictionary, where thing is None if function
                is passed as-is, and bool if dill package is used to pickle the
                function, and value is the result.
            * 'id':
                the id of the targeted attribute is exported (e.g.
                id(target.name))
            * 'sig':
                The targeted attribute is a signal, and will be converted to a
                dictionary if fullcopy=True
        dic : dictionary
            A dictionary where the object will be exported
        fullcopy : bool
            Copies of objects are stored, not references. If any found,
            functions will be pickled and signals converted to dictionaries

    """
    whitelist_flags = {}
    for key, value in whitelist.items():
        if value is None:
            # No flags and/or values are given, just save the target
            thing = attrgetter(key)(target)
            if fullcopy:
                thing = deepcopy(thing)
            dic[key] = thing
            whitelist_flags[key] = ""
            continue

        flags_str, value = value
        flags = parse_flag_string(flags_str)
        check_that_flags_make_sense(flags)
        if key is "self":
            if "id" not in flags:
                raise ValueError('Key "self" is only available with flag "id" given')
            value = id(target)
        else:
            if "id" in flags:
                value = id(attrgetter(key)(target))

        # here value is either id(thing), or None (all others except 'init'),
        # or something for init
        if "init" not in flags and value is None:
            value = attrgetter(key)(target)
        # here value either id(thing), or an actual target to export
        if "sig" in flags:
            if fullcopy:
                from hyperspy.signal import Signal

                if isinstance(value, Signal):
                    value = value._to_dictionary()
                    value["data"] = deepcopy(value["data"])
        elif "fn" in flags:
            if fullcopy:
                if dill_avail:
                    value = (True, dill.dumps(value))
                else:
                    # Apparently this fails because Python does not guarantee backwards-compatibility for marshal, and pickle does
                    # not work for our lambda functions. Hence drop marshal
                    # support and only work with dill package
                    value = (False, marshal.dumps(value.__code__))
            else:
                value = (None, value)
        elif fullcopy:
            value = deepcopy(value)

        dic[key] = value
        whitelist_flags[key] = flags_str

    if "_whitelist" not in dic:
        dic["_whitelist"] = {}
    # the saved whitelist does not have any values, as they are saved in the
    # original dictionary. Have to restore then when loading from dictionary,
    # most notably all with 'init' flags!!
    dic["_whitelist"].update(whitelist_flags)


# Traceback (EOFError)
m = tmp.models.restore('BK_w_fine_structure')
---------------------------------------------------------------------------
EOFError                                  Traceback (most recent call last)
<ipython-input-349-668408e20346> in <module>()
----> 1 tmp.models.restore('BK_w_fine_structure')

/home/josh/git_repos/hyperspy/hyperspy/signal.py in restore(self, name)
253         name = self._check_name(name, True)
254         d = self._models.get_item(name + '._dict').as_dictionary()
--> 255         return self._signal.create_model(dictionary=copy.deepcopy(d))
256
257     def __repr__(self):

/home/josh/git_repos/hyperspy/hyperspy/_signals/eels.py in create_model(self, ll, auto_background, auto_add_edges, GOS, dictionary)
1254                           auto_add_edges=auto_add_edges,
1255                           GOS=GOS,
-> 1256                           dictionary=dictionary)
1257         return model
1258

/home/josh/git_repos/hyperspy/hyperspy/models/eelsmodel.py in __init__(self, spectrum, auto_background, auto_add_edges, ll, GOS, dictionary)
84             auto_background = False
85             auto_add_edges = False
---> 86             self._load_dictionary(dictionary)
87
88         if auto_background is True:

/home/josh/git_repos/hyperspy/hyperspy/model.py in _load_dictionary(self, dic)
255
256                 self.append(getattr(components, comp['_id_name'])(**init_args))
--> 257                 id_dict.update(self[-1]._load_dictionary(comp))
258             # deal with twins:
259             for comp in dic['components']:

/home/josh/git_repos/hyperspy/hyperspy/component.py in _load_dictionary(self, dic)
1079                 if hasattr(self, idname):
1080                     par = getattr(self, idname)
-> 1081                     t_id = par._load_dictionary(p)
1082                     id_dict[t_id] = par
1083                 else:

/home/josh/git_repos/hyperspy/hyperspy/component.py in _load_dictionary(self, dictionary)
183         """
184         if dictionary['_id_name'] == self._id_name:
--> 185             load_from_dictionary(self, dictionary)
186             return dictionary['self']
187         else:

/home/josh/git_repos/hyperspy/hyperspy/misc/export_dictionary.py in load_from_dictionary(target, dic)
182         flags = parse_flag_string(flags_str)
183         if 'id' not in flags:
--> 184             value = reconstruct_object(flags, value)
185             if 'init' in flags:
186                 new_whitelist[key] = (flags_str, value)

/home/josh/git_repos/hyperspy/hyperspy/misc/export_dictionary.py in reconstruct_object(flags, value)
215             return thing
216         if ifdill in [False, 'False', b'False']:
--> 217             return types.FunctionType(marshal.loads(thing), globals())
218         if ifdill in [True, 'True', b'True']:
219             if not dill_avail:

EOFError: marshal data too short

# Fixed code
def export_to_dictionary(target, whitelist, dic, fullcopy=True):
    """Exports attributes of target from whitelist.keys() to dictionary dic
    All values are references only by default.

    Parameters
    ----------
        target : object
            must contain the (nested) attributes of the whitelist.keys()
        whitelist : dictionary
            A dictionary, keys of which are used as attributes for exporting.
            Key 'self' is only available with tag 'id', when the id of the
            target is saved. The values are either None, or a tuple, where:
                - the first item a string, which containts flags, separated by
                commas.
                - the second item is None if no 'init' flag is given, otherwise
                the object required for the initialization.
            The flag conventions are as follows:
            * 'init':
                object used for initialization of the target. The object is
                saved in the tuple in whitelist
            * 'fn':
                the targeted attribute is a function, and may be pickled. A
                tuple of (thing, value) will be exported to the dictionary,
                where thing is None if function is passed as-is, and True if
                dill package is used to pickle the function, with the value as
                the result of the pickle.
            * 'id':
                the id of the targeted attribute is exported (e.g.
                id(target.name))
            * 'sig':
                The targeted attribute is a signal, and will be converted to a
                dictionary if fullcopy=True
        dic : dictionary
            A dictionary where the object will be exported
        fullcopy : bool
            Copies of objects are stored, not references. If any found,
            functions will be pickled and signals converted to dictionaries

    """
    whitelist_flags = {}
    for key, value in whitelist.items():
        if value is None:
            # No flags and/or values are given, just save the target
            thing = attrgetter(key)(target)
            if fullcopy:
                thing = deepcopy(thing)
            dic[key] = thing
            whitelist_flags[key] = ""
            continue

        flags_str, value = value
        flags = parse_flag_string(flags_str)
        check_that_flags_make_sense(flags)
        if key is "self":
            if "id" not in flags:
                raise ValueError('Key "self" is only available with flag "id" given')
            value = id(target)
        else:
            if "id" in flags:
                value = id(attrgetter(key)(target))

        # here value is either id(thing), or None (all others except 'init'),
        # or something for init
        if "init" not in flags and value is None:
            value = attrgetter(key)(target)
        # here value either id(thing), or an actual target to export
        if "sig" in flags:
            if fullcopy:
                from hyperspy.signal import Signal

                if isinstance(value, Signal):
                    value = value._to_dictionary()
                    value["data"] = deepcopy(value["data"])
        elif "fn" in flags:
            if fullcopy:
                value = (True, dill.dumps(value))
            else:
                value = (None, value)
        elif fullcopy:
            value = deepcopy(value)

        dic[key] = value
        whitelist_flags[key] = flags_str

    if "_whitelist" not in dic:
        dic["_whitelist"] = {}
    # the saved whitelist does not have any values, as they are saved in the
    # original dictionary. Have to restore then when loading from dictionary,
    # most notably all with 'init' flags!!
    dic["_whitelist"].update(whitelist_flags)