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
229
230
231
232
233
234
235
236
237
| #-- reset pluginconf if .bind is used
pluginconf.plugin_base = []
def load(name):
"""
Import individual plugin from any of the base paths 🚐
Parameters
----------
name : str
Plugin name in any of the registered plugin_base´s. (The whole
namespace is assumed to be flat, and identifiers to be unique.)
"""
for package in pluginconf.plugin_base:
if package in ("", "."):
continue
module_name = package + "." + name
if module_name in sys.modules:
return sys.modules[module_name]
try:
return importlib.import_module(module_name)
except ImportError:
pass
def base(module, path=None):
"""
Register module as package/plugin_base. Or expand its search path 🛠 .
Parameters
----------
module : module/str
The package basename to later load plugins from (must be a package,
like `plugins/__init__.py`, or be tied to a path= or zip). Ideally
this module was already imported in main. But parameter may be a string.
path : str
Add a directory or pyz/zip bundle to registered plugin_base. Could
be invoked multiple times =./contrib/, =/usr/share/app/extenstions/,
=~/.config/app/userplug/ (same as declaring the `__path__` in the
base `package/__init__.py`.)
"""
# if supplied as string, instead of active module
if isinstance(module, str):
module = sys.modules.get(module) or __import__(module)
# add to search list
if module.__name__ not in pluginconf.plugin_base:
pluginconf.plugin_base.append(module.__name__)
# enjoin dir or pyz
if not hasattr(module, "__path__"):
module.__path__ = [_dirname(module.__file__)]
if path:
module.__path__.append(_dirname(path))
def find(**kwargs):
"""
Find plugins by e.g. type= or category= 🧩
Parameters
----------
type : str
Usually you'd search on a designated plugin categorization, like type=
and api=, or slot=, or class= or whatever is most consistent. Multiple
attributes can be filtered on. (Version/title probably not useful here.)
Returns
----------
dict : basename → PluginMeta dict
"""
def has_all(pmd):
for key, dep in kwargs.items():
if not pmd.get(key) == dep:
break
else:
return True
return pluginconf.PluginMeta({
name: pmd for name, pmd in pluginconf.all_plugin_meta().items() if has_all(pmd)
})
def load_enabled(conf):
"""
Import modules that are enabled in conf[plugins]={name:True,…} 🧾 🚐
Parameters
----------
conf : dict
Simple options-value dictionary, but with one conf["plugins"] = {} subdict,
which holds plugin activation states. The config dict doesn't have to match
the available plugin options (defaults can be added), but should focus on
essential presets. Differentiation only might become relevant for storage.
"""
for name, state in conf.get("plugins", conf).items():
if not state:
continue
if name.startswith("_"):
continue
yield load(name)
def defaults(conf):
"""
Traverse installed plugins and expand config dict with presets 🧩 🧾
Parameters
----------
conf : dict 🔁
Expands the top-level config dict with preset values from any plugins.
"""
for name, pmd in pluginconf.all_plugin_meta().items():
pluginconf.add_plugin_defaults(conf, conf["plugins"], pmd, name)
# pylint: disable=invalid-name
class isolated():
|
>
|
|
<
|
|
|
|
<
|
>
>
|
>
|
|
<
>
|
<
|
|
|
|
|
|
<
<
|
<
<
|
|
|
<
<
|
<
<
>
|
|
<
|
>
| 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
229
230
231
| #-- reset pluginconf if .bind is used
pluginconf.plugin_base = []
def load(name):
"""
Import individual plugin from any of the base paths 🚐
(The whole namespace is assumed to be flat, and identifiers to be unique.)
| Parameters | | |
|-------------|--------|-------------------------------|
| name | str | Plugin name in any of the registered plugin_base´s. |
| **Returns** | module | Imported module |
"""
for package in pluginconf.plugin_base:
if package in ("", "."):
continue
module_name = package + "." + name
if module_name in sys.modules:
return sys.modules[module_name]
try:
return importlib.import_module(module_name)
except ImportError:
pass
def base(module, path=None):
"""
Register module as package/plugin_base. Or expand its search path 🛠 .
| Parameters | | |
|-------------|------------|-------------------------------|
| 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):
module = sys.modules.get(module) or __import__(module)
# add to search list
if module.__name__ not in pluginconf.plugin_base:
pluginconf.plugin_base.append(module.__name__)
# enjoin dir or pyz
if not hasattr(module, "__path__"):
module.__path__ = [_dirname(module.__file__)]
if path:
module.__path__.append(_dirname(path))
def find(**kwargs):
"""
Find plugins by any combination of e.g. type= or category= 🧩
| Parameters | | |
|-------------|-----------|-------------------------------------------|
| type | str | Search by type: in plugins |
| api | str | Matching api: designator |
| category | str | Or a menu/category or other attributes |
| **Returns** | dict | basename → `PluginMeta` dict of matches |
"""
def has_all(pmd):
for key, dep in kwargs.items():
if not pmd.get(key) == dep:
break
else:
return True
return pluginconf.PluginMeta({
name: pmd for name, pmd in pluginconf.all_plugin_meta().items() if has_all(pmd)
})
def load_enabled(conf):
"""
Import modules that are enabled in conf[plugins]={name:True,…} 🧾 🚐
| Parameters | | |
|-------------|-----------|-------------------------------------------|
| conf | dict | Should contain conf["plugins"] activation states |
| **Returns** | generator | Of loaded modules |
"""
for name, state in conf.get("plugins", conf).items():
if not state:
continue
if name.startswith("_"):
continue
yield load(name)
def defaults(conf):
"""
Traverse installed plugins and expand config dict with presets 🧩 🧾
| Parameters | | |
|-------------|-----------|-------------------------------------------|
| conf | dict 🔁 | Expands the conf dict with preset values from any plugins. |
| **Returns** | None | - |
"""
for name, pmd in pluginconf.all_plugin_meta().items():
pluginconf.add_plugin_defaults(conf, conf["plugins"], pmd, name)
# pylint: disable=invalid-name
class isolated():
|