def get_dir_from_zipfile(zip_file, fallback=None):
    """Return the name of the root folder in a zip file.

    :param zip_file: a :class:`zipfile.ZipFile` instance.
    :param fallback: if `None`, the name of the zip file is used. This is
        returned if the zip file contains more than one top-level directory,
        or none at all.
    """
    fallback = fallback or zip_file.filename
    directories = [name for name in zip_file.namelist() if name.endswith("/")
                   and len(PurePath(name).parts) == 1]
    return fallback if len(directories) > 1 else directories[0]
def cell_edited_callback(self, text_cell, path, new_text):
        """If a backend name is changed, it saves the changes in the Backend

        @param text_cell: not used. The Gtk.CellRendererText that emitted the
                          signal. Only here because it's passed by the signal
        @param path: the Gtk.TreePath of the edited cell
        @param new_text: the new name of the backend
        """
        # we strip everything not permitted in backend names
        new_text = ''.join(c for c in new_text if (c.isalnum() or c in [" ", "-", "_"]))
        selected_iter = self.liststore.get_iter(path)
        # update the backend name
        backend_id = self.liststore.get_value(selected_iter,
                                              self.COLUMN_BACKEND_ID)
        backend = self.dialog.get_requester().get_backend(backend_id)
        if backend:
            backend.set_human_name(new_text)
            # update the text in the liststore
            self.liststore.set(selected_iter, self.COLUMN_TEXT, new_text)