Differences From Artifact [8927125c8f]:

To Artifact [ce733ed382]:


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
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
    ----------
    | 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.)
    | 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
    ----------
    | 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
    | 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.
    
        Add a directory or pyz/zip bundle to registered plugin_base. Could
        be invoked multiple times =./contrib/, =/usr/share/app/extenstions/,
    This could be invoked multiple times for the package name to append further
    path= arguments (=./contrib/, =/usr/share/app/extenstions/, or a .pyz). Which
        =~/.config/app/userplug/ (same as declaring the `__path__` in the
        base `package/__init__.py`.)
    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 e.g. type= or category= 🧩
    Find plugins by any combination of e.g. type= or category= 🧩

    Parameters
    ----------
    type : str
        Usually you'd search on a designated plugin categorization, like type=
    | Parameters  | | |
    |-------------|-----------|-------------------------------------------|
    | type        | str       | Search by type: in plugins                |
    | api         | str       | Matching api: designator                  |
        and api=, or slot=, or class= or whatever is most consistent. Multiple
        attributes can be filtered on. (Version/title probably not useful here.)

    | category    | str       | Or a menu/category or other attributes    |
    Returns
    ----------
    dict : basename → PluginMeta dict
    | **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
    ----------
    | 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
    | conf        | dict      | Should contain conf["plugins"] activation states |
        the available plugin options (defaults can be added), but should focus on
        essential presets. Differentiation only might become relevant for storage.
    | **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
    ----------
    | Parameters  | | |
    |-------------|-----------|-------------------------------------------|
    conf : dict 🔁
        Expands the top-level config dict with preset values from any plugins.
    | 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():