Overview
| Comment: | pylint fixes for pluginconf.depends |
|---|---|
| Downloads: | Tarball | ZIP archive | SQL archive |
| Timelines: | family | ancestors | descendants | both | trunk |
| Files: | files | file ages | folders |
| SHA3-256: |
3be9f23c5701f8129ae0b491aa3bcd3e |
| User & Date: | mario on 2022-10-27 07:44:40 |
| Other Links: | manifest | tags |
Context
|
2022-10-27
| ||
| 07:47 | update Check check-in: 2568a72ca8 user: mario tags: trunk | |
| 07:44 | pylint fixes for pluginconf.depends check-in: 3be9f23c57 user: mario tags: trunk | |
| 05:34 | lots pylint fixes, add documentation check-in: 9c3d295916 user: mario tags: trunk | |
Changes
Modified README.md from [d0883b96e6] to [05362db7a2].
1 2 | Provides meta data extraction and plugin basename lookup. And it’s meant for in-application feature and option management. | | | 1 2 3 4 5 6 7 8 9 10 |
Provides meta data extraction and plugin basename lookup. And it’s meant for
in-application feature and option management.
The [descriptor format](https://fossil.include-once.org/pluginspec/)
(*self-contained* atop each script) is basically:
# encoding: utf-8
# api: python
# type: handler
# category: io
# title: Plugin configuration
|
| ︙ | ︙ | |||
54 55 56 57 58 59 60 |
module_base = "pluginconf" # or any top-level app module
Which declares module and plugin basenames, which get used for lookups by
just module= names in e.g. `module_list()`. (Works for literal setups
and within PYZ bundles).
This is unnecessary for plain `plugin_meta(fn=)` extraction.
| | | 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
module_base = "pluginconf" # or any top-level app module
Which declares module and plugin basenames, which get used for lookups by
just module= names in e.g. `module_list()`. (Works for literal setups
and within PYZ bundles).
This is unnecessary for plain `plugin_meta(fn=)` extraction.
#### [plugin_meta](https://fossil.include-once.org/pluginspec/doc/trunk/html/index.html)( module= | filename= | src= | frame= )
Returns a meta data dictionary for the given module name, file, source code, or caller frame:
{
"title": "Compound★",
"description": "...",
"version": "0.1",
|
| ︙ | ︙ | |||
180 181 182 183 184 185 186 |
requires = ["flit_core", "pluginconf"]
build-backend = "pluginconf.flit"
[project]
name = "projectname"
It can be invoked via `python -m pluginconf.flit build` or even
| | | | 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 |
requires = ["flit_core", "pluginconf"]
build-backend = "pluginconf.flit"
[project]
name = "projectname"
It can be invoked via `python -m pluginconf.flit build` or even
`flit-pluginconf build`. Field mapping isn't very robust yet.
## other modules
* `pluginconf.depends` provides `Check` for .valid() and .depends() probing
* argparse_map() might also end up in a separate module.
#### Caveats
* It’s mostly just an excerpt from streamtuner2.
* Might need customization prior use.
|
| ︙ | ︙ |
Modified pluginconf/depends.py from [6e210a4c05] to [f49268879d].
| ︙ | ︙ | |||
10 11 12 13 14 15 16 | # license: PD # priority: optional # # This is a rather basic depends: checker, mostly for local and # installable modules. It's largely built around streamtuner2 # requirements, and should be customized. # | | | > > | < < < | | 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
# license: PD
# priority: optional
#
# This is a rather basic depends: checker, mostly for local and
# installable modules. It's largely built around streamtuner2
# requirements, and should be customized.
#
# Check().depends()/.valid()
# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
# Probes a new plugins` depends: list against installed base modules.
# Utilizes each version: fields and allows for virtual modules, or
# alternatives and honors alias: names.
#
""" Dependency validation and consistency checker for updates """
import sys
import re
#import zipfile
import logging
import pluginconf
try:
from distutils.spawn import find_executable
except ImportError:
try:
from compat2and3 import find_executable
except ImportError:
find_executable = lambda name: False
# Minimal depends: probing
# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
class Check(object):
"""
Now this definitely requires customization. Each plugin can carry
a list of (soft-) dependency names.
\# depends: config, appcore >= 2.0, bin:wkhtmltoimage, python < 3.5
Here only in-application modules are honored, system references
|
| ︙ | ︙ | |||
65 66 67 68 69 70 71 |
api : list
allowed api: identifiers for .valid() stream checks
log : logging
warning handler
have : dict
accumulated list of existing/virtual plugins
"""
| | | | | > > | > > | 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 |
api : list
allowed api: identifiers for .valid() stream checks
log : logging
warning handler
have : dict
accumulated list of existing/virtual plugins
"""
# supported APIs
api = ["python", "streamtuner2"]
# debugging
log = logging.getLogger("pluginconf.dependency")
# ignore bin:… or python:… package in depends
system_deps = False
def __init__(self, add={}, core=["st2", "uikit", "config", "action"]):
"""
Prepare list of known plugins and versions in self.have={}
Parameters
----------
add : dict
name to pmd list of existing/core/virtual plugins (can define
versions or own dependencies)
core : list
name list of virtual plugins
|
| ︙ | ︙ | |||
110 111 112 113 114 115 116 |
# aliases
for name, meta in self.have.copy().items():
if meta.get("alias"):
for alias in re.split(r"\s*[,;]\s*", meta["alias"]):
self.have[alias] = self.have[name]
| < < > > | > > | | | > < | < > > > > > | | | | > | < | | | | < | > | < > < | > > | | | > > | | > | | > < | 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 |
# aliases
for name, meta in self.have.copy().items():
if meta.get("alias"):
for alias in re.split(r"\s*[,;]\s*", meta["alias"]):
self.have[alias] = self.have[name]
def valid(self, new_plugin):
"""
Plugin pre-screening from online repository stream.
Fields are $name, $file, $dist, api, id, depends, etc
Exclude installed or for newer-version presence.
"""
if not "$name" in new_plugin:
self.log.warning(".valid() checks online plugin lists, requires $name")
name = new_plugin.get("$name", "__invalid")
have_ver = self.have.get(name, {}).get("version", "0")
if name.find("__") == 0:
self.log.debug("wrong/no id")
elif new_plugin.get("api") not in self.api:
self.log.debug("not in allowed APIs")
elif {new_plugin.get("status"), new_plugin.get("priority")} & {"obsolete", "broken"}:
self.log.debug("wrong status (obsolete/broken)")
elif have_ver >= new_plugin.get("version", "0.0"):
self.log.debug("newer version already installed")
else:
return True
return False
def depends(self, plugin):
""" Verify depends: and breaks: against existing plugins/modules """
result = True
if plugin.get("depends"):
result &= self.and_or(self.split(plugin["depends"]), self.have)
if plugin.get("breaks"):
result &= self.neither(self.split(plugin["breaks"]), self.have)
self.log.debug("plugin '%s' matching requirements: %i", plugin["id"], result)
return result
def split(self, dep_str):
"""
Split trivial "pkg | alt, mod>=1, uikit<4.0" string
into nested list [ [alt, alt], [dep], [dep] ];
with each entry comprised of (name, operator, version).
"""
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 part:
continue
if part.find(":") >= 0:
self.have[part] = {"version": self.module_test(*part.split(":"))}
# find comparison and version num
part += " >= 0"
match = re.search(r"([\w.:-]+)\s*\(?\s*([>=<!~]+)\s*([\d.]+([-~.]\w+)*)", part)
if match and match.group(2):
alt_cmp.append([match.group(i) for i in (1, 2, 3)])
if alt_cmp:
dep_cmp.append(alt_cmp)
return dep_cmp
def cmp(self, name_op_ver, have, absent=True):
""" Single comparison """
name, operator, ver = name_op_ver
# 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,
}
result = tbl.get(operator, True)
self.log.debug("VERSION_COMPARE: %s → (%s %s %s) == %s", name, curr, operator, ver, result)
return result
def and_or(self, deps, have, inner_true=True):
""" Compare nested structure of [[dep],[alt,alt]] """
#print deps
return not False in [
inner_true in [self.cmp(d, have) for d in alternatives] for alternatives in deps
]
def neither(self, deps, have):
""" Breaks/Conflicts: check [[or],[or]] """
return not True in [
self.cmp(d, have, absent=None) for cnd in deps for d in cnd
]
def module_test(self, urn, name):
""" Probes "bin:name" or "python:name" dependency URNs """
if not self.system_deps:
return "1"
if "_" + urn in dir(self):
if bool(getattr(self, "_" + urn)(name)):
return "1"
return "-1" # basically a negative version -v1
@staticmethod
def _bin(name):
""" `bin:name` lookup """
return find_executable(name)
@staticmethod
def _python(name):
""" `python:module` test """
return __import__("imp").find_module(name) is not None
|
Modified test/depends.py from [10d2104189] to [3267defa9d].
| ︙ | ︙ | |||
13 14 15 16 17 18 19 | import pluginconf.depends import logging logging.basicConfig(level=logging.DEBUG) @pytest.fixture def check(): | | | 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
import pluginconf.depends
import logging
logging.basicConfig(level=logging.DEBUG)
@pytest.fixture
def check():
deps = pluginconf.depends.Check(
add={"core": 2.555, "config": 2.0, "existing": 0.1},
core=["IMPLICIT"],
)
deps.api = ["python", "foobar"]
return deps
@pytest.fixture
|
| ︙ | ︙ |