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
490
491
492
493
494
495
496
497
|
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
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
|
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
-
+
-
+
+
+
-
+
+
-
+
-
-
+
+
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
|
for name in core:
self.have[name] = plugin_meta(module=name, extra_base=["config"])
# aliases
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]
#dbg
#for name,meta in sorted(self.have.items()):
# print "HAVE ", name, " == ", meta.get("version")
# depends:
def depends(self, plugin):
r = True
if plugin.get("depends"):
dep_cmp = self.deps(plugin["depends"])
for alt_cmp in dep_cmp:
if not True in [self.cmp([d], self.have) for d in alt_cmp]:
r = False
return r
# basic list pre-filtering (skip __init__, filter by api:,
# basic plugin pre-screening (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 True
# Verify depends: and breaks: against existing plugins/modules
def depends(self, plugin):
r = True
if plugin.get("depends"):
r &= self.and_or(self.split(plugin["depends"]), self.have)
if plugin.get("breaks"):
r &= self.neither(self.split(plugin["breaks"]), self.have)
return r
# Split trivial "pkg | alt, mod >= 1, uikit < 4.0" string into nested list [[dep],[alt,alt],[dep]]
def deps(self, dep_str):
def split(self, dep_str):
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:
if not len(part):
continue
if part.find(":") >= 0:
self.have[part] = { "version": self.module_test(*part.split(":")) }
# find comparison and version num
part += " >= 0"
m = re.search(r"([\w.-]+)\s*([>=<!~]+)\s*([\d.]+([-~.]\w+)*)", part)
m = re.search(r"([\w.:-]+)\s*\(?\s*([>=<!~]+)\s*([\d.]+([-~.]\w+)*)", part)
if m and m.group(2):
alt_cmp.append([m.group(i) for i in (1, 2, 3)])
if alt_cmp:
dep_cmp.append(alt_cmp)
dep_cmp.append(alt_cmp)
return dep_cmp
# Do actual comparison
def cmp(self, d, have):
# Single comparison
def cmp(self, d, have, absent=True):
r = True
for name, op, ver in d:
name, op, ver = 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,
}
r &= tbl.get(op, True)
#print "log.VERSION_COMPARE: ", name, " → (", curr, op, ver, ") == ", r
# absent=True is the relaxed check, will ignore unknown plugins // set absent=False or None for strict check (as in breaks: rule e.g.)
if not have.get(name, {}).get("version"):
return absent
# curr = installed version
curr = have[name]["version"]
tbl = {
">=": curr >= ver,
"<=": curr <= ver,
"==": curr == ver,
">": curr > ver,
"<": curr < ver,
"!=": curr != ver,
}
r = tbl.get(op, True)
#print "log.VERSION_COMPARE: ", name, " → (", curr, op, ver, ") == ", r
return r
# Compare nested structure of [[dep],[alt,alt]]
def and_or(self, deps, have, r = True):
#print deps
return not False in [True in [self.cmp(d, have) for d in alternatives] for alternatives in deps]
# Breaks/Conflicts: check [[or],[or]]
def neither(self, deps, have):
return not True in [self.cmp(d, have, absent=None) for cnd in deps for d in cnd]
# Resolves/injects complex "bin:name" or "python:name" dependency URNs
def module_test(self, type, name):
return "1" # disabled for now
if "_" + type in dir(self):
return "1" if bool(getattr(self, "_" + type)(name)) else "-1"
# `bin:name` lookup
def _bin(self, name):
return find_executable(name)
# `python:module` test
def _python(self, name):
return __import__("imp").find_module(name) is not None
# 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
# dictionary `conf{}` and a plugin list in `conf.plugins{}`.
#
|