view tests/test.py @ 428:090a25f36a3d

FIX: Allow jailed configurations to use correctly use base configurations that use a different "default" marker object. Jailed configurations assumed that their "default" marker object is identical to the "default" marker object in the unjailed base configuration. This is not always true, especially if "_JailedConfiguration.rebind()" is used. Removed the explicit "default" keyword argument and passed the complete keywords argument dictionary to the base instead. This triggers correct default handling in the base.
author Franz Glasner <f.glasner@feldmann-mg.com>
date Thu, 09 Dec 2021 13:02:17 +0100
parents 40be1d25ff1c
children b96f49c9c76b
line wrap: on
line source

# -*- coding: utf-8 -*-

import sys
import os
import unittest
import platform
import io

sys.path.insert(
    0,
    os.path.abspath(
        os.path.normpath(os.path.join(os.path.dirname(__file__), ".."))))

import configmix
import configmix.ini
import configmix.yaml
import configmix.json
import configmix.py
import configmix.toml
from configmix.compat import u


TESTDATADIR = os.path.join(
    os.path.abspath(os.path.dirname(__file__)),
    "data")


class T01Basic(unittest.TestCase):

    """Check with low-level internal interfaces"""

    def __check_types(self, cfg):
        self.assertEqual(u("the value"),
                         cfg.get("key1"))
        self.assertTrue(isinstance(cfg.get("key1"), type(u(''))))
        self.assertEqual(2, cfg.get("key2"))
        self.assertEqual(5.7, cfg.get("key3"))
        self.assertTrue(isinstance(cfg.get("key1"), type(u(''))))
        self.assertTrue(cfg.get("key4"))
        self.assertTrue(isinstance(cfg.get("key4"), bool))
        self.assertFalse(cfg.get("key5"))
        self.assertTrue(isinstance(cfg.get("key5"), bool))
        self.assertEqual(255, cfg.get("key6"))
        self.assertEqual(u("Umlaute: ÄÖÜäöüß"),
                         cfg.get("key7"))

    def __check_comment(self, cfg):
        # Check comments: low level comments are *not* filtered
        self.assertEqual(u("Comment 1"), cfg.get("__comment1"))
        self.assertEqual(u("Comment no 2"), cfg.get("__comment2"))

    def __check_no_comment(self, cfg):

        def _c(name):
            def _f():
                cfg[u(name)]
            return _f

        # Variables with leading underscores are *not*  imported by default
        self.assertRaises(KeyError, _c("__comment1"))
        self.assertRaises(KeyError, _c("__comment2"))

    def __check_tree(self, cfg):
        self.assertEqual(u("in the root namespace"),
                         cfg.get("key1"))
        self.assertEqual(u("in the root namespace -- too"),
                         cfg.get("key2"))
        self.assertEqual(32,
                         cfg["tree1"]["key3"])
        self.assertEqual(u("get this as `tree1.tree2.key4'"),
                         cfg["tree1"]["tree2"]["key4"])
        self.assertTrue(cfg["tree1"]["tree2"]["key5"])

    def test01_ini_types(self):
        cfg = configmix.ini.load(os.path.join(TESTDATADIR, "conf1.ini"))
        self.__check_types(cfg)
        self.__check_comment(cfg)

    def test01_toml_types(self):
        cfg = configmix.toml.load(os.path.join(TESTDATADIR, "conf1.toml"))
        self.__check_types(cfg)
        self.__check_comment(cfg)

    def test02_py_types(self):
        cfg = configmix.py.load(os.path.join(TESTDATADIR, "conf1.py"))
        self.__check_types(cfg)
        self.__check_no_comment(cfg)

    def test03_yaml_types(self):
        with io.open(os.path.join(TESTDATADIR, "conf1.yml"), "rt",
                     encoding="utf-8") as f:
            cfg = configmix.yaml.safe_load(f)
            if configmix.yaml.OrderedDict:
                self.assertTrue(isinstance(cfg, configmix.yaml.OrderedDict))
            self.__check_types(cfg)
            self.__check_comment(cfg)

    def test04_json_types(self):
        cfg = configmix.json.load(os.path.join(TESTDATADIR, "conf1.json"))
        self.assertTrue(isinstance(cfg, configmix.json.DictImpl))
        self.__check_types(cfg)
        self.__check_comment(cfg)

    def test05_py_export_all(self):
        # When __all__ is given only it's keys are exported
        cfg = configmix.py.load(os.path.join(TESTDATADIR, "conf2.py"))
        self.assertEqual(u("the next value"), cfg.get("key1"))
        self.assertTrue(isinstance(cfg.get("key1"), type(u(''))))
        self.assertTrue(cfg.get("key2") is None)

    def test06_py_hide_private(self):
        # When no __all__ is given all symbols with leading "_" are hidden
        cfg = configmix.py.load(os.path.join(TESTDATADIR, "conf3.py"))
        self.assertEqual(u("the next value "), cfg.get("key1"))
        self.assertTrue(isinstance(cfg.get("key1"), type(u(''))))
        self.assertTrue(cfg.get("_key2") is None)

    def test07_ini_tree(self):
        cfg = configmix.ini.load(os.path.join(TESTDATADIR, "conf10.ini"))
        self.__check_tree(cfg)

    def test08_py_tree(self):
        cfg = configmix.py.load(os.path.join(TESTDATADIR, "conf10.py"))
        self.__check_tree(cfg)

    def test09_yaml_tree(self):
        with io.open(os.path.join(TESTDATADIR, "conf10.yml"), "rt",
                     encoding="utf-8") as f:
            cfg = configmix.yaml.safe_load(f)
            self.__check_tree(cfg)

    def test10_json_tree(self):
        cfg = configmix.json.load(os.path.join(TESTDATADIR, "conf10.json"))
        self.__check_tree(cfg)

    def test11_toml_tree(self):
        cfg = configmix.toml.load(os.path.join(TESTDATADIR, "conf10.toml"))
        self.__check_tree(cfg)

    def test12_yaml_no_duplicate_keys(self):
        import yaml.constructor as yc
        with io.open(os.path.join(TESTDATADIR, "duplicate-keys.yml"), "rt",
                     encoding="utf-8") as f:
            cfg = configmix.yaml.safe_load(f)

        with io.open(os.path.join(TESTDATADIR, "duplicate-keys.yml"), "rt",
                     encoding="utf-8") as f:
            self.assertRaises(yc.ConstructorError,
                              configmix.yaml.safe_load,
                              f, strict=True)

    def test13_yaml_no_duplicate_keys_2(self):
        import yaml.constructor as yc
        with io.open(os.path.join(TESTDATADIR, "duplicate-keys-2.yml"), "rt",
                     encoding="utf-8") as f:
            cfg = configmix.yaml.safe_load(f)

        with io.open(os.path.join(TESTDATADIR, "duplicate-keys-2.yml"), "rt",
                     encoding="utf-8") as f:
            self.assertRaises(yc.ConstructorError,
                              configmix.yaml.safe_load,
                              f, strict=True)


