Overview
Comment: | doc additions, add params for setup and gui, fix flit sample |
---|---|
Downloads: | Tarball | ZIP archive | SQL archive |
Timelines: | family | ancestors | descendants | both | trunk |
Files: | files | file ages | folders |
SHA3-256: |
93aac201515248d5ceb9bdf723aff53c |
User & Date: | mario on 2022-10-31 12:56:46 |
Other Links: | manifest | tags |
Context
2022-10-31
| ||
18:56 | add pacakge disovery, and additional comment styles (different languages) check-in: f03780244f user: mario tags: trunk | |
12:56 | doc additions, add params for setup and gui, fix flit sample check-in: 93aac20151 user: mario tags: trunk | |
06:12 | updated template to mimick RTD theme (barely) check-in: e12fd2f3f3 user: mario tags: trunk | |
Changes
Modified html/depends.html from [ee1c309587] to [8dad0b793d].
︙ | ︙ | |||
46 47 48 49 50 51 52 | helper might want to auto-tick them on, etc. This example is just meant for probing downloadable plugins.</p> <p>The .valid() helper only asserts the api: string, or skips existing modules, and if they're more recent. While .depends() compares minimum versions against existing modules.</p> <p>In practice there's little need for full-blown dependency resolving for application-level modules.</p> | > > > | > > | > > > > | | > > > > > > > > | | > > | > | | > > > > > | > > | > > > > | | | > | > | > > | | 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 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 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 | helper might want to auto-tick them on, etc. This example is just meant for probing downloadable plugins.</p> <p>The .valid() helper only asserts the api: string, or skips existing modules, and if they're more recent. While .depends() compares minimum versions against existing modules.</p> <p>In practice there's little need for full-blown dependency resolving for application-level modules.</p> <table> <thead> <tr> <th>Attributes</th> <th></th> <th></th> </tr> </thead> <tbody> <tr> <td>api</td> <td>list</td> <td>allowed api: identifiers for .valid() stream checks</td> </tr> <tr> <td>system_deps</td> <td>bool</td> <td>check <code>bin:app</code> or <code>python:package</code> dependencies</td> </tr> <tr> <td>log</td> <td>logging</td> <td>warning handler</td> </tr> <tr> <td>have</td> <td>dict</td> <td>accumulated list of existing/virtual plugins</td> </tr> </tbody> </table> <p>Prepare list of known plugins and versions in self.have={}</p> <table> <thead> <tr> <th>Parameters</th> <th></th> <th></th> </tr> </thead> <tbody> <tr> <td>add</td> <td>dict</td> <td>name→pmd of existing/core plugins (incl ver or deps)</td> </tr> <tr> <td>core</td> <td>list</td> <td>name list of virtual plugins</td> </tr> </tbody> </table></div> <h3>Class variables</h3> <dl> <dt id="pluginconf.depends.Check.api"><code class="name">var <span class="ident">api</span></code></dt> <dd> <div class="desc"></div> </dd> <dt id="pluginconf.depends.Check.log"><code class="name">var <span class="ident">log</span></code></dt> |
︙ | ︙ | |||
97 98 99 100 101 102 103 | <dd> <div class="desc"><p>Single comparison</p></div> </dd> <dt id="pluginconf.depends.Check.depends"><code class="name flex"> <span>def <span class="ident">depends</span></span>(<span>self, plugin)</span> </code></dt> <dd> | | > > > > > > > > > > > > > > > > > > > > > | 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 | <dd> <div class="desc"><p>Single comparison</p></div> </dd> <dt id="pluginconf.depends.Check.depends"><code class="name flex"> <span>def <span class="ident">depends</span></span>(<span>self, plugin)</span> </code></dt> <dd> <div class="desc"><p>Verify depends: and breaks: against existing plugins/modules</p> <table> <thead> <tr> <th>Parameters</th> <th></th> <th></th> </tr> </thead> <tbody> <tr> <td>plugin</td> <td>dict</td> <td>plugin meta properties of (new?) plugin</td> </tr> <tr> <td><strong>Returns</strong></td> <td>bool</td> <td>matches up with existing .have{} installation</td> </tr> </tbody> </table></div> </dd> <dt id="pluginconf.depends.Check.module_test"><code class="name flex"> <span>def <span class="ident">module_test</span></span>(<span>self, urn, name)</span> </code></dt> <dd> <div class="desc"><p>Probes "bin:name" or "python:name" dependency URNs</p></div> </dd> |
︙ | ︙ | |||
125 126 127 128 129 130 131 | </dd> <dt id="pluginconf.depends.Check.valid"><code class="name flex"> <span>def <span class="ident">valid</span></span>(<span>self, new_plugin)</span> </code></dt> <dd> <div class="desc"><p>Plugin pre-screening from online repository stream. Fields are $name, $file, $dist, api, id, depends, etc | | > > > > > > > > > > > > > > > > > > > > > | 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 | </dd> <dt id="pluginconf.depends.Check.valid"><code class="name flex"> <span>def <span class="ident">valid</span></span>(<span>self, new_plugin)</span> </code></dt> <dd> <div class="desc"><p>Plugin pre-screening from online repository stream. Fields are $name, $file, $dist, api, id, depends, etc Exclude installed or for newer-version presence.</p> <table> <thead> <tr> <th>Parameters</th> <th></th> <th></th> </tr> </thead> <tbody> <tr> <td>new_plugin</td> <td>dict</td> <td>online properties of available plugin</td> </tr> <tr> <td><strong>Returns</strong></td> <td>bool</td> <td>is updatatable</td> </tr> </tbody> </table></div> </dd> </dl> </dd> </dl> </section> </article> <nav id="sidebar"> |
︙ | ︙ |
Modified html/flit.html from [560905addb] to [f4ec8aad3f].
︙ | ︙ | |||
26 27 28 29 30 31 32 | <p>monkeypatches flit to use pluginconf sources for packaging with a <code>pyproject.toml</code> like:</p> <table> <tr><th>pyproject.toml</th> <th>foobar/__init__.py</th></tr> <tr><td><code><pre> [build-system] | | | 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | <p>monkeypatches flit to use pluginconf sources for packaging with a <code>pyproject.toml</code> like:</p> <table> <tr><th>pyproject.toml</th> <th>foobar/__init__.py</th></tr> <tr><td><code><pre> [build-system] requires = ["pluginconf", "flit"] build-backend = "pluginconf.flit" [project] name = "foobar" dynamic = ["*"] </pre></code></td> <td><code><pre> |
︙ | ︙ |
Modified html/gui.html from [bd7e7636e7] to [8c9d3bdc20].
︙ | ︙ | |||
44 45 46 47 48 49 50 | <dd> <div class="desc"><p>checkbox for plugin name</p></div> </dd> <dt id="pluginconf.gui.plugin_layout"><code class="name flex"> <span>def <span class="ident">plugin_layout</span></span>(<span>pmd_list, config, plugin_states, opt_label=False)</span> </code></dt> <dd> | | > > > | > > | > > > > | | > > | > | > > | > | > > | > | > > | > | > > | > | > > | > | | > | | | | > | | 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 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 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 | <dd> <div class="desc"><p>checkbox for plugin name</p></div> </dd> <dt id="pluginconf.gui.plugin_layout"><code class="name flex"> <span>def <span class="ident">plugin_layout</span></span>(<span>pmd_list, config, plugin_states, opt_label=False)</span> </code></dt> <dd> <div class="desc"><p>craft list of widgets: *( <code><a title="pluginconf.gui.plugin_entry" href="#pluginconf.gui.plugin_entry">plugin_entry()</a></code>, *<code><a title="pluginconf.gui.option_entry" href="#pluginconf.gui.option_entry">option_entry()</a></code> )</p></div> </dd> <dt id="pluginconf.gui.read_options"><code class="name flex"> <span>def <span class="ident">read_options</span></span>(<span>files)</span> </code></dt> <dd> <div class="desc"><p>read files, return dict of {id:pmd} for all plugins</p></div> </dd> <dt id="pluginconf.gui.window"><code class="name flex"> <span>def <span class="ident">window</span></span>(<span>config, plugin_states, files=['*/*.py'], **kwargs)</span> </code></dt> <dd> <div class="desc"><p>Reads *.py files and crafts a settings dialog from meta data.</p> <p>Where <code>plugin_states{}</code> is usually an entry in <code>config{}</code> itself. Depending on plugin and option names, it might even be a flat/shared namespace for both. Per default you'd set <code>files=["plugins/*.py", __file__]</code> to be read. But with <code>files=[]</code> it's possible to provide a <code>plugins=pluginconf.get_plugin_meta()</code> or prepared plugin/options dict instead.</p> <table> <thead> <tr> <th>Params</th> <th></th> <th></th> </tr> </thead> <tbody> <tr> <td>config</td> <td>dict 🔁</td> <td>Config settings, updated after dialog completion</td> </tr> <tr> <td>plugin_states</td> <td>dict 🔁</td> <td>Plugin activation states, also input/output</td> </tr> <tr> <td>files</td> <td>list</td> <td>Glob list of *.py files to extract meta definitions from</td> </tr> <tr> <td>plugins</td> <td>dict</td> <td>Alternatively to files=[] list, a preparsed list of pluginmeta+config dicts can be injected</td> </tr> <tr> <td>opt_label</td> <td>bool</td> <td>Show config name= as label (instead of description)</td> </tr> <tr> <td>theme</td> <td>str</td> <td>Set PSG window theme.</td> </tr> <tr> <td>**kwargs</td> <td>dict</td> <td>Other options are passed on to PySimpleGUI</td> </tr> <tr> <td><strong>Returns</strong></td> <td>True</td> <td>if updated config{} values should be [Saved]</td> </tr> </tbody> </table></div> </dd> <dt id="pluginconf.gui.wrap"><code class="name flex"> <span>def <span class="ident">wrap</span></span>(<span>text, width=50)</span> </code></dt> <dd> <div class="desc"><p>textwrap for <code>description</code> and <code>help</code> option fields</p></div> </dd> |
︙ | ︙ |
Modified html/index.html from [62873924d5] to [bd45e395b2].
︙ | ︙ | |||
86 87 88 89 90 91 92 | <section> <h2 class="section-title" id="header-functions">Functions</h2> <dl> <dt id="pluginconf.add_plugin_defaults"><code class="name flex"> <span>def <span class="ident">add_plugin_defaults</span></span>(<span>conf_options, conf_plugins, meta, module='')</span> </code></dt> <dd> | | | < | | | 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 | <section> <h2 class="section-title" id="header-functions">Functions</h2> <dl> <dt id="pluginconf.add_plugin_defaults"><code class="name flex"> <span>def <span class="ident">add_plugin_defaults</span></span>(<span>conf_options, conf_plugins, meta, module='')</span> </code></dt> <dd> <div class="desc"><p>Utility function to collect defaults from plugin meta data to a config dict/store.</p> <table> <thead> <tr> <th>Parameters</th> <th></th> <th></th> </tr> </thead> <tbody> <tr> <td>conf_options</td> <td>dict 🔁</td> <td>storage for amassed #config: options</td> </tr> <tr> <td>conf_plugins</td> <td>dict 🔁</td> <td>activation status derived from state/priority:</td> </tr> <tr> <td>meta</td> <td>dict</td> <td>input plugin meta data (invoke once per plugin)</td> </tr> <tr> |
︙ | ︙ | |||
145 146 147 148 149 150 151 | <th></th> </tr> </thead> <tbody> <tr> <td><strong>Returns</strong></td> <td>dict</td> | | | 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 | <th></th> </tr> </thead> <tbody> <tr> <td><strong>Returns</strong></td> <td>dict</td> <td>names to <code><a title="pluginconf.PluginMeta" href="#pluginconf.PluginMeta">PluginMeta</a></code> dict</td> </tr> </tbody> </table></div> </dd> <dt id="pluginconf.get_data"><code class="name flex"> <span>def <span class="ident">get_data</span></span>(<span>filename, decode=False, gzip=False, file_root=None)</span> </code></dt> |
︙ | ︙ |
Modified html/setup.html from [6a9e752056] to [e6c6068172].
︙ | ︙ | |||
45 46 47 48 49 50 51 | </dd> <dt id="pluginconf.setup.setup"><code class="name flex"> <span>def <span class="ident">setup</span></span>(<span>filename=None, debug=False, **kwargs)</span> </code></dt> <dd> <div class="desc"><p>Wrapper around <code>setuptools.setup()</code> which adds some defaults and plugin meta data import, with some shortcut parameters:</p> | > > > | > > | > > > | > | > > | > | > > | > | | > > | 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 | </dd> <dt id="pluginconf.setup.setup"><code class="name flex"> <span>def <span class="ident">setup</span></span>(<span>filename=None, debug=False, **kwargs)</span> </code></dt> <dd> <div class="desc"><p>Wrapper around <code>setuptools.setup()</code> which adds some defaults and plugin meta data import, with some shortcut parameters:</p> <table> <thead> <tr> <th>Parameters</th> <th></th> <th></th> </tr> </thead> <tbody> <tr> <td>filename</td> <td>str</td> <td>main file "pkg/main.py" (else deduced from primary package name)</td> </tr> <tr> <td>debug</td> <td>bool</td> <td>display collected options prior setuptools.setup() invocation</td> </tr> <tr> <td>long_description</td> <td>str</td> <td>e.g. "README.md", else the comment block gets used</td> </tr> </tbody> </table> <p>Other setup() params work as usual, and are passed trough. Notably entry_points= or data_files= can be used, even if they get augmented.</p></div> </dd> </dl> </section> <section> <h2 class="section-title" id="header-classes">Classes</h2> |
︙ | ︙ |
Modified pluginconf/__init__.py from [66c11c0c8c] to [5e7e9e9afe].
︙ | ︙ | |||
9 10 11 12 13 14 15 | # classifiers: documentation # depends: python >= 2.7 # suggests: python:flit, python:PySimpleGUI # license: PD # priority: core # api-docs: https://fossil.include-once.org/pluginspec/doc/trunk/html/index.html # docs: https://fossil.include-once.org/pluginspec/ | | | 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | # classifiers: documentation # depends: python >= 2.7 # suggests: python:flit, python:PySimpleGUI # license: PD # priority: core # api-docs: https://fossil.include-once.org/pluginspec/doc/trunk/html/index.html # docs: https://fossil.include-once.org/pluginspec/ # url: https://fossil.include-once.org/pluginspec/wiki/pluginconf # config: - # format: off # permissive: 0.75 # pylint: disable=invalid-name # console-scripts: flit-pluginconf=pluginconf.flit:main # # Provides plugin lookup and meta data extraction utility functions. |
︙ | ︙ | |||
250 251 252 253 254 255 256 | """ This is a trivial wrapper to assemble a complete dictionary of available/installed plugins. It associates each plugin name with a its meta{} fields. | Parameters | | | |-------------|---------|---------------------------------| | | | 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 | """ This is a trivial wrapper to assemble a complete dictionary of available/installed plugins. It associates each plugin name with a its meta{} fields. | Parameters | | | |-------------|---------|---------------------------------| | **Returns** | dict | names to `PluginMeta` dict | """ return { name: plugin_meta(module=name) for name in module_list() } # Plugin meta data extraction |
︙ | ︙ | |||
555 556 557 558 559 560 561 | return {k: w for k, w in kwargs.items() if w is not None} # Add plugin defaults to conf.* store # ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾ def add_plugin_defaults(conf_options, conf_plugins, meta, module=""): """ | | | < | | | 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 | return {k: w for k, w in kwargs.items() if w is not None} # Add plugin defaults to conf.* store # ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾ def add_plugin_defaults(conf_options, conf_plugins, meta, module=""): """ Utility function to collect defaults from plugin meta data to a config dict/store. | Parameters | | | |-------------|---------|--------------------------------------| | conf_options| dict 🔁 | storage for amassed #config: options | | conf_plugins| dict 🔁 | activation status derived from state/priority: | | meta | dict | input plugin meta data (invoke once per plugin)| | module | str | basename of meta: blocks plugin file | | **Returns** | None | - | """ # Option defaults, if not yet defined for opt in meta.get("config", []): |
︙ | ︙ |
Modified pluginconf/bind.py from [ce733ed382] to [fcff8bd0e5].
1 2 3 4 5 6 7 8 9 10 11 | # encoding: utf-8 # api: pluginconf ##type: loader # title: plugin loader # description: implements a trivial unified namespace importer # version: 0.1 # state: alpha # priority: optional # # Most basic plugin management/loader. It unifies meta data retrieval # and instantiation. It still doesn't dictate a plugin API or config | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | # encoding: utf-8 # api: pluginconf ##type: loader # title: plugin loader # description: implements a trivial unified namespace importer # version: 0.1 # state: alpha # priority: optional # # Most basic plugin management/loader. It unifies meta data retrieval # and instantiation. It still doesn't dictate a plugin API or config # storage (using json in examples). And can be a simpler setup: # # Create an empty plugins/__init__.py to use as package and for # plugin discovery. # # Designate it as such: # # import pluginconf.bind # (first import resets .plugin_base) # pluginconf.bind.base("plugins") # # Set up a default conf={} in your application, with some presets, # or updating it from a stored config file: # # conf = { # "first_run": 1, |
︙ | ︙ | |||
37 38 39 40 41 42 43 | # pluginconf.bind.defaults(conf) # # Instantiate plugin modules based on load conf[plugins] state: # # for module in pluginconf.bind.load_enabled(conf): # module.register(main_window) # | | > > | | | | | > > > > > > > > > > > > > | 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 | # pluginconf.bind.defaults(conf) # # Instantiate plugin modules based on load conf[plugins] state: # # for module in pluginconf.bind.load_enabled(conf): # module.register(main_window) # # Electing a simple init function often suffices, if plugins don't # register themselves. Alternatively use a class name aligned with # the plugin basename to disover it, or dir(), or similar such. # (This is what "pluginconf imposes no API" means: you still have # to decide on a convention.) # # If you want users to update settings or plugin states, use the # .window module: # # pluginconf.gui.window(conf, conf["plugins"], files=["plugins/*.py"]) # json.dump(conf, open("app.json", "w")) # # Alternatively there's load() for well-known plugin names, or find() # to uncover them based on descriptors. Or isolated() to instantiate # from a different set. # # Notably the whole effort makes most sense if you have user-supplied # plugins and some repository to fetch/update new ones from. (Optional # meta descriptions are quite suitable to signify beta or experimental # extensions). If so, entangle the alternative directory to be scanned: # # pluginconf.bind.base("plugins", dir="~/.config/app/usrplugs/") # pluginconf.bind.defaults(conf) # update # # A simpler user plugin mechanism might just download a zip: # # usr_pyz = f"{os.environ['HOME']}/.config/app/community.pyz" # with open(usr_pyz, "w") as write: # write.write( # requests.get("http://example.org/usr-plugins.zip").content # ) # # And register that as pyz instead (on startup): # # if os.path.exists(usr_pyz): # pluginconf.bind.base("plugins", dir=usr_pyz) # # With PySimpleGUI, `conf` could be a psg.UserSettings("app.json") for # automatic settings+update storage, btw. # #-- bit briefer for API docs -- """ |
︙ | ︙ | |||
93 94 95 96 97 98 99 | mod.init() ### Find by type for name, pmd in pluginconf.bind.find(type="effect").items(): mod = pluginconf.bind.load(name) if pmd.category == "menu": | | > | 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 | mod.init() ### Find by type for name, pmd in pluginconf.bind.find(type="effect").items(): mod = pluginconf.bind.load(name) if pmd.category == "menu": main_win.add_menu(mod.menu) Note that this uses meta data extraction, so should best be confined for app setup/initialization, not used recurringly. The config state usage is the preferred method. (Only requires property loading once, for installation or with `pluginconf.gui.window()` usage.) ---- Method interactions: 🚐 = import, 🧩 = meta, 🧾 = config, 🛠 = setup """ import os import sys import importlib import functools import pluginconf #-- reset pluginconf if .bind is used pluginconf.plugin_base = [] def load(name): |
︙ | ︙ | |||
148 149 150 151 152 153 154 | |-------------|------------|-------------------------------| | module | module/str | Package basename to later load plugins from | | path | str | Bind directory or pyz/zip bundle to plugin_base. | | **Returns** | None | - | Module should be a package, as in a directory and init `plugins/__init__.py`. Ideally this module was already imported in main. But parameter may be a string. | | | 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 | |-------------|------------|-------------------------------| | module | module/str | Package basename to later load plugins from | | path | str | Bind directory or pyz/zip bundle to plugin_base. | | **Returns** | None | - | Module should be a package, as in a directory and init `plugins/__init__.py`. Ideally this module was already imported in main. But parameter may be a string. This could be invoked multiple times for the package name to append further path= arguments (=./contrib/, =/usr/share/app/extenstions/, or a .pyz). Which is functionally identical to delcaring `__path__` in the `package/__init__.py`. """ # if supplied as string, instead of active module if isinstance(module, str): |
︙ | ︙ | |||
276 277 278 279 280 281 282 283 284 285 | @staticmethod def defaults(): """ *return* defaults for isolated plugin structure 🧩 🧾 """ conf = {"plugins": {}} defaults(conf) return conf def _dirname(file): return os.path.dirname(os.path.realpath(file)) | > > > > > > > > > > > > > > | 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 | @staticmethod def defaults(): """ *return* defaults for isolated plugin structure 🧩 🧾 """ conf = {"plugins": {}} defaults(conf) return conf def _enable_cache(state=True): """ Reduce plugin_meta() lookup costs, suitable for repeat find() calls """ if hasattr(pluginconf.plugin_meta, "__wrapped__"): if state: return pluginconf.plugin_meta = pluginconf.plugin_meta.__wrapped__ elif state: decorator = functools.lru_cache(maxsize=None) pluginconf.plugin_meta = decorator(pluginconf.plugin_meta) def _dirname(file): """ absolute dirname for file """ return os.path.dirname(os.path.realpath(file)) |
Modified pluginconf/depends.py from [f7f101f5bf] to [b7808bf08a].
︙ | ︙ | |||
56 57 58 59 60 61 62 | The .valid() helper only asserts the api: string, or skips existing modules, and if they're more recent. While .depends() compares minimum versions against existing modules. In practice there's little need for full-blown dependency resolving for application-level modules. | | | < | < > | < | | | < | < < | | 56 57 58 59 60 61 62 63 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 92 93 94 | The .valid() helper only asserts the api: string, or skips existing modules, and if they're more recent. While .depends() compares minimum versions against existing modules. In practice there's little need for full-blown dependency resolving for application-level modules. | Attributes | | | |------------|---------|-----------------------------------------------------| | api | list | allowed api: identifiers for .valid() stream checks | | system_deps| bool | check `bin:app` or `python:package` dependencies | | 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=None, core=["st2", "uikit", "config", "action"]): """ Prepare list of known plugins and versions in self.have={} | Parameters | | | |------------|---------|------------------------------------------------------| | add | dict | name→pmd of existing/core plugins (incl ver or deps) | | core | list | name list of virtual plugins | """ self.have = { "python": {"version": sys.version} } # inject virtual modules for name, meta in (add or {}).items(): |
︙ | ︙ | |||
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 | 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): | > > > > > > | > > > > > > | 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 | 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. | Parameters | | | |-------------|---------|------------------------------------------------------| | new_plugin | dict | online properties of available plugin | | **Returns** | bool | is updatatable | """ 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 | Parameters | | | |-------------|---------|------------------------------------------------------| | plugin | dict | plugin meta properties of (new?) plugin | | **Returns** | bool | matches up with existing .have{} installation | """ 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 |
︙ | ︙ |
Modified pluginconf/flit.py from [4725663a44] to [7e6be741f0].
︙ | ︙ | |||
33 34 35 36 37 38 39 | `pyproject.toml` like: <table> <tr><th>pyproject.toml</th> <th>foobar/__init__.py</th></tr> <tr><td><code><pre> [build-system] | | | 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | `pyproject.toml` like: <table> <tr><th>pyproject.toml</th> <th>foobar/__init__.py</th></tr> <tr><td><code><pre> [build-system] requires = ["pluginconf", "flit"] build-backend = "pluginconf.flit" [project] name = "foobar" dynamic = ["*"] </pre></code></td> <td><code><pre> |
︙ | ︙ |
Modified pluginconf/gui.py from [fd35fa0517] to [579f93bf8f].
︙ | ︙ | |||
48 49 50 51 52 53 54 | Reads *.py files and crafts a settings dialog from meta data. Where `plugin_states{}` is usually an entry in `config{}` itself. Depending on plugin and option names, it might even be a flat/shared namespace for both. Per default you'd set `files=["plugins/*.py", __file__]` to be read. But with `files=[]` it's possible to provide a `plugins=pluginconf.get_plugin_meta()` or prepared plugin/options dict instead. | | | < | < | < | < | < | < | < | | < < < | 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 | Reads *.py files and crafts a settings dialog from meta data. Where `plugin_states{}` is usually an entry in `config{}` itself. Depending on plugin and option names, it might even be a flat/shared namespace for both. Per default you'd set `files=["plugins/*.py", __file__]` to be read. But with `files=[]` it's possible to provide a `plugins=pluginconf.get_plugin_meta()` or prepared plugin/options dict instead. | Params | | | |---------------|---------|---------------------------------------------------------| | config | dict 🔁 | Config settings, updated after dialog completion | | plugin_states | dict 🔁 | Plugin activation states, also input/output | | files | list | Glob list of *.py files to extract meta definitions from| | plugins | dict | Alternatively to files=[] list, a preparsed list of pluginmeta+config dicts can be injected | | opt_label | bool | Show config name= as label (instead of description) | | theme | str | Set PSG window theme. | | **kwargs | dict | Other options are passed on to PySimpleGUI | | **Returns** | True | if updated config{} values should be [Saved] | """ plugins = kwargs.get("plugins", {}) opt_label = kwargs.get("opt_label", False) theme = kwargs.get("theme", "DefaultNoMoreNagging") if theme: if "theme" in kwargs: del kwargs["theme"] |
︙ | ︙ | |||
110 111 112 113 114 115 116 | if plugins.get(key): plugin_states[key] = val return True #print(config, plugin_states) def plugin_layout(pmd_list, config, plugin_states, opt_label=False): | | | 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 | if plugins.get(key): plugin_states[key] = val return True #print(config, plugin_states) def plugin_layout(pmd_list, config, plugin_states, opt_label=False): """ craft list of widgets: \*( `plugin_entry`, \*`option_entry` )""" layout = [] for plg in pmd_list: #print(plg.get("id")) layout = layout + plugin_entry(plg, plugin_states) for opt in plg["config"]: if opt.get("name"): if opt_label: |
︙ | ︙ |
Modified pluginconf/setup.py from [a5d9b67dec] to [69618d8a4a].
︙ | ︙ | |||
199 200 201 202 203 204 205 | @pluginconf.renamed_arguments({"fn": "filename"}) def setup(filename=None, debug=False, **kwargs): """ Wrapper around `setuptools.setup()` which adds some defaults and plugin meta data import, with some shortcut parameters: | | | < | < | < | | 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 | @pluginconf.renamed_arguments({"fn": "filename"}) def setup(filename=None, debug=False, **kwargs): """ Wrapper around `setuptools.setup()` which adds some defaults and plugin meta data import, with some shortcut parameters: | Parameters | | | |-------------|--------|------------------------------------------------| | filename | str | main file "pkg/main.py" (else deduced from primary package name) | | debug | bool | display collected options prior setuptools.setup() invocation | |long_description| str | e.g. "README.md", else the comment block gets used | Other setup() params work as usual, and are passed trough. Notably entry_points= or data_files= can be used, even if they get augmented. """ # optionalized here (avoid dependency in flit import) # p-y-l-i-n-t: disable=import-outside-toplevel |
︙ | ︙ |