Mercurial > hgrepos > Python > libs > ConfigMix
changeset 171:1ff11462a5c1
The associations from filename extensions to parsers are "fnmatch" style patterns now.
Calling "configmix.set_loader" prepends to the currently defined
associations and therefore gets the highest priority.
| author | Franz Glasner <f.glasner@feldmann-mg.com> |
|---|---|
| date | Thu, 25 Apr 2019 17:00:09 +0200 |
| parents | c247a5dc35ed |
| children | 8138d56d7cd3 |
| files | CHANGES.txt configmix/__init__.py doc/introduction.rst tests/test.py |
| diffstat | 4 files changed, 86 insertions(+), 42 deletions(-) [+] |
line wrap: on
line diff
--- a/CHANGES.txt Thu Apr 25 16:14:50 2019 +0200 +++ b/CHANGES.txt Thu Apr 25 17:00:09 2019 +0200 @@ -16,6 +16,15 @@ :version: 0.7.dev1 :released: unreleased + .. change:: + :tags: breaking, feature + + The associations from filename extensions to parsers are + :py:mod:`fnmatch` style patterns now. + + Calling :py:func:`configmix.set_loader` prepends to the currently + defined associations and therefore gets the highest priority. + .. changelog:: :version: 0.6
--- a/configmix/__init__.py Thu Apr 25 16:14:50 2019 +0200 +++ b/configmix/__init__.py Thu Apr 25 17:00:09 2019 +0200 @@ -19,6 +19,7 @@ __date__ = "|VCSJustDate|" +import fnmatch import copy from .compat import u, u2fs @@ -26,7 +27,8 @@ __all__ = ["load", "safe_load", - "set_loader", "default_loaders", + "set_loader", "get_loader", + "get_default_loader", "Configuration"] @@ -92,14 +94,17 @@ return ini.load(filename) -default_loaders = { - ".yml": _load_yaml, - ".yaml": _load_yaml, - ".json": _load_json, - ".py": _load_py, - ".ini": _load_ini -} -"""The builtin default associations of extensions with loaders""" +_default_loaders = [ + ("*.yml", _load_yaml), + ("*.yaml", _load_yaml), + ("*.json", _load_json), + ("*.py", _load_py), + ("*.ini", _load_ini), +] +"""The builtin default associations of extensions with loaders -- in that +order + +""" DEFAULT_LOADER = object() @@ -108,51 +113,80 @@ To be used in :func:`set_loader`. """ -_loaders = {} +def get_default_loader(pattern): + """Return the default loader for the :mod:`fnmatch` pattern `pattern`. + + :raises: :class:`KeyError` if the `pattern` is not found. + + """ + for pat, loader in _default_loaders: + if pattern == pat: + return loader + else: + raise KeyError("No loader for pattern %r" % pattern) + + +_loaders = [] """All configured loader functions""" def clear_loader(): """Remove all configured loader associations. - The :data:`default_loaders` are **not** changed. + The :data:`_default_loaders` are **not** changed. """ - _loaders.clear() + del _loaders[:] -def set_loader(extension, loader): - """Associate a filename trailer `extension` with a callable `loader` that - will be called when :func:`load` encounters a file argument that ends - with `extension`. +def get_loader(pattern): + """Return the default loader for the :mod:`fnmatch` pattern `pattern`. + + :raises: :class:`KeyError` if the `pattern` is not found. - :param str extension: the extension to associate a loader with + """ + for pat, loader in _loaders: + if pattern == pat: + return loader + else: + raise KeyError("No loader for pattern %r" % pattern) + + +def set_loader(fnpattern, loader): + """Associate a :mod:`fnmatch` pattern `fnpattern` with a callable + `loader` that will be called when :func:`load` encounters a file + argument that matches `fnpattern`. + + :param str fnpattern: the :mod:`fnmatch` pattern to associate a loader with :param callable loader: a callable that accepts a `filename` argument and returns a parsed configuration from a given file - `extension` should be all lowercase because lookup in the loader database - is *case-insensitive*. + This function prepends to the given pattern to the currently defined + associations. + + The OS specific case-sensitivity behaviour of + :func:`fnmatch.fnmatch` apply (i.e. :func:`os.path.normpath` will + be called for both arguments). If `loader` is :data:`DEFAULT_LOADER` then the default association - from :data:`default_loaders` will be used -- if any. + from :data:`_default_loaders` will be used -- if any. """ if loader is DEFAULT_LOADER: - try: - _loaders[extension] = default_loaders[extension] - except KeyError: - raise ValueError("no DEFAULT loader for extension: %r" % extension) + for _p, _l in _default_loaders: + if _p == fnpattern: + _loaders.insert(0, (fnpattern, _l)) + break + else: + raise ValueError("no DEFAULT loader for pattern: %r" % fnpattern) else: - _loaders[extension] = loader + _loaders.insert(0, (fnpattern, loader)) def _load_cfg_from_file(filename): - fnl = filename.lower() - extensions = list(_loaders.keys()) - extensions.sort(key=lambda x: len(x), reverse=True) - for ext in extensions: - if fnl.endswith(ext): - return _loaders[ext](filename) + for _p, _l in _loaders: + if fnmatch.fnmatch(filename, _p): + return _l(filename) else: raise ValueError("Unknown configuration file type for filename " "%r" % filename) @@ -304,5 +338,5 @@ # # Init loader defaults # -for _extension in default_loaders: - set_loader(_extension, DEFAULT_LOADER) +for _pattern, _ld in _default_loaders: + set_loader(_pattern, _ld)
--- a/doc/introduction.rst Thu Apr 25 16:14:50 2019 +0200 +++ b/doc/introduction.rst Thu Apr 25 17:00:09 2019 +0200 @@ -352,12 +352,13 @@ Associate an additional new extension (e.g. ".conf") with an existing configuration file style (e.g. YAML):: - configmix.set_loader(".conf", configmix.default_loaders[".yml"]) + configmix.set_loader("*.conf", configmix.get_loader("*.yml")) - Allow only files with extension ".cfg" in INI-style:: + Allow only files with extension ".cfg" in INI-style -- using the default + loader for INI-files:: configmix.clear_loader() - configmix.set_loader(".cfg", configmix.default_loders[".ini"]) + configmix.set_loader("*.cfg", configmix.get_default_loader("*.ini")) Just a new configuration file style:: @@ -366,4 +367,4 @@ return some_dict_alike configmix.clear_loader() - configmix.set_loader(".my.configuration", my_custom_loader) + configmix.set_loader("*.my.configuration", my_custom_loader)
--- a/tests/test.py Thu Apr 25 16:14:50 2019 +0200 +++ b/tests/test.py Thu Apr 25 17:00:09 2019 +0200 @@ -312,11 +312,11 @@ def _reset(self): configmix.clear_loader() - for ext in configmix.default_loaders: - configmix.set_loader(ext, configmix.default_loaders[ext]) + for pat, loader in configmix._default_loaders: + configmix.set_loader(pat, loader) def test01_additional(self): - configmix.set_loader(".conf", configmix.default_loaders[".yml"]) + configmix.set_loader("*.conf", configmix.get_default_loader("*.yml")) cfg = configmix.load( os.path.join(TESTDATADIR, "conf1.ini"), os.path.join(TESTDATADIR, "conf30.conf")) @@ -325,7 +325,7 @@ def test02_only_style_wrong_style(self): configmix.clear_loader() - configmix.set_loader(".conf", configmix.default_loaders[".yml"]) + configmix.set_loader("*.conf", configmix.get_default_loader("*.yml")) def _ld(): return configmix.load(os.path.join(TESTDATADIR, "conf1.ini"), @@ -335,7 +335,7 @@ def test03_only_style_corrrect_style(self): configmix.clear_loader() - configmix.set_loader(".conf", configmix.default_loaders[".yml"]) + configmix.set_loader("*.conf", configmix.get_default_loader("*.yml")) cfg = configmix.load(os.path.join(TESTDATADIR, "conf30.conf")) self.assertEqual(u("new value"), cfg.getvar_s("key-new"))