class _T02MixinLoadAndMerge:

    def test01_load(self):
        cfg = self._load(
            os.path.join(TESTDATADIR, "conf20.yml"),
            os.path.join(TESTDATADIR, "conf21.yml"))

        self.assertEqual(u("the_database_user"),
                         cfg.getvar_s("db.user.name"))
        self.assertEqual(u("the-database-password"),
                         cfg.getvar_s("db.user.pwd"))

        tmpdir = cfg.getvar_s("tmpdir")
        if os.name == 'nt':
            self.assertFalse(u('/') in tmpdir)
        self.assertEqual(os.path.normpath(
            os.path.abspath(os.path.join(os.getcwd(), "tmp"))),
                         tmpdir)

        self.assertEqual(u("anotherhost"),
                         cfg.getvar_s("db.locinfo.ro.hostname"))
        self.assertEqual(u("localhost"),
                         cfg.getvar_s("db.locinfo.rw.hostname"))

        self.assertEqual(5432, cfg.getvar_s("db.locinfo.ro.port"))

        url = cfg.getvar_s("db.engines.ro.url")
        self.assertEqual(
            u("postgresql+psycopg2://the_database_user:the-database-password@anotherhost:5432/my_database_catalog"),
            url)

        self.assertEqual(u("not a list any more"),
                         cfg.getvar_s("test.List"))

        self.assertEqual(list(range(0, 3)),
                         cfg.getvar_s("test.Str"))

    def test02_load_with_ini(self):
        cfg = self._load(
            os.path.join(TESTDATADIR, "conf20.yml"),
            os.path.join(TESTDATADIR, "conf21.yml"),
            os.path.join(TESTDATADIR, "conf22.ini"))

        self.assertEqual(u("the_database_user_2"),
                         cfg.getvar_s("db.user.name"))
        self.assertEqual(u("the-database-password-2"),
                         cfg.getvar_s("db.user.pwd"))

        tmpdir = cfg.getvar_s("tmpdir")
        self.assertEqual(u(os.getcwd()) + u("/tmp\\2"), tmpdir)

        self.assertEqual(u("3rd-host"),
                         cfg.getvar_s("db.locinfo.ro.hostname"))
        self.assertEqual(u("localhost"),
                         cfg.getvar_s("db.locinfo.rw.hostname"))

        self.assertEqual(5432, cfg.getvar_s("db.locinfo.ro.port"))

        url = cfg.getvar_s("db.engines.ro.url")
        self.assertEqual(
            u("postgresql+psycopg2://the_database_user_2:the-database-password-2@3rd-host:5432/my_database_catalog"),
            url)

    def test02_load_with_json(self):
        cfg = self._load(
            os.path.join(TESTDATADIR, "conf20.yml"),
            os.path.join(TESTDATADIR, "conf21.yml"),
            # .ini replaced with an equivalent .json
            os.path.join(TESTDATADIR, "conf23.json"))

        self.assertEqual(u("the_database_user_2"),
                         cfg.getvar_s("db.user.name"))
        self.assertEqual(u("the-database-password-2"),
                         cfg.getvar_s("db.user.pwd"))

        tmpdir = cfg.getvar_s("tmpdir")
        self.assertEqual(u(os.getcwd()) + u("/tmp\\3"), tmpdir)

        self.assertEqual(u("3rd-host"),
                         cfg.getvar_s("db.locinfo.ro.hostname"))
        self.assertEqual(u("localhost"),
                         cfg.getvar_s("db.locinfo.rw.hostname"))

        self.assertEqual(5432, cfg.getvar_s("db.locinfo.ro.port"))

        url = cfg.getvar_s("db.engines.ro.url")
        self.assertEqual(
            u("postgresql+psycopg2://the_database_user_2:the-database-password-2@3rd-host:5432/my_database_catalog"),
            url)

    def test02_load_with_toml(self):
        cfg = self._load(
            os.path.join(TESTDATADIR, "conf20.yml"),
            os.path.join(TESTDATADIR, "conf21.yml"),
            # .ini replaced with an equivalent .toml
            os.path.join(TESTDATADIR, "conf24.toml"))

        self.assertEqual(u("the_database_user_2"),
                         cfg.getvar_s("db.user.name"))
        self.assertEqual(u("the-database-password-2"),
                         cfg.getvar_s("db.user.pwd"))

        tmpdir = cfg.getvar_s("tmpdir")
        self.assertEqual(u(os.getcwd()) + u("/tmp\\4"), tmpdir)

        self.assertEqual(u("3rd-host"),
                         cfg.getvar_s("db.locinfo.ro.hostname"))
        self.assertEqual(u("localhost"),
                         cfg.getvar_s("db.locinfo.rw.hostname"))

        self.assertEqual(5432, cfg.getvar_s("db.locinfo.ro.port"))

        url = cfg.getvar_s("db.engines.ro.url")
        self.assertEqual(
            u("postgresql+psycopg2://the_database_user_2:the-database-password-2@3rd-host:5432/my_database_catalog"),
            url)

    def test03_namespace(self):
        cfg = self._load(
            os.path.join(TESTDATADIR, "conf20.yml"),
            os.path.join(TESTDATADIR, "conf21.yml"),
            os.path.join(TESTDATADIR, "conf22.ini"))
        self.assertEqual(u(os.getcwd()), cfg.getvar("OS:cwd"))
        self.assertEqual(u(platform.python_version()),
                         cfg.getvar_s("PY:version"))

    def test03_namespace_l(self):
        cfg = self._load(
            os.path.join(TESTDATADIR, "conf20.yml"),
            os.path.join(TESTDATADIR, "conf21.yml"),
            os.path.join(TESTDATADIR, "conf22.ini"))
        self.assertEqual(u(os.getcwd()), cfg.getvarl("cwd", namespace="OS"))
        self.assertEqual(u(platform.python_version()),
                         cfg.getvarl_s("version", namespace="PY"))

    def test04_no_filter(self):
        cfg = self._load(
            os.path.join(TESTDATADIR, "conf20.yml"),
            os.path.join(TESTDATADIR, "conf21.yml"),
            os.path.join(TESTDATADIR, "conf22.ini"))

        def _look():
            return cfg.getvar("OS:cwd|upper")

        self.assertRaises(KeyError, _look)

    def test04_no_filter_l(self):
        cfg = self._load(
            os.path.join(TESTDATADIR, "conf20.yml"),
            os.path.join(TESTDATADIR, "conf21.yml"),
            os.path.join(TESTDATADIR, "conf22.ini"))

        def _look():
            return cfg.getvarl("cwd|upper", namespace="OS")

        self.assertRaises(KeyError, _look)

    def test05_comments(self):
        cfg = self._load(os.path.join(TESTDATADIR, "conf20.yml"),
                         os.path.join(TESTDATADIR, "conf21.yml"),
                         os.path.join(TESTDATADIR, "conf22.ini"),
                         os.path.join(TESTDATADIR, "conf23.json"),
                         os.path.join(TESTDATADIR, "conf24.toml"))

        def _c(name):
            def _f():
                cfg.getvar_s(name)
            return _f

        # Variables with leading underscores are *not*  imported by default
        self.assertEqual(0o0027, int(cfg.getvar_s("process.umask"), 0))
        self.assertRaises(KeyError, _c("process.__doc1"))
        self.assertRaises(KeyError, _c("db.__comment1"))
        self.assertRaises(KeyError, _c("db.user.__doc2"))

    def test05_comments_l(self):
        cfg = self._load(os.path.join(TESTDATADIR, "conf20.yml"),
                         os.path.join(TESTDATADIR, "conf21.yml"),
                         os.path.join(TESTDATADIR, "conf22.ini"),
                         os.path.join(TESTDATADIR, "conf23.json"),
                         os.path.join(TESTDATADIR, "conf24.toml"))

        def _c(*names):
            def _f():
                cfg.getvarl_s(*names)
            return _f

        # Variables with leading underscores are *not*  imported by default
        self.assertEqual(0o0027, int(cfg.getvarl_s("process", "umask"), 0))
        self.assertRaises(KeyError, _c("process", "__doc1"))
        self.assertRaises(KeyError, _c("db", "__comment1"))
        self.assertRaises(KeyError, _c("db", "user", "__doc2"))

    def test06_check_all_comments(self):
        cfg = self._load(os.path.join(TESTDATADIR, "conf20.yml"),
                         os.path.join(TESTDATADIR, "conf21.yml"),
                         os.path.join(TESTDATADIR, "conf22.ini"),
                         os.path.join(TESTDATADIR, "conf23.json"),
                         os.path.join(TESTDATADIR, "conf24.toml"))

        def _check(d):
            for k, v in d.items():
                self.assertFalse(configmix._is_comment(k))
                if isinstance(v, dict):
                    _check(v)

        _check(cfg)

    def test07_deletions(self):
        cfg = self._load(os.path.join(TESTDATADIR, "conf20.yml"),
                         os.path.join(TESTDATADIR, "conf21.yml"),
                         os.path.join(TESTDATADIR, "conf22.ini"),
                         os.path.join(TESTDATADIR, "conf23.json"),
                         os.path.join(TESTDATADIR, "conf24.toml"),
                         os.path.join(TESTDATADIR, "delete-in-dict.yml"))
        # automatic clean-up
        self.assertRaises(KeyError, cfg.getvar_s, "not-deleted")
        # explicit deletion
        self.assertRaises(KeyError, cfg.getvar_s, "to-be-deleted")
        self.assertRaises(KeyError, cfg.getvar_s, "db.user.name")
        self.assertEqual("the-database-password-2", cfg.getvar_s("db.user.pwd"))
        self.assertRaises(KeyError, cfg.getvar_s, "test.Str")
        self.assertEqual("not a list any more", cfg.getvar_s("test.List"))
        self.assertEqual("the last value",
                         cfg.getvar_s("to-be-deleted-but-reassigned"))

    def test07_deletions_l(self):
        cfg = self._load(os.path.join(TESTDATADIR, "conf20.yml"),
                         os.path.join(TESTDATADIR, "conf21.yml"),
                         os.path.join(TESTDATADIR, "conf22.ini"),
                         os.path.join(TESTDATADIR, "conf23.json"),
                         os.path.join(TESTDATADIR, "conf24.toml"),
                         os.path.join(TESTDATADIR, "delete-in-dict.yml"))
        # automatic clean-up
        self.assertRaises(KeyError, cfg.getvarl_s, "not-deleted")
        # explicit deletion
        self.assertRaises(KeyError, cfg.getvarl_s, "to-be-deleted")
        self.assertRaises(KeyError, cfg.getvarl_s, "db" "user.name")
        self.assertEqual("the-database-password-2",
                         cfg.getvarl_s("db", "user", "pwd"))
        self.assertRaises(KeyError, cfg.getvarl_s, "test", "Str")
        self.assertEqual("not a list any more", cfg.getvarl_s("test", "List"))
        self.assertEqual("the last value",
                         cfg.getvarl_s("to-be-deleted-but-reassigned"))

    def test08_None_filter_single(self):
        cfg = self._load()
        x = cfg.expand_variable("{{non-existing|None}}")
        self.assertIsNone(x)

    def test09_None_filter_embedded(self):
        cfg = self._load()
        x = cfg.expand_variable("A{{non-existing|None}}Z")
        self.assertEqual("AZ", x)

    def test10_Empty_filtersingle(self):
        cfg = self._load()
        x = cfg.expand_variable("{{non-existing|Empty}}")
        self.assertEqual("", x)

    def test11_None_filter_pass_through(self):
        cfg = self._load(os.path.join(TESTDATADIR, "conf20.yml"),
                         os.path.join(TESTDATADIR, "conf21.yml"),
                         os.path.join(TESTDATADIR, "conf22.ini"),
                         os.path.join(TESTDATADIR, "conf23.json"),
                         os.path.join(TESTDATADIR, "conf24.toml"))
        x = cfg.expand_variable("{{intl.cache.items|None}}")
        self.assertEqual(10, x)

    def test12_Empty_filter_pass_through(self):
        cfg = self._load(os.path.join(TESTDATADIR, "conf20.yml"),
                         os.path.join(TESTDATADIR, "conf21.yml"),
                         os.path.join(TESTDATADIR, "conf22.ini"),
                         os.path.join(TESTDATADIR, "conf23.json"),
                         os.path.join(TESTDATADIR, "conf24.toml"))
        x = cfg.expand_variable("{{intl.cache.items|Empty}}")
        self.assertEqual(10, x)

    def test13_keyerror(self):
        cfg = self._load(os.path.join(TESTDATADIR, "conf20.yml"))
        self.assertRaises(KeyError, cfg.getvar_s, "non.existing.key")

    def test14_getvar_with_default(self):
        cfg = self._load(os.path.join(TESTDATADIR, "conf20.yml"))
        self.assertEqual("999", cfg.getvar("non.existing.key", default="999"))

    def test15_getvar_s_with_default(self):
        cfg = self._load(os.path.join(TESTDATADIR, "conf20.yml"))
        self.assertEqual("999", cfg.getvar_s("non.existing.key",
                                             default="999"))

    def test16_getintvar_s_with_default(self):
        cfg = self._load(os.path.join(TESTDATADIR, "conf20.yml"))
        self.assertEqual(9999, cfg.getintvar_s("non.existing.key",
                                               default=9999))
    def test17_getintvar_s_with_default(self):
        cfg = self._load(os.path.join(TESTDATADIR, "conf20.yml"))
        self.assertFalse(cfg.getboolvar_s("non.existing.key",
                                          default=u('false')))

    def test18_getfirstvar_nonexisting(self):
        cfg = self._load(os.path.join(TESTDATADIR, "conf20.yml"))
        self.assertRaises(
            KeyError,
            cfg.getfirstvar,
            "db.non.existing.key",
            "db.non.existing.key2")

    def test19_getfirstvar_nonexisting_default(self):
        cfg = self._load(os.path.join(TESTDATADIR, "conf20.yml"))
        self.assertIsNone(cfg.getfirstvar("db.non.existing.key",
                                          "db.non.existing.key2",
                                          "intl.non.existing",
                                          default=None))

    def test20_getfirstvar_existing(self):
        cfg = self._load(os.path.join(TESTDATADIR, "conf20.yml"))
        self.assertEqual("test-configmix", cfg.getfirstvar("intl.domain"))
        self.assertEqual("test-configmix", cfg.getfirstvar("intl.domain",
                                                           "intl.fallback"))
        self.assertEqual("de", cfg.getfirstvar("intl.fallback",
                                               "intl.domain",
                                               default=None))

        self.assertEqual("de", cfg.getfirstvar("intl.non.existing",
                                               "intl.fallback",
                                               default=None))

    def test21_getfirstvar_s_existing(self):
        cfg = self._load(
            os.path.join(TESTDATADIR, "conf20.yml"),
            os.path.join(TESTDATADIR, "conf21.yml"))
        self.assertEqual(
            os.getcwd()+"/locale",
            cfg.getfirstvar_s("intl.non.existing", "intl.localedir"))
        self.assertEqual(
            os.getcwd()+"/locale",
            cfg.getfirstvar_s("intl.localedir", "intl.non.existing"))

    def test22_getfirstvar_s_non_existing(self):
        cfg = self._load(
            os.path.join(TESTDATADIR, "conf20.yml"),
            os.path.join(TESTDATADIR, "conf21.yml"))
        self.assertIsNone(
            cfg.getfirstvar_s("intl.non.existing", "intl.non.existing2",
                              default=None))
        self.assertRaises(
            KeyError,
            cfg.getfirstvar_s,
            "intl.non.existing",
            "intl.non.existing2")

    def test23_getfirstintvar_s_nonexisting(self):
        cfg = self._load(os.path.join(TESTDATADIR, "conf20.yml"))
        self.assertIsNone(cfg.getfirstintvar_s("db.non.existing.key",
                                               "db.non.existing.key2",
                                               "intl.non.existing",
                                               default=None))
        self.assertRaises(
            KeyError,
            cfg.getfirstintvar_s,
            "db.non.existing.key",
            "db.non.exksting.key2",
            "intl.non.existing")

    def test23_getfirstintvar_s_nonexisting(self):
        cfg = self._load(os.path.join(TESTDATADIR, "conf20.yml"))
        self.assertEqual(20,
                         cfg.getfirstintvar_s("db.non.existing.key",
                                              "db.non.existing.key2",
                                              "intl.non.existing",
                                              default=u("20")))
        self.assertEqual(30,
                         cfg.getfirstintvar_s("db.non.existing.key",
                                              "db.non.existing.key2",
                                              "intl.non.existing",
                                              default=30))
        self.assertRaises(
            KeyError,
            cfg.getfirstintvar_s,
            "db.non.existing.key",
            "db.non.exksting.key2",
            "intl.non.existing")

    def test24_getfirstintvar_s_existing(self):
        cfg = self._load(os.path.join(TESTDATADIR, "conf20.yml"))
        self.assertEqual(10,
                         cfg.getfirstintvar_s("db.non.existing.key",
                                              "intl.cache.items",
                                              "db.non.existing.key2",
                                              default=u("20")))

    def test25_getfirstboolvar_s_nonexisting(self):
        cfg = self._load(os.path.join(TESTDATADIR, "conf20.yml"))
        self.assertFalse(
            cfg.getfirstboolvar_s("db.non.existing.key",
                                  "db.non.existing.key2",
                                  "db.engines.rw.no-echo",
                                  default=u("false")))
        self.assertTrue(
            cfg.getfirstboolvar_s("db.non.existing.key",
                                  "db.non.existing.key2",
                                  "db.engines.rw.no-echo",
                                  default=True))
        self.assertRaises(
            KeyError,
            cfg.getfirstboolvar_s,
            "db.non.existing.key",
            "db.non.exksting.key2",
            "b.engines.rw.no-echo")

    def test26_getfirstboolvar_s_existing(self):
        cfg = self._load(os.path.join(TESTDATADIR, "conf20.yml"))
        self.assertFalse(
            cfg.getfirstboolvar_s("db.non.existing.key",
                                  "db.engines.rw.echo",
                                  "db.non.existing.key2",
                                  default=u("true")))

    def test27_getfirstvarl_nonexisting(self):
        cfg = self._load(os.path.join(TESTDATADIR, "conf20.yml"))
        self.assertRaises(
            KeyError,
            cfg.getfirstvarl,
            [["db", "non", "existing", "key"],
             ("db", "non", "existing", "key2")])

    def test27b_getfirstvarl_nonexisting(self):
        cfg = self._load(os.path.join(TESTDATADIR, "conf20.yml"))
        self.assertRaises(
            KeyError,
            cfg.getfirstvarl,
            [{"namespace": None, "path": ["db", "non", "existing", "key"]},
             {"namespace": None, "path": ["db", "non", "existing", "key2"]}])

    def test28_getfirstvarl_nonexisting(self):
        cfg = self._load(os.path.join(TESTDATADIR, "conf20.yml"))
        self.assertIsNone(cfg.getfirstvarl(
            [["db", "non", "existing", "key"],
             ("db", "non", "existing", "key2")],
            default=None))

    def test28b_getfirstvarl_nonexisting(self):
        cfg = self._load(os.path.join(TESTDATADIR, "conf20.yml"))
        self.assertIsNone(cfg.getfirstvarl(
            [{"namespace": None, "path": ["db", "non", "existing", "key"]},
             {"namespace": None, "path": ("db", "non", "existing", "key2")}],
            default=None))

    def test29_getfirstvarl_existing(self):
        cfg = self._load(os.path.join(TESTDATADIR, "conf20.yml"))
        self.assertEqual(
            "test-configmix",
            cfg.getfirstvarl(*(("intl", "domain"),)))
        self.assertEqual(
            "test-configmix",
            cfg.getfirstvarl(*(("intl", "domain"), ("intl", "fallback"))))
        self.assertEqual(
            "de",
            cfg.getfirstvarl(*[["intl", "fallback"],
                               ["intl", "domain"]],
                               default=None))
        self.assertEqual(
            "de",
            cfg.getfirstvarl(*[["intl", "non", "existing"],
                               ["intl", "fallback"]],
                               default=None))

    def test29b_getfirstvarl_existing(self):
        cfg = self._load(os.path.join(TESTDATADIR, "conf20.yml"))
        self.assertEqual(
            "test-configmix",
            cfg.getfirstvarl(*({"namespace": None,
                                "path": ("intl", "domain")},)))
        self.assertEqual(
            "test-configmix",
            cfg.getfirstvarl(*({"namespace": None,
                                "path": ("intl", "domain")},
                               {"namespace": None, "path": ("intl", "fallback")})))
        self.assertEqual(
            "de",
            cfg.getfirstvarl(*[{"namespace": None, "path": ["intl", "fallback"]},
                               {"namespace": None, "path": ["intl", "domain"]}],
                               default=None))
        self.assertEqual(
            "de",
            cfg.getfirstvarl(*[{"namespace": None, "path": ["intl", "non", "existing"]},
                               {"namespace": None, "path": ["intl", "fallback"]}],
                               default=None))

    def test30_getfirstvarl_s_existing(self):
        cfg = self._load(
            os.path.join(TESTDATADIR, "conf20.yml"),
            os.path.join(TESTDATADIR, "conf21.yml"))
        self.assertEqual(
            os.getcwd()+"/locale",
            cfg.getfirstvarl_s(*[["intl", "non", "existing"],
                                 ["intl", "localedir"]]))
        self.assertEqual(
            os.getcwd()+"/locale",
            cfg.getfirstvarl_s(*[["intl", "localedir"], ["intl", "non", "existing"]]))

    def test30b_getfirstvarl_s_existing(self):
        cfg = self._load(
            os.path.join(TESTDATADIR, "conf20.yml"),
            os.path.join(TESTDATADIR, "conf21.yml"))
        self.assertEqual(
            os.getcwd()+"/locale",
            cfg.getfirstvarl_s(*[{"namespace": None, "path": ["intl", "non", "existing"]},
                                 {"namespace": None, "path": ["intl", "localedir"]}]))
        self.assertEqual(
            os.getcwd()+"/locale",
            cfg.getfirstvarl_s(*[{"namespace": None, "path": ["intl", "localedir"]}, {"namespace": None, "path": ["intl", "non", "existing"]}]))

    def test31_getfirstvar_s_non_existing(self):
        cfg = self._load(
            os.path.join(TESTDATADIR, "conf20.yml"),
            os.path.join(TESTDATADIR, "conf21.yml"))
        self.assertIsNone(
            cfg.getfirstvarl_s(
                *[["intl", "non", "existing"], ["intl", "non", "existing2"]],
                default=None))
        self.assertRaises(
            KeyError,
            cfg.getfirstvarl_s,
            ["intl" ,"non", "existing"],
            ["intl", "non", "existing2"])

    def test31b_getfirstvar_s_non_existing(self):
        cfg = self._load(
            os.path.join(TESTDATADIR, "conf20.yml"),
            os.path.join(TESTDATADIR, "conf21.yml"))
        self.assertIsNone(
            cfg.getfirstvarl_s(
                *[{"namespace": None, "path": ["intl", "non", "existing"]},
                  {"namespace": None, "path": ["intl", "non", "existing2"]}],
                default=None))
        self.assertRaises(
            KeyError,
            cfg.getfirstvarl_s,
            {"namespace": None, "path": ["intl" ,"non", "existing"]},
            {"namespace": None, "path": ["intl", "non", "existing2"]})

    def test32_getfirstintvarl_s_nonexisting(self):
        cfg = self._load(os.path.join(TESTDATADIR, "conf20.yml"))
        self.assertIsNone(cfg.getfirstintvarl_s(
            *(("db", "non", "existing", "key"),
              ("db", "non", "existing", "key2"),
              ("intl", "non", "existing")),
            default=None))
        self.assertRaises(
            KeyError,
            cfg.getfirstintvarl_s,
            ("db", "non", "existing", "key"),
            ("db", "non", "exksting", "key2"),
            ("intl", "non", "existing"))

    def test33_getfirstintvarl_s_nonexisting(self):
        cfg = self._load(os.path.join(TESTDATADIR, "conf20.yml"))
        self.assertEqual(
            20,
            cfg.getfirstintvarl_s(
                *(("db", "non", "existing", ".key"),
                  ("db", "non", "existing", "key2"),
                  ("intl", "non", "existing")),
                default=u("20")))
        self.assertEqual(
            30,
            cfg.getfirstintvarl_s(
                *(("db", "non", "existing", "key"),
                  ("db", "non", "existing", "key2"),
                  ("intl", "non", "existing")),
                default=30))
        self.assertRaises(
            KeyError,
            cfg.getfirstintvarl_s,
            ("db", "non", "existing", "key"),
            ("db", "non", "exksting", "key2"),
            ("intl", "non", "existing"))

    def test34_getfirstintvarl_s_existing(self):
        cfg = self._load(os.path.join(TESTDATADIR, "conf20.yml"))
        self.assertEqual(
            10,
            cfg.getfirstintvarl_s(
                *(("db", "non", "existing", "key"),
                  ("intl", "cache", "items"),
                  ("db", "non", "existing", "key2")),
                default=u("20")))

    def test35_keysl(self):
        cfg = self._load(os.path.join(TESTDATADIR, "conf20.yml"))
        self.assertEqual(
            set([u"domain", u"localedir", u"fallback", u"cache",]),
            set(cfg.getkeysl(u"intl")))

    def test36_keys(self):
        cfg = self._load(os.path.join(TESTDATADIR, "conf20.yml"))
        self.assertEqual(
            set([u"name", u"pwd"]),
            set(cfg.getkeys(u"db.user")))

    def test37_get_root_object(self):
        cfg = self._load(os.path.join(TESTDATADIR, "conf20.yml"))
        self.assertTrue(cfg.getvarl() is cfg)

    def test38_get_root_object(self):
        cfg = self._load(os.path.join(TESTDATADIR, "conf20.yml"))
        self.assertTrue(cfg.getvar(u"") is cfg)

    def test39_get_root_keys(self):
        cfg = self._load(os.path.join(TESTDATADIR, "conf10.py"))
        self.assertEqual(
            set([u"key1", u"key2", u"tree1"]),
            set(cfg.getkeys(u"")))

    def test39b_get_root_keys(self):
        cfg = self._load(os.path.join(TESTDATADIR, "conf10.py"))
        self.assertEqual(
            set([u"key1", u"key2", u"tree1"]),
            set(cfg.getkeysl()))


