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
27
28
29
30
31
32
33
34
35
36
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
| 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/index)
(*self-contained* atop each script) is basically:
# encoding: utf-8
# api: python
# type: handler
# category: io
# title: Plugin configuration
# description: Read meta data, pyz/package contents, module locating
# version: 0.5
# priority: core
# docs: http://fossil.include-once.org/streamtuner2/wiki/plugin+meta+data
# config:
# { name: xyz, value: 1, type: bool, description: "Sets..." }
# { name: var, value: "init", description: "Another..." }
# license: MITL
#
# Documentation goes here...
The `key: value` format is language-agnostic. It’s basically YAML in the
topmost script comment. For Python only # hash comments are used. Defaults
to rather common field names, encourages a documentation block, and an
obvious [config: { .. } scheme](https://fossil.include-once.org/pluginspec/wiki/config)
for options and defaults.
How it's used:
import pluginconf
meta = pluginconf.plugin_meta(fn="./plugin/func.py")
print(meta)
What it’s not:
> * This is not another config reader/parser/storage class.
> * Doesn’t impose a specific plugin API.
> * Neither concerns itself with module/package loading. (See [pluginbase](https://pypi.org/project/pluginbase/) or just `__import__`.)
What for then?
> * Separates code from meta data. Avoids keeping seldomly used descriptors in variables.
> * Does away with externalized ini/json files for modules, yet simplifies use of external tooling.
> * Minimizes premature module loading just to inspect meta information.
pluginconf is less about a concrete implementation/code, but pushing a universal meta data format.
# API
Lookup configuration is currently just done through injection:
plugin_base = [__package__, "myapp.plugins", "/usr/share/app/extensions"]
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( module= | fn= | src= | frame= )
Returns a meta data dictionary for the given module name, file, source code, or caller frame:
{
"title": "Compound★",
"description": "...",
"version": "0.1",
|
|
|
|
|
| 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
27
28
29
30
31
32
33
34
35
36
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
| 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/index)
(*self-contained* atop each script) is basically:
# encoding: utf-8
# api: python
# type: handler
# category: io
# title: Plugin configuration
# description: Read meta data, pyz/package contents, module locating
# version: 0.5
# priority: core
# docs: https://fossil.include-once.org/pluginspec/
# config:
# { name: xyz, value: 1, type: bool, description: "Sets..." }
# { name: var, value: "init", description: "Another..." }
# license: MITL
#
# Documentation goes here...
The `key: value` format is language-agnostic. It’s basically YAML in the
topmost script comment. For Python only # hash comments are used. Defaults
to rather common field names, encourages a documentation block, and an
obvious [config: { .. } scheme](https://fossil.include-once.org/pluginspec/wiki/config)
for options and defaults.
How it's used:
import pluginconf
meta = pluginconf.plugin_meta(filename="./plugin/func.py")
print(meta)
What it’s not:
> * This is not another config reader/parser/storage class.
> * Doesn’t impose a specific plugin API.
> * Neither concerns itself with module/package loading. (See [pluginbase](https://pypi.org/project/pluginbase/) or just `__import__`.)
What for then?
> * Separates code from meta data. Avoids keeping seldomly used descriptors in variables.
> * Does away with externalized ini/json files for modules, yet simplifies use of external tooling.
> * Minimizes premature module loading just to inspect meta information.
pluginconf is foremost about the universal meta comment format.
# API
Lookup configuration is currently just done through injection:
plugin_base = [__package__, "myapp.plugins", "/usr/share/app/extensions"]
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( 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",
|
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
| pluginconf.plugin_base = [__package__]
conf = {
"defaults": "123",
"plugins": {} # ← stores the activation states
}
for module,meta in pluginconf.all_plugin_meta().items():
pluginconf.add_plugin_defaults(conf, conf["plugins"], meta, module)
# share the same dict ↑ ↑
#### get_data( fn= )
Is mostly an alias for pkgutil.get_data(). Abstracts usage within PYZ packages a little.
#### argparse_map()
Provides a simpler way to specify ugly argparse definitions. And allows to amass options from plugins.
|
|
|
| 93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
| pluginconf.plugin_base = [__package__]
conf = {
"defaults": "123",
"plugins": {} # ← stores the activation states
}
for module, meta in pluginconf.all_plugin_meta().items():
pluginconf.add_plugin_defaults(conf, conf["plugins"], meta, module)
# share the same dict ↑ ↑
#### get_data( filename= )
Is mostly an alias for pkgutil.get_data(). Abstracts usage within PYZ packages a little.
#### argparse_map()
Provides a simpler way to specify ugly argparse definitions. And allows to amass options from plugins.
|
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
| # setup.py wrapper
Another obvious use case for PMD is simplifying packaging. A `setup()`
script can become as short as:
from pluginconf.setup import setup
setup(
fn="main/pkg.py"
)
Which will reuse version: and descriptors from the meta comment. For simple
one-module packages, you might get away with just `setup()` and an all
automatic lookup. The non-standard PMD field `# classifiers: x11, python`
can be used to lookup trove categories (crude search on select topics).
All other `setup(fields=…)` are passed on to distutils/setuptools as is.
-- Btw, [setupmeta](https://pypi.org/project/setupmeta/) is an even more
versatile wrapper with sensible defaults and source scanning.
#### Caveats
* It’s mostly just an excerpt from streamtuner2.
* Might need customization prior use.
* The GUI implmentation is fairly simplistic.
* Doesn't bundle any plugin repo loader logic.
* So doesn't make use of the dependency class.
* The description fields can double as packaging source (setup.py). There's also a
[# pack: specifier](https://fossil.include-once.org/pluginspec/wiki/References)
for fpm (deb/rpm/arch/exe/pyzw/pip generation), unused in the `setup.py`
wrapper here however.
* In Python `# type:` might neede doubled commenting (## pylint), or alternating to
other descriptors like`class:` or `slot:`. (The whole scheme is agnostic to
designators. Common keys are just recommended for potential interoparability.)
* The format incidentally mixes well with established comment markers like
`# format: off` or `# pylint: disable=…` for instance.
|
|
>
>
>
>
>
>
>
| 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
| # setup.py wrapper
Another obvious use case for PMD is simplifying packaging. A `setup()`
script can become as short as:
from pluginconf.setup import setup
setup(
filename="main/pkg.py"
)
Which will reuse version: and descriptors from the meta comment. For simple
one-module packages, you might get away with just `setup()` and an all
automatic lookup. The non-standard PMD field `# classifiers: x11, python`
can be used to lookup trove categories (crude search on select topics).
All other `setup(fields=…)` are passed on to distutils/setuptools as is.
-- Btw, [setupmeta](https://pypi.org/project/setupmeta/) is an even more
versatile wrapper with sensible defaults and source scanning.
# other modules
* DependencyValidation is now in `pluginconf.depends`
* `pluginconf.flit` is meant as alternative to setup.
#### Caveats
* It’s mostly just an excerpt from streamtuner2.
* Might need customization prior use.
* The GUI implmentation is fairly simplistic.
* Doesn't bundle any plugin repo loader logic.
* So doesn't make use of the dependency class.
* The description fields can double as packaging source (setup.py). There's also a
[# pack: specifier](https://fossil.include-once.org/pluginspec/wiki/References)
for fpm (deb/rpm/arch/exe/pyzw/pip generation), unused in the `setup.py`
wrapper here however.
* In Python `# type:` might neede doubled commenting (## pylint), or alternating to
other descriptors like`class:` or `slot:`. (The whole scheme is agnostic to
designators. Common keys are just recommended for potential interoparability.)
* The format incidentally mixes well with established comment markers like
`# format: off` or `# pylint: disable=…` for instance.
|