diff --git a/geninfo/genisolist.py b/geninfo/genisolist.py index 6df8414..45f4d23 100755 --- a/geninfo/genisolist.py +++ b/geninfo/genisolist.py @@ -16,6 +16,7 @@ from urllib.parse import urljoin from configparser import ConfigParser from argparse import ArgumentParser, ArgumentError + class Version: def __init__(self, vstring=None): if vstring: @@ -56,11 +57,11 @@ class Version: class LooseVersion(Version): - component_re = re.compile(r'(\d+ | [a-z]+ | \.)', re.VERBOSE) + component_re = re.compile(r"(\d+ | [a-z]+ | \.)", re.VERBOSE) def parse(self, vstring): self.vstring = vstring - components = [x for x in self.component_re.split(vstring) if x and x != '.'] + components = [x for x in self.component_re.split(vstring) if x and x != "."] for i, obj in enumerate(components): try: components[i] = int(obj) @@ -102,42 +103,45 @@ class LooseVersion(Version): if self_version > other_version: return 1 + parser = ArgumentParser( - prog='iso info list generator', - description='Generate iso info list', + prog="iso info list generator", + description="Generate iso info list", +) +parser.add_argument("-d", "--dir", default=None, help="Override root directory.") +parser.add_argument( + "-R", + "--remote", + default=None, + help="[Remote Mode] Using rsync to get file list instead of reading from INI. Need the base of target site, for example, `mirror.tuna.tsinghua.edu.cn`.", ) parser.add_argument( - '-d', '--dir', default=None, - help='Override root directory.' -) -parser.add_argument( - '-R', '--remote', default=None, - help='[Remote Mode] Using rsync to get file list instead of reading from INI. Need the base of target site, for example, `mirror.nju.edu.cn`.' -) -parser.add_argument( - '-T', '--test', default=None, nargs="*", - help='Test specified `distro`s (multiple arguments input is supported) in INI. If Remote Mode is on, `distro`s must be specified in case of heavy rsync job.' + "-T", + "--test", + default=None, + nargs="*", + help="Test specified `distro`s (multiple arguments input is supported) in INI. If Remote Mode is on, `distro`s must be specified in case of heavy rsync job.", ) args = parser.parse_args() logger = logging.getLogger(__name__) -CONFIG_FILE = os.path.join(os.path.dirname(__file__), 'genisolist.ini') +CONFIG_FILE = os.path.join(os.path.dirname(__file__), "genisolist.ini") def getPlatformPriority(platform): platform = platform.lower() - if platform in ['amd64', 'x86_64', '64bit']: + if platform in ["amd64", "x86_64", "64bit"]: return 100 - elif platform in ['arm64', 'aarch64', 'arm64v8']: + elif platform in ["arm64", "aarch64", "arm64v8"]: return 95 - elif platform in ['riscv64']: + elif platform in ["riscv64"]: return 95 - elif platform in ['loongson2f', 'loongson3']: + elif platform in ["loongson2f", "loongson3"]: return 95 - elif platform in ['i386', 'i486', 'i586', 'i686', 'x86', '32bit']: + elif platform in ["i386", "i486", "i586", "i686", "x86", "32bit"]: return 90 - elif platform in ['arm32', 'armhf', 'armv7']: + elif platform in ["arm32", "armhf", "armv7"]: return 85 else: return 0 @@ -152,10 +156,10 @@ def renderTemplate(template, result): def getSortKeys(template, result): keys = [] - for i in template.split(' '): + for i in template.split(" "): if not i: continue - if i[0] != '$': + if i[0] != "$": keys.append(i) else: keys.append(result.group(int(i[1:])) or "") @@ -165,8 +169,8 @@ def getSortKeys(template, result): def parseSection(items, rsync=False): items = dict(items) - if 'location' in items: - locations = [items['location']] + if "location" in items: + locations = [items["location"]] else: locations = [] i = 0 @@ -179,7 +183,6 @@ def parseSection(items, rsync=False): images = {} for location in locations: - if rsync: # find last non-wildcard path non_regex = [] @@ -197,7 +200,8 @@ def parseSection(items, rsync=False): # use fnmatch to mimic glob behavior image_paths = list( - filter(lambda path: fnmatch.fnmatch(path, location), image_paths)) + filter(lambda path: fnmatch.fnmatch(path, location), image_paths) + ) else: logger.debug("[GLOB] %s", location) @@ -218,14 +222,17 @@ def parseSection(items, rsync=False): for prop in ("version", "type", "platform", "category"): imageinfo[prop] = renderTemplate(items.get(prop, ""), result) - if 'version' not in imageinfo: - imageinfo['version'] = '0.0' + if "version" not in imageinfo: + imageinfo["version"] = "0.0" sort_by = items.get("sort_by", "") if not (sort_by): - imageinfo['sort_key'] = ( - imageinfo['version'], imageinfo['platform'], imageinfo['type']) + imageinfo["sort_key"] = ( + imageinfo["version"], + imageinfo["platform"], + imageinfo["type"], + ) else: - imageinfo['sort_key'] = getSortKeys(sort_by, result) + imageinfo["sort_key"] = getSortKeys(sort_by, result) logger.debug("[JSON] %r", imageinfo) key = renderTemplate(items.get("key_by", ""), result) @@ -234,17 +241,21 @@ def parseSection(items, rsync=False): images[key].append(imageinfo) for image_group in images.values(): - if 'nosort' not in items: - image_group.sort(key=lambda k: (LooseVersion(k['version']), - getPlatformPriority(k['platform']), - k['type']), - reverse=True) + if "nosort" not in items: + image_group.sort( + key=lambda k: ( + LooseVersion(k["version"]), + getPlatformPriority(k["platform"]), + k["type"], + ), + reverse=True, + ) i = 0 versions = set() - listvers = int(items.get('listvers', 0xFF)) + listvers = int(items.get("listvers", 0xFF)) for image in image_group: - versions.add(image['version']) + versions.add(image["version"]) if len(versions) <= listvers: yield image else: @@ -252,27 +263,34 @@ def parseSection(items, rsync=False): def getDetail(image_info, urlbase): - url = urljoin(urlbase, image_info['filepath']) - desc = "%s (%s%s)" % ( - image_info['version'], - image_info['platform'], - ", %s" % image_info['type'] if image_info['type'] else '' - ) if image_info['platform'] != "" else image_info['version'] + url = urljoin(urlbase, image_info["filepath"]) + desc = ( + "%s (%s%s)" + % ( + image_info["version"], + image_info["platform"], + ", %s" % image_info["type"] if image_info["type"] else "", + ) + if image_info["platform"] != "" + else image_info["version"] + ) - category = image_info.get('category', 'os') or "os" + category = image_info.get("category", "os") or "os" return (desc, url, category) def getJsonOutput(url_dict, prio={}): raw = [] for distro in url_dict: - raw.append({ - "distro": distro, - "category": list({c for _, _, c in url_dict[distro]})[0], - "urls": [ - {"name": name, "url": url} for name, url, _ in url_dict[distro] - ] - }) + raw.append( + { + "distro": distro, + "category": list({c for _, _, c in url_dict[distro]})[0], + "urls": [ + {"name": name, "url": url} for name, url, _ in url_dict[distro] + ], + } + ) raw.sort(key=lambda d: prio.get(d["distro"], 0xFFFF)) @@ -285,13 +303,13 @@ def getImageList(): raise Exception("%s not found!" % CONFIG_FILE) prior = {} - for (name, value) in ini.items("%main%"): + for name, value in ini.items("%main%"): if re.match(r"d\d+$", name): prior[value] = int(name[1:]) url_dict = {} - root = ini.get("%main%", 'root') - urlbase = ini.get("%main%", 'urlbase') + root = ini.get("%main%", "root") + urlbase = ini.get("%main%", "urlbase") if not args.remote: # Local @@ -308,10 +326,10 @@ def getImageList(): if section == "%main%": continue for image in parseSection(ini.items(section)): - img_dict[image['distro']].append(image) + img_dict[image["distro"]].append(image) for distro, images in img_dict.items(): - images.sort(key=lambda x: x['sort_key'], reverse=True) + images.sort(key=lambda x: x["sort_key"], reverse=True) logger.debug("[IMAGES] %r %r", distro, images) url_dict[distro] = [getDetail(image, urlbase) for image in images] @@ -320,38 +338,41 @@ def getImageList(): else: # Remote for spec_distro in args.test: - img_dict = collections.defaultdict(list) for section in ini.sections(): if section == "%main%": continue - if ini.get(section, 'distro') != spec_distro: + if ini.get(section, "distro") != spec_distro: continue else: for image in parseSection(ini.items(section), rsync=True): - img_dict[image['distro']].append(image) + img_dict[image["distro"]].append(image) for distro, images in img_dict.items(): - images.sort(key=lambda x: x['sort_key'], reverse=True) + images.sort(key=lambda x: x["sort_key"], reverse=True) logger.debug("[IMAGES] %r %r", distro, images) - url_dict[distro] = [getDetail(image, urlbase) - for image in images] + url_dict[distro] = [getDetail(image, urlbase) for image in images] return getJsonOutput(url_dict, prior) def rsyncQuery(url: str): rsync_proc = os.popen( - "rsync -r --list-only --no-motd rsync://%s | awk '{ $1=$2=$3=$4=\"\"; print substr($0,5); }'" % url) + "rsync -r --list-only --no-motd rsync://%s | awk '{ $1=$2=$3=$4=\"\"; print substr($0,5); }'" + % url + ) return [path.rstrip() for path in rsync_proc.readlines()] if __name__ == "__main__": import sys + logging.basicConfig(stream=sys.stderr, level=logging.WARNING) if args.remote: if not args.test: raise ArgumentError( - None, 'If Remote Mode is on, images must be specified in case of heavy rsync job.') + None, + "If Remote Mode is on, images must be specified in case of heavy rsync job.", + ) print(getImageList())