class T02LoadAndMerge(_T02MixinLoadAndMerge, unittest.TestCase):

    def setUp(self):
        self._load = configmix.load

    def test0100_identity(self):
        cfg = configmix.ini.load(os.path.join(TESTDATADIR, "conf1.ini"))
        cfg2 = configmix.merge(cfg, None)
        self.assertEqual(id(cfg), id(cfg2))

    def test0101_identity(self):
        cfg = configmix.ini.load(os.path.join(TESTDATADIR, "conf1.ini"))
        cfg2 = configmix.merge(cfg, {})
        self.assertEqual(id(cfg), id(cfg2))


class T03SafeLoadAndMerge(_T02MixinLoadAndMerge, unittest.TestCase):

    def setUp(self):
        self._load = configmix.safe_load

    def test0100_deepcopy(self):
        cfg = configmix.ini.load(os.path.join(TESTDATADIR, "conf1.ini"))
        cfg2 = configmix.safe_merge(cfg, None)
        self.assertNotEqual(id(cfg), id(cfg2))

    def test0101_deepcopy(self):
        cfg = configmix.ini.load(os.path.join(TESTDATADIR, "conf1.ini"))
        cfg2 = configmix.safe_merge(cfg, {})
        self.assertNotEqual(id(cfg), id(cfg2))


