| 
395
396
397
398
399
400
401
402
403
404
405
406
407408
409
410
411
412
413414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434435
436437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464465
466
467
468
469
470
471
472
473 | 
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
 | 
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+
+
+
-
-
+
+
-
+
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
 | 
#
# In practice there's little need for full-blown dependency resolving
# for application-level modules.
#
class dependency(object):
    # prepare list of known plugins and versions
    def __init__(self, core=def __init__(self, add={}, core=["st2", "uikit", "config", "action"]):
        self.have = {
            "python": { "version": sys.version }
        }
        # inject virtual modules
        for name, meta in add.items():
            if isinstance(meta, bool): meta = 1 if meta else -1
            if isinstance(meta, tuple): meta = ".".join(str(n) for n in meta)
            if isinstance(meta, (int, float, str)): meta = {"version": str(meta)}
            self.have[name] = meta
        # read plugins/*
        self.have.update(all_plugin_meta())
        # add core modules
        for name in core:
            self.have[name] = plugin_meta(module=name, extra_base=["config"])
        # aliases{}):
        self.have = {}.update(core)self.have = all_plugin_meta()
        #dependencies oncore modules are somewhat more interesting:for name in("st2", "uikit", "config", "action"):        for name, meta in self.have.items():
            if meta.get("alias"):
                for alias in re.split("\s*[,;]\s*", meta["alias"]):
                    self.have[alias] = self.have[name]
    # depends:
    def depends(self, plugin):
        if plugin.get("depends"):self.have["streamtuner2"] =self.have["st2"]have = {}            d = self.deps(plugin["depends"])
            if not self.cmp(dep_cmp = self.deps(plugin["depends"])
            if not (True in [self.cmp(alt_cmp, self.have) for alt_cmp in dep_cmp]):
                return False
        return True
    # basic list pre-filtering (skip __init__, filter by api:,
    # exclude installed & same-version plugins)
    def valid(self, newpl):
        id = newpl.get("$name", "__invalid")
        have_ver = self.have.get(id, {}).get("version", "0")
        if id.find("__") == 0:
            pass
        elif newpl.get("api") not in ("python", "streamtuner2"):
            pass
        elif set((newpl.get("status"), newpl.get("priority"))).intersection(set(("obsolete", "broken"))):
            pass
        elif have_ver >= newpl.get("version", "0.0"):
            pass
        else:
            return Trued, self.have):    # Split trivial "pkg, mod >= 1, uikit < 4.0" # Split trivial "pkg | alt, mod >= 1, uikit < 4.0" string into nested list [[dep],[alt,alt],[dep]]
    def deps(self, dep_str):list        d = []
        for dep_cmp = []
        for alt_str in re.split(r"\s*[,;]+\s*", dep_str):
            alt_cmp = []
            # split alternatives |
            for part in re.split(r"\s*\|+\s*", alt_str):
                # skip deb:pkg-name, rpm:name, bin:name etc.
                if not len(part) or part.find(":") >= 0:
                    continue
                # find comparison and version num
                part += " >= 0"
                m = re.search(r"([\w.-]+)\s*([>=<!~]+)\s*([\d.]+([-~.]\w+)*)", part)
                if m and m.group(2):
                    alt_cmp.append([m.group(i) for i in (1, 2, 3)])
            dep_cmp.append(alt_cmp)
        return dep_cmp
    # Do actual comparison
    def cmp(self, d, have):
        r = True
        for name, op, ver in d:
            # skip unknown plugins, might be python module references
            if not have.get(name, {}).get("version"):
                continue
            curr = have[name]["version"]
            tbl = {
                ">=": curr >= ver,
                "<=": curr <= ver,
                "==": curr == ver,
                ">":  curr > ver,
                "<":  curr < ver,
                "!=": curr != ver,
            }depin re.split(r"\s*[,;]+\s*", dep_str):
            # skip deb:pkg-name, rpm:name, bin:name etc.
            if not len(dep) ordep.find(":") >= 0:
                continue
            # find comparison and version numdep+= " >= 0"
            m = re.search(r"([\w.-]+)\s*([>=<!~]+)\s*([\d.]+([-~.]\w+)*)",dep)
            if m and m.group(2):d.append([m.group(i) for i in (1, 2, 3)])
        return dr &= tbl.get(op, True)
            #print "log.VERSION_COMPARE: ", name, " → (", curr, op, ver, ") == ", r
        return r
# Add plugin defaults to conf.* store
# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
# Utility function which applies plugin meta data to a config
# store. Which in the case of streamtuner2 is really just a            #log.VERSION_COMPARE(name, " → (", curr, op, ver, ") == ", r) |