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 _parse_filter():
        op = pyparsing.oneOf('! & |')
        lpar  = pyparsing.Literal('(').suppress()
        rpar  = pyparsing.Literal(')').suppress()

        k = pyparsing.Word(pyparsing.alphanums)
        # NOTE: We may need to expand on this list, but as this is not a real
        # LDAP server we should be OK.
        # Value to contain:
        #   numbers, upper/lower case letters, astrisk, at symbol, minus, full
        #   stop, backslash or a space
        v = pyparsing.Word(pyparsing.alphanums + "-*@.\\ äöü")
        rel = pyparsing.oneOf("= ~= >= <=")

        expr = pyparsing.Forward()
        atom = pyparsing.Group(lpar + op + expr + rpar) \
                            | pyparsing.Combine(lpar + k + rel + v + rpar)
        expr << atom + pyparsing.ZeroOrMore( expr )

        return expr