class T04CustomExtension(unittest.TestCase):

    def setUp(self):
        self._reset()

    def tearDown(self):
        self._reset()

    def _reset(self):
        configmix.clear_assoc()
        for pat, fmode in configmix.DEFAULT_ASSOC:
            configmix.set_assoc(pat, fmode)

    def test01_additional(self):
        configmix.set_assoc("*.conf", configmix.get_default_assoc("*.yml"))
        cfg = configmix.load(
            os.path.join(TESTDATADIR, "conf1.ini"),
            os.path.join(TESTDATADIR, "conf30.conf"))
        self.assertEqual(u("Umlaute: ÄÖÜäöüß"), cfg.getvar_s("key7"))
        self.assertEqual(u("new value"), cfg.getvar_s("key-new"))

    def test02_only_style_wrong_style(self):
        configmix.clear_assoc()
        configmix.set_assoc("*.conf", configmix.get_default_assoc("*.yml"))

        def _ld():
            return configmix.load(os.path.join(TESTDATADIR, "conf1.ini"),
                                  os.path.join(TESTDATADIR, "conf30.conf"))

        self.assertRaises(ValueError, _ld)

    def test02_ignore_one_style(self):
        configmix.clear_assoc()
        configmix.set_assoc("*.conf", configmix.get_default_assoc("*.yml"))
        configmix.set_assoc("*", "-*-ignore-*-", append=True)

        cfg = configmix.load(os.path.join(TESTDATADIR, "conf1.ini"),
                             os.path.join(TESTDATADIR, "conf30.conf"))
        self.assertEqual(u("new value"), cfg.getvar_s("key-new"))
        self.assertRaises(KeyError, cfg.getvar_s, "key1")

    def test02_ignore_all(self):
        configmix.clear_assoc()
        configmix.set_assoc("*.conf", configmix.get_default_assoc("*.yml"))
        # This inserts at index 0 effectively ignoring all configuration files!
        configmix.set_assoc("*", "-*-ignore-*-")

        cfg = configmix.load(os.path.join(TESTDATADIR, "conf1.ini"),
                             os.path.join(TESTDATADIR, "conf30.conf"))
        self.assertRaises(KeyError, cfg.getvar_s, "key-new")
        self.assertRaises(KeyError, cfg.getvar_s, "key1")

    def test03_only_style_corrrect_style(self):
        configmix.clear_assoc()
        configmix.set_assoc("*.conf", configmix.get_default_assoc("*.yml"))
        cfg = configmix.load(os.path.join(TESTDATADIR, "conf30.conf"))
        self.assertEqual(u("new value"), cfg.getvar_s("key-new"))

        def _g():
            return cfg.getvar_s("key7")

        self.assertRaises(KeyError, _g)

    def test04_determine_mode(self):
        configmix.clear_assoc()
        configmix.set_assoc("*.conf", configmix.try_determine_filemode)
        cfg = configmix.load(os.path.join(TESTDATADIR, "conf30.conf"))
        self.assertEqual(u("new value"), cfg.getvar_s("key-new"))

        self.assertRaises(KeyError, cfg.getvar_s, "key7")

    def test05_try_determine_mode_none(self):
        configmix.clear_assoc()
        configmix.set_assoc("*.conf", configmix.try_determine_filemode)

        def _ld():
            return configmix.load(os.path.join(TESTDATADIR, "no-mode.conf"))

        self.assertRaises(ValueError, _ld)

    def test06_try_determine_mode_unknown(self):
        configmix.clear_assoc()
        configmix.set_assoc("*.conf", configmix.try_determine_filemode)

        def _ld():
            return configmix.load(os.path.join(TESTDATADIR, "unknown-mode.conf"))

        self.assertRaises(KeyError, _ld)


