Mercurial > hgrepos > Python > libs > ConfigMix
comparison configmix/__init__.py @ 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 |
comparison
equal
deleted
inserted
replaced
| 170:c247a5dc35ed | 171:1ff11462a5c1 |
|---|---|
| 17 | 17 |
| 18 __revision__ = "|VCSRevision|" | 18 __revision__ = "|VCSRevision|" |
| 19 __date__ = "|VCSJustDate|" | 19 __date__ = "|VCSJustDate|" |
| 20 | 20 |
| 21 | 21 |
| 22 import fnmatch | |
| 22 import copy | 23 import copy |
| 23 | 24 |
| 24 from .compat import u, u2fs | 25 from .compat import u, u2fs |
| 25 from .config import Configuration | 26 from .config import Configuration |
| 26 | 27 |
| 27 | 28 |
| 28 __all__ = ["load", "safe_load", | 29 __all__ = ["load", "safe_load", |
| 29 "set_loader", "default_loaders", | 30 "set_loader", "get_loader", |
| 31 "get_default_loader", | |
| 30 "Configuration"] | 32 "Configuration"] |
| 31 | 33 |
| 32 | 34 |
| 33 COMMENTS = [u("__comment"), | 35 COMMENTS = [u("__comment"), |
| 34 u("__doc"), | 36 u("__doc"), |
| 90 def _load_ini(filename): | 92 def _load_ini(filename): |
| 91 from . import ini | 93 from . import ini |
| 92 return ini.load(filename) | 94 return ini.load(filename) |
| 93 | 95 |
| 94 | 96 |
| 95 default_loaders = { | 97 _default_loaders = [ |
| 96 ".yml": _load_yaml, | 98 ("*.yml", _load_yaml), |
| 97 ".yaml": _load_yaml, | 99 ("*.yaml", _load_yaml), |
| 98 ".json": _load_json, | 100 ("*.json", _load_json), |
| 99 ".py": _load_py, | 101 ("*.py", _load_py), |
| 100 ".ini": _load_ini | 102 ("*.ini", _load_ini), |
| 101 } | 103 ] |
| 102 """The builtin default associations of extensions with loaders""" | 104 """The builtin default associations of extensions with loaders -- in that |
| 105 order | |
| 106 | |
| 107 """ | |
| 103 | 108 |
| 104 | 109 |
| 105 DEFAULT_LOADER = object() | 110 DEFAULT_LOADER = object() |
| 106 """Marker for the default loader for an extension. | 111 """Marker for the default loader for an extension. |
| 107 | 112 |
| 108 To be used in :func:`set_loader`. | 113 To be used in :func:`set_loader`. |
| 109 """ | 114 """ |
| 110 | 115 |
| 111 _loaders = {} | 116 def get_default_loader(pattern): |
| 117 """Return the default loader for the :mod:`fnmatch` pattern `pattern`. | |
| 118 | |
| 119 :raises: :class:`KeyError` if the `pattern` is not found. | |
| 120 | |
| 121 """ | |
| 122 for pat, loader in _default_loaders: | |
| 123 if pattern == pat: | |
| 124 return loader | |
| 125 else: | |
| 126 raise KeyError("No loader for pattern %r" % pattern) | |
| 127 | |
| 128 | |
| 129 _loaders = [] | |
| 112 """All configured loader functions""" | 130 """All configured loader functions""" |
| 113 | 131 |
| 114 | 132 |
| 115 def clear_loader(): | 133 def clear_loader(): |
| 116 """Remove all configured loader associations. | 134 """Remove all configured loader associations. |
| 117 | 135 |
| 118 The :data:`default_loaders` are **not** changed. | 136 The :data:`_default_loaders` are **not** changed. |
| 119 | 137 |
| 120 """ | 138 """ |
| 121 _loaders.clear() | 139 del _loaders[:] |
| 122 | 140 |
| 123 | 141 |
| 124 def set_loader(extension, loader): | 142 def get_loader(pattern): |
| 125 """Associate a filename trailer `extension` with a callable `loader` that | 143 """Return the default loader for the :mod:`fnmatch` pattern `pattern`. |
| 126 will be called when :func:`load` encounters a file argument that ends | 144 |
| 127 with `extension`. | 145 :raises: :class:`KeyError` if the `pattern` is not found. |
| 128 | 146 |
| 129 :param str extension: the extension to associate a loader with | 147 """ |
| 148 for pat, loader in _loaders: | |
| 149 if pattern == pat: | |
| 150 return loader | |
| 151 else: | |
| 152 raise KeyError("No loader for pattern %r" % pattern) | |
| 153 | |
| 154 | |
| 155 def set_loader(fnpattern, loader): | |
| 156 """Associate a :mod:`fnmatch` pattern `fnpattern` with a callable | |
| 157 `loader` that will be called when :func:`load` encounters a file | |
| 158 argument that matches `fnpattern`. | |
| 159 | |
| 160 :param str fnpattern: the :mod:`fnmatch` pattern to associate a loader with | |
| 130 :param callable loader: a callable that accepts a `filename` argument and | 161 :param callable loader: a callable that accepts a `filename` argument and |
| 131 returns a parsed configuration from a given file | 162 returns a parsed configuration from a given file |
| 132 | 163 |
| 133 `extension` should be all lowercase because lookup in the loader database | 164 This function prepends to the given pattern to the currently defined |
| 134 is *case-insensitive*. | 165 associations. |
| 166 | |
| 167 The OS specific case-sensitivity behaviour of | |
| 168 :func:`fnmatch.fnmatch` apply (i.e. :func:`os.path.normpath` will | |
| 169 be called for both arguments). | |
| 135 | 170 |
| 136 If `loader` is :data:`DEFAULT_LOADER` then the default association | 171 If `loader` is :data:`DEFAULT_LOADER` then the default association |
| 137 from :data:`default_loaders` will be used -- if any. | 172 from :data:`_default_loaders` will be used -- if any. |
| 138 | 173 |
| 139 """ | 174 """ |
| 140 if loader is DEFAULT_LOADER: | 175 if loader is DEFAULT_LOADER: |
| 141 try: | 176 for _p, _l in _default_loaders: |
| 142 _loaders[extension] = default_loaders[extension] | 177 if _p == fnpattern: |
| 143 except KeyError: | 178 _loaders.insert(0, (fnpattern, _l)) |
| 144 raise ValueError("no DEFAULT loader for extension: %r" % extension) | 179 break |
| 145 else: | 180 else: |
| 146 _loaders[extension] = loader | 181 raise ValueError("no DEFAULT loader for pattern: %r" % fnpattern) |
| 182 else: | |
| 183 _loaders.insert(0, (fnpattern, loader)) | |
| 147 | 184 |
| 148 | 185 |
| 149 def _load_cfg_from_file(filename): | 186 def _load_cfg_from_file(filename): |
| 150 fnl = filename.lower() | 187 for _p, _l in _loaders: |
| 151 extensions = list(_loaders.keys()) | 188 if fnmatch.fnmatch(filename, _p): |
| 152 extensions.sort(key=lambda x: len(x), reverse=True) | 189 return _l(filename) |
| 153 for ext in extensions: | |
| 154 if fnl.endswith(ext): | |
| 155 return _loaders[ext](filename) | |
| 156 else: | 190 else: |
| 157 raise ValueError("Unknown configuration file type for filename " | 191 raise ValueError("Unknown configuration file type for filename " |
| 158 "%r" % filename) | 192 "%r" % filename) |
| 159 | 193 |
| 160 | 194 |
| 302 | 336 |
| 303 | 337 |
| 304 # | 338 # |
| 305 # Init loader defaults | 339 # Init loader defaults |
| 306 # | 340 # |
| 307 for _extension in default_loaders: | 341 for _pattern, _ld in _default_loaders: |
| 308 set_loader(_extension, DEFAULT_LOADER) | 342 set_loader(_pattern, _ld) |
