amprolla

devuan's apt repo merger
git clone https://git.parazyd.org/amprolla
Log | Files | Refs | README | LICENSE

parse.py (7712B)


      1 # See LICENSE file for copyright and license details.
      2 
      3 """
      4 Parsing functions/helpers
      5 """
      6 
      7 from time import mktime, strptime
      8 
      9 
     10 def get_time(date):
     11     """
     12     Gets epoch time
     13     """
     14     if not date:
     15         # hardcode if something's amiss
     16         date = 'Sun, 29 Oct 2017 10:00:00 UTC'
     17     return mktime(strptime(date, '%a, %d %b %Y %H:%M:%S %Z'))
     18 
     19 
     20 def get_date(relfile):
     21     """
     22     Gets the date from the contents of a Release file
     23     """
     24     date = None
     25     contents = relfile.split('\n')
     26     for line in contents:
     27         if line.startswith('Date: '):
     28             date = line.split(': ')[1]
     29             break
     30     return date
     31 
     32 
     33 def parse_release(reltext):
     34     """
     35     Parses a Release file and returns a dict of the files we need
     36     key = filename, value = tuple of sha256sum and file size
     37     """
     38     hashes = {}
     39 
     40     contents = reltext.split('\n')
     41 
     42     sha256 = False
     43     for line in contents:
     44         if sha256 is True and line != '':
     45             filename = line.split()[2]
     46             filesize = line.split()[1]
     47             checksum = line.split()[0]
     48             hashes[filename] = (checksum, filesize)
     49         elif line.startswith('SHA256:'):
     50             sha256 = True
     51 
     52     return hashes
     53 
     54 
     55 def parse_release_head(reltext):
     56     """
     57     Parses the header of the release file to grab needed metadata
     58     """
     59     metadata = {}
     60 
     61     contents = reltext.split('\n')
     62 
     63     splitter = 'MD5Sum:'
     64 
     65     md5sum = False
     66     for line in contents:
     67         if md5sum is True:
     68             break
     69         elif line.startswith(splitter):
     70             md5sum = True
     71         else:
     72             key = line.split(': ')[0]
     73             val = line.split(': ')[1]
     74             metadata[key] = val
     75 
     76     return metadata
     77 
     78 
     79 def parse_package(entry):
     80     """
     81     Parses a single Packages entry
     82     """
     83     pkgs = {}
     84 
     85     contents = entry.split('\n')
     86 
     87     key = ''
     88     value = ''
     89     for line in contents:
     90         if line.startswith(' '):
     91             value += '\n' + line
     92         else:
     93             pkgs[key] = value
     94 
     95             val = line.split(':', 1)
     96             key = val[0]
     97             value = val[1][1:]
     98 
     99     if key:
    100         pkgs[key] = value
    101 
    102     return pkgs
    103 
    104 
    105 def parse_packages(pkgtext):
    106     """
    107     Parses our package file contents into a hashmap
    108     key: package name, value: entire package paragraph as a hashmap
    109     """
    110     _map = {}
    111 
    112     pkgs = pkgtext.split('\n\n')
    113     for pkg in pkgs:
    114         single = pkg.split('\n')
    115         for line in single:
    116             if line.startswith('Package: '):
    117                 key = line.split(': ')[1]
    118                 _map[key] = parse_package(pkg)
    119                 break
    120 
    121     return _map
    122 
    123 
    124 def parse_dependencies(dependencies):
    125     """
    126     Parses a dependency line from a debian Packages file.
    127 
    128     Example line::
    129 
    130         'lib6 (>= 2.4), libdbus-1-3 (>= 1.0.2), foo | bar (>= 4.5.6)'
    131 
    132     Output::
    133 
    134         A list of dep alternatives, whose elements are dicts, in the form:
    135 
    136         [{'lib6': '(>= 2.4)'}, {'libdbus-1-3': '(>= 1.0.2)'},
    137          {'foo': None, 'bar': '(>= 4.5.6)'}]
    138     """
    139     ret = []
    140 
    141     for alternatives in dependencies.split(', '):
    142         depset = {}
    143         for pkg_plus_version in alternatives.split('|'):
    144             ver = pkg_plus_version.strip(' ').split(' ', 1)
    145             name = ver[0]
    146 
    147             # If we get passed an empty string, the name is '', and we just
    148             # outright stop.
    149             if not name:
    150                 return []
    151             if len(ver) == 2:
    152                 version = ver[1]
    153                 depset[name] = version
    154             else:
    155                 depset[name] = None
    156         ret.append(depset)
    157 
    158     return ret
    159 
    160 
    161 def compare_epochs(epo1, epo2):
    162     """
    163     Compares two given epochs and returns their difference.
    164     """
    165     return int(epo1) - int(epo2)
    166 
    167 
    168 def get_epoch(ver):
    169     """
    170     Parses and returns the epoch, and the rest, split of a version string.
    171     """
    172     if ':' in ver:
    173         return ver.split(':', 1)
    174     return "0", ver
    175 
    176 
    177 def get_upstream(rest):
    178     """
    179     Separate upstream_version from debian-version. The latter is whatever is
    180     found after the last "-" (hyphen)
    181     """
    182     split_s = rest.rsplit('-', 1)
    183     if len(split_s) < 2:
    184         return split_s[0], ""
    185     return split_s
    186 
    187 
    188 def get_non_digit(s):
    189     """
    190     Get a string and return the longest leading substring consisting
    191     exclusively of non-digits (or an empty string), and the remaining
    192     substring.
    193     """
    194     if not s:
    195         return "", ""
    196     head = ""
    197     tail = s
    198     N = len(s)
    199     i = 0
    200     while i < N and not s[i].isdigit():
    201         head += s[i]
    202         tail = tail[1:]
    203         i += 1
    204     return head, tail
    205 
    206 
    207 def get_digit(s):
    208     """
    209     Get a string and return the integer value of the longest leading substring
    210     consisting exclusively of digit characters (or zero otherwise), and the
    211     remaining substring.
    212     """
    213     if not s:
    214         return 0, ""
    215     head = ""
    216     tail = s
    217     N = len(s)
    218     i = 0
    219     while i < N and s[i].isdigit():
    220         head += s[i]
    221         tail = tail[1:]
    222         i += 1
    223     return int(head), tail
    224 
    225 
    226 def char_val(c):
    227     """
    228     Returns an integer value of a given unicode character. Returns 0 on ~
    229     (since this is in Debian's policy)
    230     """
    231     if c == '~':
    232         return 0
    233     elif not c.isalpha():
    234         return 256 + ord(c)
    235     return ord(c)
    236 
    237 
    238 def compare_deb_str(a1, a2):
    239     while len(a1) > 0 and len(a2) > 0:
    240         char_diff = char_val(a1[0]) - char_val(a2[0])
    241         if char_diff != 0:
    242             return char_diff
    243         a1 = a1[1:]
    244         a2 = a2[1:]
    245     if len(a1) == 0:
    246         if len(a2) == 0:
    247             return 0
    248         else:
    249             if a2[0] == '~':
    250                 return 512
    251             else:
    252                 return -ord(a2[0])
    253     else:
    254         if a1[0] == '~':
    255             return -512
    256         else:
    257             return ord(a1[0])
    258 
    259 
    260 def compare_non_epoch(s1, s2):
    261     cont = True
    262     while cont:
    263         alpha1, tail1 = get_non_digit(s1)
    264         alpha2, tail2 = get_non_digit(s2)
    265         if alpha1 == alpha2:
    266             if not tail1 and not tail2:
    267                 diff = 0
    268                 break
    269             num1, s1 = get_digit(tail1)
    270             num2, s2 = get_digit(tail2)
    271             if num1 == num2:
    272                 cont = True
    273             else:
    274                 diff = num1 - num2
    275                 cont = False
    276         else:
    277             cont = False
    278             diff = compare_deb_str(alpha1, alpha2)
    279 
    280     return diff
    281 
    282 
    283 def cmppkgver(ver1, ver2):
    284     """
    285     Main function to compare two package versions. Wraps around other
    286     functions to provide a result. It returns an integer < 0 if ver1 is
    287     earlier than ver2, 0 if ver1 is the same as ver2, and an integer > 0
    288     if ver2 is earlier than ver2.
    289 
    290     WARNING: The function does not induce a total order (i.e., return values
    291     MUST NOT be added or subtracted)
    292 
    293     https://www.debian.org/doc/debian-policy/#version
    294     """
    295     epoch1, rest1 = get_epoch(ver1)
    296     epoch2, rest2 = get_epoch(ver2)
    297 
    298     ec = compare_epochs(epoch1, epoch2)
    299     if ec != 0:
    300         # The two versions differ on epoch
    301         return ec
    302 
    303     upst1, rev1 = get_upstream(rest1)
    304     upst2, rev2 = get_upstream(rest2)
    305 
    306     up_diff = compare_non_epoch(upst1, upst2)
    307     if up_diff == 0:
    308         return compare_non_epoch(rev1, rev2)
    309     return up_diff
    310 
    311 
    312 def compare_dict(dic1, dic2):
    313     """
    314     Compares two dicts
    315     Takes two dicts and returns a dict of tuples with the differences.
    316 
    317     Example input:
    318 
    319         dic1={'foo': 'bar'}, dic2={'foo': 'baz'}
    320 
    321     Example output:
    322 
    323         {'foo': ('bar', 'baz')}
    324     """
    325     d1_keys = set(dic1.keys())
    326     d2_keys = set(dic2.keys())
    327     intersect_keys = d1_keys.intersection(d2_keys)
    328     mod = {o: (dic1[o], dic2[o]) for o in intersect_keys if dic1[o] != dic2[o]}
    329     return mod