class T05SubstituteExpand(unittest.TestCase):

    def setUp(self):
        self._reset()

    def tearDown(self):
        self._reset()

    def _reset(self):
        configmix.clear_assoc()
        for pat, fmode in configmix.DEFAULT_ASSOC:
            configmix.set_assoc(pat, fmode)

    def test01_expand_int_ini(self):
        cfg = configmix.load(os.path.join(TESTDATADIR, "conf1.ini"))
        self.assertEqual(2, cfg.getvar_s("key100"))

    def test01_expand_int_ini_l(self):
        cfg = configmix.load(os.path.join(TESTDATADIR, "conf1.ini"))
        self.assertEqual(2, cfg.getvarl_s("key100"))

    def test02_expand_int_indirect_ini(self):
        cfg = configmix.load(os.path.join(TESTDATADIR, "conf1.ini"))
        self.assertEqual(2, cfg.getvar_s("key102"))

    def test02_expand_int_indirect_ini_l(self):
        cfg = configmix.load(os.path.join(TESTDATADIR, "conf1.ini"))
        self.assertEqual(2, cfg.getvarl_s("key102"))

    def test03_expand_int2str_ini(self):
        cfg = configmix.load(os.path.join(TESTDATADIR, "conf1.ini"))
        self.assertEqual("the 2 value", cfg.getvar_s("key101"))

    def test03_expand_int2str_ini_l(self):
        cfg = configmix.load(os.path.join(TESTDATADIR, "conf1.ini"))
        self.assertEqual("the 2 value", cfg.getvarl_s("key101"))

    def test04_expand_intint2str_ini(self):
        cfg = configmix.load(os.path.join(TESTDATADIR, "conf1.ini"))
        self.assertEqual("22", cfg.getvar_s("key103"))

    def test04_expand_intint2str_ini_l(self):
        cfg = configmix.load(os.path.join(TESTDATADIR, "conf1.ini"))
        self.assertEqual("22", cfg.getvarl_s("key103"))


