def _parse_version(vs):
    """
    Parse a version string, e.g. '2!3.0.1.rc2.dev3'
    :param vs: version to parse
    :type vs: str
    :return: a dict containing the fragments about the version
    :rtype: dict
    """
    # NOTE: the below code may be a bit messy, because it was rewritten multiple times and then repurposed from a sort-function to a parsing-function
    # version scheme (PEP440): [N!]N(.N)*[{a|b|rc}N][.postN][.devN]
    # convert to lowercase, since all versions must be case insenstive (PEP440)
    e = vs.lower()
    # extract information from
    if "!" in e:
        # read epoch
        es = e[:e.find("!")]
        e = e[e.find("!") + 1:]
        epoch = int(es)
    else:
        # default epoch is 0
        epoch = 0
    # parse version tuple
    veis = re.search("[^0-9\\.]", e)
    if veis is None:
        # no non-digits
        vei = len(e)
    else:
        vei = veis.start()
    vstr = e[:vei]
    while vstr.endswith("."):
        # remove trailing '.'
        vstr = vstr[:-1]
    splitted = vstr.split(".")
    verparts = []
    for v in splitted:
        verparts.append(int(v))
    # parse post release
    rtstr = e[vei:]
    if "post" in rtstr:
        postrnstr = rtstr[rtstr.find("post") + 4:]
        postrnres = re.search("[0-9]*", postrnstr)
        if postrnres is None or postrnres.end() == 0:
            # PEP440: implicit post version 0
            subpriority = 0
        else:
            subpriority = int(postrnstr[:postrnres.end()])
        rtstr = rtstr[:rtstr.find("post") - 1]
        is_post = True
    else:
        subpriority = 0
        is_post = False
    # parse release type
    rtype = None
    if len(rtstr) == 0:
        rtype = None
    elif rtstr[0] in ("a", "b"):
        rtype = rtstr[0]
    elif rtstr.startswith("rc"):
        rtype = "rc"
    # parse number of release
    if rtype is None:
        # not needed
        rsubpriority = 0
    else:
        # 1. strip rtype
        rtps = rtstr[len(rtype):]
        # 2. extract until non-digit
        rtpsr = re.search("[0-9]*", rtps)
        if rtpsr is None or rtpsr.end() == 0:
            # no number
            rsubpriority = 0
        else:
            rsubpriority = int(rtps[:rtpsr.end()])
    # extract dev release information
    devr = re.search("\\.?dev[0-9]*", e)
    if devr is None:
        isdev = False
        devnum = 0
    else:
        isdev = True
        devns = e[devr.start() + 4:]  # 4: 1 for '.'; 3 for 'dev'
        if len(devns) == 0:
            devnum = 0
        else:
            devnum = int(devns)
    return {
        "epoch": epoch,
        "versiontuple": tuple(verparts),
        "rtype": rtype,
        "subversion": rsubpriority,
        "postrelease": (subpriority if is_post else None),
        "devrelease": (devnum if isdev else None),
    }

    Parse a version string, e.g. '2!3.0.1.rc2.dev3'
    :param vs: version to parse
    :type vs: str
    :return: a dict containing the fragments about the version
    :rtype: dict
    
lib/libversion.py