import cffi _CDEF = """ void parseEVR(const char* evr, const char **e, const char **v, const char **r); int rpmEVRcmp(const char * const aE, const char * const aV, const char * const aR, const char * const aDepend, const char * const bE, const char * const bV, const char * const bR, const char * const bDepend); int rpmRangesOverlap(const char * AName, const char * AEVR, int AFlags, const char * BName, const char * BEVR, int BFlags); """ _FFI = cffi.FFI() _FFI.cdef(_CDEF) _LIBRPM = _FFI.dlopen('librpm-4.0.4.so') _EMPTY = _FFI.new('char[]', b'') def _pp_str(p_str): """Convert FFI's char** to Python string. Assumes that pointer points to a zero-terminated C-string. Returns None if the pointer is NULL. """ if p_str and p_str[0]: return _FFI.string(p_str[0]) return None def parse_evr(evr): """Returns 3-tuple (epoch, version, release)""" p_evr = _FFI.new('char[]', evr) e = _FFI.new('char**') v = _FFI.new('char**') r = _FFI.new('char**') _LIBRPM.parseEVR(p_evr, e, v, r) epoch = _pp_str(e) epoch = int(epoch) if epoch else None return epoch, _pp_str(v), _pp_str(r) def ranges_overlap(aname, aevr, aflags, bname, bevr, bflags): return _LIBRPM.rpmRangesOverlap( _FFI.new('char[]', aname), _FFI.new('char[]', aevr), aflags, _FFI.new('char[]', bname), _FFI.new('char[]', bevr), bflags) def _to_pchar(part): if not part: return _FFI.NULL if not isinstance(part, bytes): part = part.encode() return _FFI.new('char[]', part) if part else _FFI.NULL def evr_cmp(evr1, evr2): # Each EVR is 3-tuple 'epoch, version, release'. # If epoch is None we replace it with '0'. return _LIBRPM.rpmEVRcmp( _to_pchar(str(evr1[0]).encode() if evr1[0] else b'0'), _to_pchar(evr1[1]), _to_pchar(evr1[2]), _EMPTY, _to_pchar(str(evr2[0]).encode() if evr2[0] else b'0'), _to_pchar(evr2[1]), _to_pchar(evr2[2]), _EMPTY) def ver_cmp(ver1, ver2): return evr_cmp((None, ver1, b'1'), (None, ver2, b'1')) def _tests(): assert parse_evr(b'3:42.8.4-alt1.mips0') == (3, b'42.8.4', b'alt1.mips0') assert parse_evr(b'0:42.8.4-alt1.mips0') == (0, b'42.8.4', b'alt1.mips0') assert parse_evr(b'42.8.4-alt1.mips0') == (None, b'42.8.4', b'alt1.mips0') assert parse_evr(b'1.20.0-alt1_1') == (None, b'1.20.0', b'alt1_1') assert parse_evr(b'1:1.20.1-alt1') == (1, b'1.20.1', b'alt1') assert evr_cmp((3, b'42.8.4', b'alt1'), (3, b'42.8.4', b'alt2')) < 0 assert evr_cmp((3, b'42.9.4', b'alt1'), (3, b'42.8.4', b'alt2')) > 0 assert evr_cmp((3, b'42.8.4', b'alt1'), (3, b'42.8.4', b'alt1')) == 0 assert evr_cmp((5, b'42.8.4', b'alt1'), (3, b'42.8.4', b'alt1')) > 0 assert evr_cmp((3, b'42.9.4', b'alt1'), (5, b'42.8.4', b'alt1')) < 0 # unicode test assert evr_cmp((3, u'42.8.4', u'alt1'), (3, b'42.8.4', b'alt1')) == 0 assert evr_cmp((1, b'1.2.0', b'alt1'), (None, b'1.2.0', b'alt1_1')) > 0 assert evr_cmp((None, b'1.2.0', b'alt1_1'), (1, b'1.2.0', b'alt1')) < 0 if __name__ == '__main__': _tests()