class T06References(unittest.TestCase):

    def setUp(self):
        self._reset()
        self._cfg = configmix.load(
            os.path.join(TESTDATADIR, "conf20.yml"),
            os.path.join(TESTDATADIR, "conf21.yml"),
            os.path.join(TESTDATADIR, "conf22.ini"),
            os.path.join(TESTDATADIR, "conf23.json"),
            os.path.join(TESTDATADIR, "conf24.toml"),
            os.path.join(TESTDATADIR, "reference-style.yml"))

    def tearDown(self):
        self._reset()

    def _reset(self):
        configmix.clear_assoc()
        for pat, fmode in configmix.DEFAULT_ASSOC:
            configmix.set_assoc(pat, fmode)

    def test01_reference_without_expansions(self):
        self.assertTrue(isinstance(self._cfg.getvar("wsgi.profiler"), dict))
        self.assertTrue(isinstance(
            self._cfg.getvar("wsgi.profiler.params"), dict))
        self.assertEqual("werkzeug",
                         self._cfg.getvar("wsgi.profiler.params.type"))
        self.assertTrue(self._cfg.getvar("wsgi.profiler.params.params.evalex"))
        self.assertEqual(self._cfg.getvar("wsgi.debugger"),
                         self._cfg.getvar("wsgi.profiler.params"))

    def test01_reference_without_expansions_l(self):
        self.assertTrue(isinstance(self._cfg.getvarl("wsgi", "profiler"), dict))
        self.assertTrue(isinstance(
            self._cfg.getvarl("wsgi", "profiler", "params"), dict))
        self.assertEqual(
            "werkzeug",
            self._cfg.getvarl("wsgi", "profiler", "params", "type"))
        self.assertTrue(self._cfg.getvarl(
            "wsgi", "profiler", "params", "params", "evalex"))
        self.assertEqual(self._cfg.getvarl("wsgi", "debugger"),
                         self._cfg.getvarl("wsgi", "profiler", "params"))

    def test02_reference__with_expansions(self):
        self.assertTrue(isinstance(self._cfg.getvar_s("wsgi.profiler"), dict))
        self.assertTrue(isinstance(
            self._cfg.getvar_s("wsgi.profiler.params"), dict))
        self.assertTrue(
            self._cfg.getvar_s("wsgi.profiler.params.params.evalex"))
        self.assertEqual("werkzeug",
                         self._cfg.getvar_s("wsgi.profiler.params.type"))

    def test02_reference__with_expansions_l(self):
        self.assertTrue(isinstance(
            self._cfg.getvarl_s("wsgi", "profiler"), dict))
        self.assertTrue(isinstance(
            self._cfg.getvarl_s("wsgi", "profiler", "params"), dict))
        self.assertTrue(
            self._cfg.getvarl_s("wsgi", "profiler", "params", "params", "evalex"))
        self.assertEqual(
            "werkzeug",
            self._cfg.getvarl_s("wsgi", "profiler", "params", "type"))

    def test03_no_direct_attribute_access_to_expanded_references(self):
        self.assertEqual(
            "{{ref:#wsgi.debugger}}",
            self._cfg.wsgi.profiler.params)
        try:
            self._cfg.wsgi.profiler.params.type
        except AttributeError:
            pass
        else:
            self.fail("no attribute error seen")

    def test04_indirect_recursive_references(self):
        self.assertEqual(
            "werkzeug",
            self._cfg.getvar_s("testref.here.params.type"))
        self.assertTrue(
            self._cfg.getvar_s("testref.here.params.params.evalex"))

    def test04_indirect_recursive_references_l(self):
        self.assertEqual(
            "werkzeug",
            self._cfg.getvarl_s("testref", "here", "params", "type"))
        self.assertTrue(
            self._cfg.getvarl_s("testref", "here", "params", "params", "evalex"))

    def test05_recursive_expansion(self):
        c = self._cfg.getvar_s("testref")
        self.assertTrue(c["here"]["params"]["params"]["evalex"])

    def test05_recursive_expansion_l(self):
        c = self._cfg.getvarl_s("testref")
        self.assertTrue(c["here"]["params"]["params"]["evalex"])

    def test06_no_recursive_expansion_in_getvar_parents(self):
        v = self._cfg.getvar("wsgi.profiler")
        self.assertEqual(
            "{{ref:#wsgi.debugger}}",
            v["params"])

    def test06_no_recursive_expansion_in_getvar_parents_l(self):
        v = self._cfg.getvarl("wsgi", "profiler")
        self.assertEqual(
            "{{ref:#wsgi.debugger}}",
            v["params"])

    def test07_explicit_reference_expansion(self):
        v = self._cfg.getvar("wsgi.profiler")
        self.assertTrue(isinstance(self._cfg.expand_if_reference(v["params"]),
                                   dict))

    def test07_explicit_reference_expansion_l(self):
        v = self._cfg.getvarl("wsgi", "profiler")
        self.assertTrue(isinstance(self._cfg.expand_if_reference(v["params"]),
                                   dict))

    def test08_explicit_indirect_expansion_through_value(self):
        v = self._cfg.getvar_s("expand-ref-value.key0")
        self.assertTrue(isinstance(v, bool))
        self.assertTrue(v)
        # but not that .getvar does not **not**
        v2 = self._cfg.getvar("expand-ref-value.key0")
        self.assertEqual("{{testref.here.params.params.evalex}}", v2)

    def test08_explicit_indirect_expansion_through_value_l(self):
        v = self._cfg.getvarl_s("expand-ref-value", "key0")
        self.assertTrue(isinstance(v, bool))
        self.assertTrue(v)
        # but not that .getvar does not **not**
        v2 = self._cfg.getvarl("expand-ref-value", "key0")
        self.assertEqual("{{testref.here.params.params.evalex}}", v2)


