changeset 139:c87b0dc54e1d

Allow custom configuration filename extensions and custom loaders that can handle custom configuration file syntax styles
author Franz Glasner <hg@dom66.de>
date Fri, 06 Apr 2018 22:28:45 +0200
parents b883f4ef1967
children d8d47893df5b
files CHANGES.txt configmix/__init__.py doc/introduction.rst
diffstat 3 files changed, 86 insertions(+), 3 deletions(-) [+]
line wrap: on
line diff
--- a/CHANGES.txt	Fri Apr 06 09:42:17 2018 +0200
+++ b/CHANGES.txt	Fri Apr 06 22:28:45 2018 +0200
@@ -51,6 +51,12 @@
 
       Allow JSON formatted files as configuration files also (suffix ".json").
 
+   .. change::
+      :tags: feature
+
+      Allow custom configuration filename extensions and custom loaders that
+      can handle custom configuration file syntax styles.
+
 
 .. changelog::
    :version: 0.5
--- a/configmix/__init__.py	Fri Apr 06 09:42:17 2018 +0200
+++ b/configmix/__init__.py	Fri Apr 06 22:28:45 2018 +0200
@@ -23,7 +23,9 @@
 from .config import Configuration
 
 
-__all__ = ["load", "safe_load", "Configuration"]
+__all__ = ["load", "safe_load",
+           "set_loader", "default_loaders",
+           "Configuration"]
 
 
 def load(*files):
@@ -79,13 +81,55 @@
     return ini.load(filename)
 
 
-_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"""
+
+
+DEFAULT_LOADER = object()
+"""Marker for the default loader for an extension.
+
+To be used in :func:`set_loader`.
+"""
+
+_loaders = {}
+"""All configured loader functions"""
+
+
+def clear_loader():
+    """Remove all configured loader associations.
+
+    The :data:`default_loaders` are **not** changed.
+
+    """
+    _loaders.clear()
+
+
+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`.
+
+    :param str extension: the extension to associate a loader with
+    :param callable loader: a callable that accepts a `filename` argument and
+                            returns a parsed configuration from a given file
+
+    If `loader` is :data:`DEFAULT_LOADER` then the default association
+    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)
+    else:
+        _loaders[extension] = loader
 
 
 def _load_cfg_from_file(filename):
@@ -194,3 +238,10 @@
             else:
                 user[k] = _safe_merge(user[k], v)
     return user
+
+
+#
+# Init loader defaults
+#
+for _extension in default_loaders:
+    set_loader(_extension, DEFAULT_LOADER)
--- a/doc/introduction.rst	Fri Apr 06 09:42:17 2018 +0200
+++ b/doc/introduction.rst	Fri Apr 06 22:28:45 2018 +0200
@@ -156,7 +156,7 @@
     value2 = config.getvar_s("tree1.tree2.key4")
 
 
-The filenames of the configuration files must have the extensions
+By default filenames of the configuration files must have the extensions
 (case-insensitively):
 
   ``.py``
@@ -325,3 +325,29 @@
 
 expands to something like ``CPYTHON`` when using the standard Python
 interpreter written in C.
+
+
+Custom filename extensions and custom loaders
+---------------------------------------------
+
+If you want to have custom configuration file extensions and/or custom loaders
+for custom configuration files you have various possibilities:
+
+  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"])
+
+  Allow only files with extension ".cfg" in INI-style::
+
+    configmix.clear_loader()
+    configmix.set_loader(".cfg", configmix.default_loders[".ini"])
+
+  Just a new configuration file style::
+
+    def my_custom_loader(filename):
+        ...
+        return some_dict_alike
+
+    configmix.clear_loader()
+    configmix.set_loader(".my.configuration", my_custom_loader)