Index: pluginconf/__init__.py ================================================================== --- pluginconf/__init__.py +++ pluginconf/__init__.py @@ -370,26 +370,40 @@ meta["config"] = plugin_meta_config(meta.get("config") or "") return PluginMeta(meta) -# Dict wrapper -# ‾‾‾‾‾‾‾‾‾‾‾‾ +# Dict/list wrappers +# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾ class PluginMeta(dict): """ Plugin meta data, as dictionary with alternative .property access. Returned for each `plugin_meta()` result, and config: options. Non-existent .fieldnames just resolve to `""`. """ + + def __getattr__(self, key, default=""): + """ Return [key] for .property access, else `""`. """ + if key == "config": + default = OptionList() + return self.get(key, default) + + def __setattr__(self, key, val): + """ Shouldn't really have this, but for parity. """ + self[key] = val + +class OptionList(list): + """ + List of config: options, with additional .name access (name= from option entry). + """ def __getattr__(self, key): - """ Return [key] for .property access, else None """ - return self.get(key, "") - - def __hasattr__(self, key): - """ Return [key] for .property access, else None """ - return key in self + """ Returns list entry with name= equaling .name access """ + for opt in self: + if opt.name == key: + return opt + raise KeyError("No option name '%s' in config list" % key) # Unpack config: structures # ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾ def plugin_meta_config(src): @@ -427,11 +441,12 @@ opt["type"] = config_opt_type_map.get(opt["type"], opt["type"] or "str") # preparse select: if opt.get("select"): opt["select"] = config_opt_parse_select(opt.get("select", "")) config.append(opt) - return config + + return OptionList(PluginMeta(opt) for opt in config) # split up `select: 1=on|2=more|3=title` or `select: foo|bar|lists` def config_opt_parse_select(select): """ unpack 1|2|3 or title=lists """ if re.search("([=:])", select): Index: pluginconf/gui.py ================================================================== --- pluginconf/gui.py +++ pluginconf/gui.py @@ -74,11 +74,12 @@ """ plugins = kwargs.get("plugins", {}) opt_label = kwargs.get("opt_label", False) theme = kwargs.get("theme", "DefaultNoMoreNagging") if theme: - del kwargs["theme"] + if "theme" in kwargs: + del kwargs["theme"] sg.theme(theme) if files: plugins = read_options(files) layout = plugin_layout(plugins.values(), config, plugin_states, opt_label=opt_label) layout.append([sg.T(" ")]) Index: test/basic.py ================================================================== --- test/basic.py +++ test/basic.py @@ -11,21 +11,21 @@ @pytest.fixture def pmd(): return pluginconf.plugin_meta(fn=__file__) -def type_(pmd): +def type_from_dict(pmd): assert pmd["type"] == "test" -def version_(pmd): +def version_as_prop(pmd): assert pmd.version == "0.1-rc1" -def title_(pmd): - assert pmd["title"] == "basic PMD" +def title_text(pmd): + assert pmd.title == "basic PMD" -def doc_(pmd): +def doc_field(pmd): assert pmd["doc"] == "This the doc." # Should probably migrate all to PluginMeta.property access def test_all_as_props(pmd): for key, val in pmd.items(): assert getattr(pmd, key) == val ADDED test/config_listwrapper.py Index: test/config_listwrapper.py ================================================================== --- test/config_listwrapper.py +++ test/config_listwrapper.py @@ -0,0 +1,28 @@ +# type: test +# title: config list wrapper +# description: check if ConfigList() access works +# config: +# { name: key, value: "test", description: "should be accessible per pmd.config.key.value" } +# { name: alt, type: bool, value: 1 } +# version: 0.8+ +# +# Do all the settings! + +import pytest +import pluginconf + +@pytest.fixture +def pmd(): + return pluginconf.plugin_meta(fn=__file__) + +def key_path(pmd): + assert pmd.config.key.value == "test" + # ^^^ + # name lookup + +def path_type(pmd): + assert pmd.config.alt.value == '1' + +def nonexistent(pmd): + with pytest.raises(KeyError) as exc: + assert pmd.config.donothave.help