class T07Quoting(unittest.TestCase):

    def setUp(self):
        self._reset()
        self._cfg = configmix.load(os.path.join(TESTDATADIR, "quoting.yml"))

    def tearDown(self):
        self._reset()

    def _reset(self):
        configmix.clear_assoc()
        for pat, fmode in configmix.DEFAULT_ASSOC:
            configmix.set_assoc(pat, fmode)

    def test_getvar(self):
        self.assertEqual(
            "value",
            self._cfg.getvar("%x23%x3a%x7c%x25%x2e.%x2e.%x3a.%x25.%x7c./.%x23"))
        self.assertEqual(
            "value",
            self._cfg.getvar(
                "%u0023%u003a%u007c%u0025%u002e.%u002e.%u003a.%u0025.%u007c./.%u0023"))
        self.assertEqual(
            "value",
            self._cfg.getvar(
                "%U00000023%U0000003a%U0000007c%U00000025%U0000002e.%U0000002e.%U0000003a.%U00000025.%U0000007c./.%U00000023"))

    def test_getvar_s(self):
        self.assertEqual(
            "value",
            self._cfg.getvar_s("%x23%x3a%x7c%x25%x2e.%x2e.%x3a.%x25.%x7c./.%x23"))
        self.assertEqual(
            "value",
            self._cfg.getvar_s(
                "%u0023%u003a%u007c%u0025%u002e.%u002e.%u003a.%u0025.%u007c./.%u0023"))
        self.assertEqual(
            "value",
            self._cfg.getvar_s(
                "%U00000023%U0000003a%U0000007c%U00000025%U0000002e.%U0000002e.%U0000003a.%U00000025.%U0000007c./.%U00000023"))

    def test_getvarl(self):
        self.assertEqual(
            "value",
            self._cfg.getvarl("#:|%.", ".", ":", "%", "|", "/", "#"))

    def test_getvarl_s(self):
        self.assertEqual(
            "value",
            self._cfg.getvarl_s("#:|%.", ".", ":", "%", "|", "/", "#"))

    def test_interpolation1(self):
        self.assertEqual(
            "value",
            self._cfg.getvarl_s("events", "qc-2021.1-5G-summit", "xname"))

    def test_interpolation2(self):
        self.assertEqual(
            "value",
            self._cfg.getvar_s("events.qc-2021%x2e1-5G-summit.xname"))

    def test_reference1(self):
        self.assertTrue(
            isinstance(
                self._cfg.getvar_s("events.qc-2021%x2e1-5G-summit.xref"),
                dict))

    def test_reference2(self):
        self.assertEqual(
            "value",
            self._cfg.getvar_s("events.qc-2021%x2e1-5G-summit.xref.%x23"))

    def test_quoting_and_unquoting_are_inverse(self):
        for c in """%.:#|"'{}[]""":
            qc = self._cfg.quote(c)
            self.assertTrue(qc.startswith("%x") and len(qc) == 4)
            self.assertEqual(c, self._cfg.unquote(qc))

    def test_quoting_and_unquoting_are_identical(self):
        # other characters
        for c in """abc09/""":
            qc = self._cfg.quote(c)
            self.assertEqual(c, qc)
            self.assertEqual(c, self._cfg.unquote(qc))

    def test_namespace_quoting(self):
        v1 = self._cfg.getvar("PY:version")
        v2 = self._cfg.getvar("P%x59:version")
        v3 = self._cfg.getvar("%U00000050Y:version")
        v4 = self._cfg.getvar("%x50%u0059:version")
        v5 = self._cfg.getvarl_s("events", "qc-2021.1-5G-summit", "xver")
        v6 = self._cfg.getvar_s("events.qc-2021%x2e1-5G-summit.xver")
        self.assertEqual(v1, v2)
        self.assertEqual(v1, v3)
        self.assertEqual(v1, v4)
        self.assertEqual(v1, v5)
        self.assertEqual(v1, v6)

    def test_direct_ref_namespace_quoting(self):
        v = self._cfg.getvar_s("re%x66:#%x23%u003a%x7c%U00000025%x2e.%x2e.%x3a.%x25.%x7c./.%x23")
        self.assertEqual("value", v)


class T08Jailed(unittest.TestCase):

    def setUp(self):
        pass

    def tearDown(self):
        self._reset()

    def _reset(self):
        configmix.clear_assoc()
        for pat, fmode in configmix.DEFAULT_ASSOC:
            configmix.set_assoc(pat, fmode)

    def test_root(self):
        cfg = configmix.load(os.path.join(TESTDATADIR, "conf10.py"))
        jcfg = cfg.jailed(root=u"tree1")

        self.assertTrue(jcfg.getvarl(u"tree2", u"key5"))
        self.assertTrue(jcfg.getvarl_s(u"tree2", u"key5"))
        self.assertEqual(
            u"get this as `tree1.tree2.key4'",
            jcfg.getvarl(u"tree2", u"key4"))
        self.assertEqual(
            u"get this as `tree1.tree2.key4'",
            jcfg.getvarl_s(u"tree2", u"key4"))

        self.assertTrue(jcfg.getvar(u"tree2.key5"))
        self.assertTrue(jcfg.getvar_s(u"tree2.key5"))
        self.assertEqual(
            u"get this as `tree1.tree2.key4'",
            jcfg.getvar(u"tree2.key4"))
        self.assertEqual(
            u"get this as `tree1.tree2.key4'",
            jcfg.getvar_s(u"tree2.key4"))

    def test_root2(self):
        cfg = configmix.load(os.path.join(TESTDATADIR, "conf10.py"))
        jcfg = cfg.jailed(root=u"tree1.tree2")

        self.assertTrue(jcfg.getvarl(u"key5"))
        self.assertTrue(jcfg.getvarl_s(u"key5"))
        self.assertEqual(
            u"get this as `tree1.tree2.key4'",
            jcfg.getvarl(u"key4"))
        self.assertEqual(
            u"get this as `tree1.tree2.key4'",
            jcfg.getvarl_s(u"key4"))

        self.assertTrue(jcfg.getvar(u"key5"))
        self.assertTrue(jcfg.getvar_s(u"key5"))
        self.assertEqual(
            u"get this as `tree1.tree2.key4'",
            jcfg.getvar(u"key4"))
        self.assertEqual(
            u"get this as `tree1.tree2.key4'",
            jcfg.getvar_s(u"key4"))

    def test_root_non_existing_raises(self):
        cfg = configmix.load(os.path.join(TESTDATADIR, "conf10.py"))
        self.assertRaises(KeyError, cfg.jailed, root=u"tree-non-existing")

    def test_rootpath(self):
        cfg = configmix.load(os.path.join(TESTDATADIR, "conf10.py"))
        jcfg = cfg.jailed(rootpath=[u"tree1"])

        self.assertTrue(jcfg.getvarl(u"tree2", u"key5"))
        self.assertTrue(jcfg.getvarl_s(u"tree2", u"key5"))
        self.assertEqual(
            u"get this as `tree1.tree2.key4'",
            jcfg.getvarl_s(u"tree2", u"key4"))

        self.assertTrue(jcfg.getvar(u"tree2.key5"))
        self.assertTrue(jcfg.getvar_s(u"tree2.key5"))
        self.assertEqual(
            u"get this as `tree1.tree2.key4'",
            jcfg.getvar_s(u"tree2.key4"))

    def test_rootpath_non_existing_raises(self):
        cfg = configmix.load(os.path.join(TESTDATADIR, "conf10.py"))
        self.assertRaises(
            KeyError, cfg.jailed, rootpath=[u"tree-non-existing"])

    def test_root_empty(self):
        cfg = configmix.load(os.path.join(TESTDATADIR, "conf10.py"))
        jcfg = cfg.jailed(root=u"")

        self.assertFalse(jcfg._path)
        self.assertFalse(jcfg._pathstr)
        self.assertTrue(jcfg._path is not None)
        self.assertTrue(jcfg._pathstr is not None)

        self.assertTrue(jcfg.getvarl(u"tree1", u"tree2", u"key5"))
        self.assertTrue(jcfg.getvarl_s(u"tree1", u"tree2", u"key5"))
        self.assertEqual(
            u"get this as `tree1.tree2.key4'",
            jcfg.getvarl_s(u"tree1", u"tree2", u"key4"))

        self.assertTrue(jcfg.getvar(u"tree1.tree2.key5"))
        self.assertTrue(jcfg.getvar_s(u"tree1.tree2.key5"))
        self.assertEqual(
            u"get this as `tree1.tree2.key4'",
            jcfg.getvar_s(u"tree1.tree2.key4"))

    def test_rootpath_empty(self):
        cfg = configmix.load(os.path.join(TESTDATADIR, "conf10.py"))
        jcfg = cfg.jailed(rootpath=tuple())

        self.assertFalse(jcfg._path)
        self.assertFalse(jcfg._pathstr)
        self.assertTrue(jcfg._path is not None)
        self.assertTrue(jcfg._pathstr is not None)

        self.assertTrue(jcfg.getvarl(u"tree1", u"tree2", u"key5"))
        self.assertTrue(jcfg.getvarl_s(u"tree1", u"tree2", u"key5"))
        self.assertEqual(
            u"get this as `tree1.tree2.key4'",
            jcfg.getvarl_s(u"tree1", u"tree2", u"key4"))

        self.assertTrue(jcfg.getvar(u"tree1.tree2.key5"))
        self.assertTrue(jcfg.getvar_s(u"tree1.tree2.key5"))
        self.assertEqual(
            u"get this as `tree1.tree2.key4'",
            jcfg.getvar_s(u"tree1.tree2.key4"))

    def test_rootpath_getfirstvar_nonexisting(self):
        cfg = configmix.load(os.path.join(TESTDATADIR, "conf10.py"))
        jcfg = cfg.jailed(rootpath=(u"tree1",))

        self.assertIsNone(jcfg.getfirstvarl(
            *[(u"a", u"b"),
              (u"tree2", u"no-key")],
            default=None))
        self.assertIsNone(jcfg.getfirstvarl_s(
            *[(u"a", u"b"),
              (u"tree2", u"no-key")],
            default=None))
        self.assertIsNone(jcfg.getfirstvar(
            u"a.b", u"tree2.no-key",
            default=None))
        self.assertIsNone(jcfg.getfirstvar_s(
            u"a.b", u"tree2.no-key",
            default=None))

    def test_rootpath_getfirstvar_raising(self):
        cfg = configmix.load(os.path.join(TESTDATADIR, "conf10.py"))
        jcfg = cfg.jailed(rootpath=(u"tree1",))

        self.assertRaises(
            KeyError,
            jcfg.getfirstvarl,
            (u"a", u"b"),
            (u"tree2", u"no-key"))
        self.assertRaises(
            KeyError,
            jcfg.getfirstvarl_s,
            (u"a", u"b"),
            (u"tree2", u"no-key"))
        self.assertRaises(
            KeyError,
            jcfg.getfirstvar,
            u"a.b", u"tree2.no-key")
        self.assertRaises(
            KeyError,
            jcfg.getfirstvar_s,
            u"a.b", u"tree2.no-key")

    def test_rootpath_getfirstvar_existing(self):
        cfg = configmix.load(os.path.join(TESTDATADIR, "conf10.py"))
        jcfg = cfg.jailed(rootpath=(u"tree1",))

        self.assertEqual(
            u"get this as `tree1.tree2.key4'",
            jcfg.getfirstvarl(
                *[(u"a", u"b"),
                  (u"tree2", u"key4")]))
        self.assertEqual(
            0x20,
            jcfg.getfirstvarl_s(
                *[(u"a", u"b"),
                  (u"key3", ),
                  (u"tree2", u"key4")]))
        self.assertEqual(
            0x20,
            jcfg.getfirstvar(
                u"key1", u"key3"))
        self.assertEqual(
            u"get this as `tree1.tree2.key4'",
            jcfg.getfirstvar_s(
                u"a.b", u"tree2.key4", u"tree2.key5"))

    def test_root_getfirstvar_nonexisting(self):
        cfg = configmix.load(os.path.join(TESTDATADIR, "conf10.py"))
        jcfg = cfg.jailed(root=u"tree1")

        self.assertIsNone(jcfg.getfirstvarl(
            *[(u"a", u"b"),
              (u"tree2", u"no-key")],
            default=None))
        self.assertIsNone(jcfg.getfirstvarl_s(
            *[(u"a", u"b"),
              (u"tree2", u"no-key")],
            default=None))
        self.assertIsNone(jcfg.getfirstvarl_s(
            *[(u"a", u"b"),
              (u"tree2", u"no-key")],
            default=None))
        self.assertIsNone(jcfg.getfirstvar_s(
            u"a.b", u"tree2.no-key",
            default=None))

    def test_root_getfirstvar_raising(self):
        cfg = configmix.load(os.path.join(TESTDATADIR, "conf10.py"))
        jcfg = cfg.jailed(root=u"tree1")

        self.assertRaises(
            KeyError,
            jcfg.getfirstvarl,
            (u"a", u"b"),
            (u"tree2", u"no-key"))
        self.assertRaises(
            KeyError,
            jcfg.getfirstvarl_s,
            (u"a", u"b"),
            (u"tree2", u"no-key"))
        self.assertRaises(
            KeyError,
            jcfg.getfirstvar,
            u"a.b", u"tree2.no-key")
        self.assertRaises(
            KeyError,
            jcfg.getfirstvar_s,
            u"a.b", u"tree2.no-key")

    def test_base_rebind(self):
        cfg = configmix.load(os.path.join(TESTDATADIR, "conf10.py"))
        self.assertFalse(cfg.is_jail)

        jcfg = cfg.jailed(rootpath=[])
        self.assertTrue(jcfg.is_jail)

        self.assertTrue(jcfg.base is cfg)

        self.assertRaises(
            KeyError,
            jcfg.getvarl,
            u"tree1", u"tree2", u"no-key")
        self.assertEqual(
            0x20,
            jcfg.getvar(u"tree1.key3"))
        self.assertEqual(
            u"in the root namespace",
            jcfg.getvar("key1"))

        cfg2 = configmix.load(os.path.join(TESTDATADIR, "conf2.py"))
        self.assertFalse(cfg2.is_jail)

        jcfg.rebind(cfg2)
        self.assertTrue(jcfg.base is cfg2)

        self.assertRaises(
            KeyError,
            jcfg.getvar,
            u"tree1.key3")
        self.assertEqual(
            u"the next value",
            jcfg.getvar("key1"))

    def test_rebind_no_nesting(self):
        cfg = configmix.load(os.path.join(TESTDATADIR, "conf10.py"))
        self.assertFalse(cfg.is_jail)

        jcfg1 = cfg.jailed(rootpath=[])
        self.assertTrue(jcfg1.is_jail)
        jcfg2 = cfg.jailed(rootpath=[u"tree1"])
        self.assertTrue(jcfg2.is_jail)

        self.assertRaises(
            TypeError,
            jcfg1.rebind,
            jcfg2)

        # base is not because rebind() failed
        self.assertTrue(cfg is jcfg1.base)

    def test_getbool(self):
        cfg = configmix.load(os.path.join(TESTDATADIR, "conf10.py"))
        jcfg = cfg.jailed(rootpath=(u"tree1", u"tree2"))

        self.assertFalse(jcfg.getboolvar_s(u"key6"))
        self.assertEqual(u"off", jcfg.getvarl_s(u"key6"))
        self.assertTrue(jcfg.getvar_s(u"key6"))

    def test_subjail_from_rootpath_empty(self):
        cfg = configmix.load(os.path.join(TESTDATADIR, "conf10.py"))
        jcfg = cfg.jailed(rootpath=tuple())

        sjcfg = jcfg.jailed(rootpath=(u"tree1",))

        self.assertTrue(sjcfg._path)
        self.assertTrue(sjcfg._pathstr)

        self.assertEqual(0x20, sjcfg.getintvar_s(u"key3"))

        self.assertTrue(sjcfg.base is cfg)

    def test_subjail_from_root_empty(self):
        cfg = configmix.load(os.path.join(TESTDATADIR, "conf10.py"))
        jcfg = cfg.jailed(root=u"")

        sjcfg = jcfg.jailed(root=u"tree1")

        self.assertTrue(sjcfg._path)
        self.assertTrue(sjcfg._pathstr)

        self.assertEqual(0x20, sjcfg.getintvar_s(u"key3"))

        self.assertTrue(sjcfg.base is cfg)

    def test_getkeys(self):
        cfg = configmix.load(os.path.join(TESTDATADIR, "conf10.py"))
        jcfg = cfg.jailed(root=u"tree1")

        self.assertEqual(
            set([u"key4", u"key5", u"key6"]),
            set(jcfg.getkeysl(u"tree2")))

        self.assertEqual(
            set([u"key4", u"key5", u"key6"]),
            set(jcfg.getkeys(u"tree2")))

        self.assertEqual(
            set([u"key3", u"tree2"]),
            set(jcfg.getkeysl()))

    def test_getkeys_all_empty_paths(self):
        cfg = configmix.load(os.path.join(TESTDATADIR, "conf10.py"))
        jcfg = cfg.jailed(rootpath=tuple())

        self.assertEqual(
            set([u"key1", u"key2", u"tree1"]),
            set(jcfg.getkeysl()))

        self.assertEqual(
            set([u"key1", u"key2", u"tree1"]),
            set(jcfg.getkeys(u"")))


if __name__ == "__main__":
    unittest.main()