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

import unittest
import platform
import io
import os
import sys

from _test_context import TESTDATADIR

import configmix
import configmix.ini
import configmix.yaml
import configmix.json
import configmix.py
import configmix.toml
import configmix.config
from configmix.compat import u, PY2


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 test02b_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("3rd-host3rd-host"),
                         cfg.getvar_s("db.locinfo.ro.hostname2"))

    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"))
        self.assertEqual(u(sys.executable), cfg.getvar_s("SYS:executable"))
        self.assertEqual(u(sys.prefix), cfg.getvar_s("SYS:prefix"))
        self.assertEqual(u(sys.platform), cfg.getvar_s("SYS:platform"))
        if hasattr(sys, "base_prefix"):
            self.assertEqual(u(sys.base_prefix),
                             cfg.getvar_s("SYS:base_prefix"))
        else:
            self.assertRaises(KeyError, cfg.getvar_s, "SYS:base_prefix")

    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"))
        self.assertEqual(u(sys.prefix),
                         cfg.getvarl_s("prefix", namespace="SYS"))

    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.interpolate_variables("{{non-existing|None}}")
        self.assertIsNone(x)

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

    def test10_Empty_filtersingle(self):
        cfg = self._load()
        x = cfg.interpolate_variables("{{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.interpolate_variables("{{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.interpolate_variables("{{intl.cache.items|Empty}}")
        self.assertEqual(10, x)
        x = cfg.interpolate_variables("{{intl.cache.items|Empty}}")
        self.assertEqual(10, x)

    def test12a_Empty_filter_pass_through_without_cache(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"))
        cfg.disable_cache()
        x = cfg.interpolate_variables("{{intl.cache.items|Empty}}")
        self.assertEqual(10, x)
        x = cfg.interpolate_variables("{{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")
        self.assertRaises(KeyError, cfg.getvar_s, "non.existing.key")

    def test13a_keyerror_without_cache(self):
        cfg = self._load(os.path.join(TESTDATADIR, "conf20.yml"))
        cfg.disable_cache()
        self.assertRaises(KeyError, cfg.getvar_s, "non.existing.key")
        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 test14_getvar_with_original_default(self):
        # The default must be the original and not a copy
        dflt = {"foo": "bar"}
        cfg = self._load(os.path.join(TESTDATADIR, "conf20.yml"))
        self.assertTrue(cfg.getvar("non.existing.key", default=dflt) is dflt)

    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"))
        self.assertEqual("999", cfg.getvar_s("non.existing.key",
                                             default="999"))

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

    def test15_getvar_s_with_original_default(self):
        # The default must be the original and not a copy
        dflt = {"foo2": "bar2"}
        cfg = self._load(os.path.join(TESTDATADIR, "conf20.yml"))
        self.assertTrue(cfg.getvar_s("non.existing.key", default=dflt) is dflt)

    def test15_getvar_s_substituting_error_with_original_default(self):
        # The default must be the original and not a copy
        dflt = {"foo22": "bar22"}
        cfg = self._load(os.path.join(TESTDATADIR, "conf20.yml"))
        # exists ...
        cfg.getvar(u"intl.localedir")
        # ... but cannot interpolated
        self.assertTrue(cfg.getvar_s(u"intl.localedir", default=dflt) is dflt)

    def test15b_getvarl_with_original_default(self):
        # The default must be the original and not a copy
        dflt = {"foo2": "bar2"}
        cfg = self._load(os.path.join(TESTDATADIR, "conf20.yml"))
        self.assertTrue(cfg.getvarl(u"non", u"existing", u"key",
                                     default=dflt) is dflt)

    def test15c_getvarl_s_with_original_default(self):
        # The default must be the original and not a copy
        dflt = {"foo3": "bar3"}
        cfg = self._load(os.path.join(TESTDATADIR, "conf20.yml"))
        self.assertTrue(cfg.getvarl_s(u"non", u"existing", u"key4",
                                      default=dflt) is dflt)

    def test15d_getvarl_s_substituting_error_with_original_default(self):
        dflt = {"foo4": "bar4"}
        cfg = self._load(os.path.join(TESTDATADIR, "conf20.yml"))
        # key exists ...
        cfg.getvarl(u"intl", u"localedir")
        # ... but cannot interpolated
        self.assertTrue(cfg.getvarl_s(u"intl", u"localedir",
                                      default=dflt) is dflt)

    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 test20a_getfirstvar_existing_without_cache(self):
        cfg = self._load(os.path.join(TESTDATADIR, "conf20.yml"))
        cfg.disable_cache()
        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 test22a_getfirstvar_s_non_existing_without_cache(self):
        cfg = self._load(
            os.path.join(TESTDATADIR, "conf20.yml"),
            os.path.join(TESTDATADIR, "conf21.yml"))
        cfg.disable_cache()
        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_default(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()))

    def test40_contains_with_string(self):
        cfg = self._load(os.path.join(TESTDATADIR, "conf10.py"))
        self.assertTrue(u"tree1" in cfg)
        self.assertFalse(u"non-existing-tree1" in cfg)

    def test41_contains_with_path(self):
        cfg = self._load(os.path.join(TESTDATADIR, "conf10.py"))
        self.assertTrue((u"tree1", u"tree2") in cfg)
        self.assertFalse((u"tree1", u"non-existing-tree2") in cfg)

    def test43_get_with_string(self):
        cfg = self._load(os.path.join(TESTDATADIR, "conf10.py"))
        self.assertEqual(
            u"in the root namespace",
            cfg.get(u"key1"))
        self.assertTrue(
            cfg.get(u"key1-not", default=None) is None)

    def test43_get_with_path(self):
        cfg = self._load(os.path.join(TESTDATADIR, "conf10.py"))
        self.assertEqual(
            0x20,
            cfg.get((u"tree1", u"key3")))
        self.assertEqual(
            0x1,
            cfg.get((u"no", u"key"), default=0x1))

    def test44_iterator(self):
        cfg = self._load(os.path.join(TESTDATADIR, "conf10.py"))
        s = []
        for k in cfg:
            s.append(k)
        s.sort()

        self.assertEqual([u"key1", u"key2", u"tree1"], s)

    def test45_clear_cache(self):
        cfg = self._load(os.path.join(TESTDATADIR, "conf10.py"))
        cfg.clear_cache()

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

    def test47_indexed_access_to_lists(self):
        cfg = self._load(os.path.join(TESTDATADIR, "conf20.yml"))
        for i in range(4):
            self.assertEqual(
                i,
                cfg.getvarl_s(u"test", u"List", i))

    def test48_indexed_access_to_lists(self):
        cfg = self._load(os.path.join(TESTDATADIR, "conf20.yml"))
        for i in range(4):
            self.assertEqual(
                i,
                cfg.getvar_s(u"test.List.~%d~" % (i, )))

    def test49_index_access_to_lists_with_subdicts(self):
        cfg = self._load(os.path.join(TESTDATADIR,
                                      "index-access-for-jails.yml"))
        for idx in range(len(cfg.getvarl(u"the-list"))):
            self.assertEqual(
                idx,
                cfg.getvarl_s(u"the-list", idx, u"entry"))

    def test50_index_access_to_lists_with_subdicts(self):
        cfg = self._load(os.path.join(TESTDATADIR,
                                      "index-access-for-jails.yml"))
        for idx in range(len(cfg.getvarl(u"the-list"))):
            self.assertEqual(
                idx,
                cfg.getvar_s(u"the-list.~%d~.entry" % (idx, )))

    def test51_neg_index_access_to_lists_with_subdicts(self):
        cfg = self._load(os.path.join(TESTDATADIR,
                                      "index-access-for-jails.yml"))
        self.assertEqual(
            2,
            cfg.getvarl_s(u"the-list", -1, u"entry"))

    def test52_neg_index_access_to_lists_with_subdicts(self):
        cfg = self._load(os.path.join(TESTDATADIR,
                                      "index-access-for-jails.yml"))
        self.assertEqual(
            2,
            cfg.getvar_s(u"the-list.~%d~.entry" % (-1, )))

    def test53_expand_an_indexed_substitution(self):
        cfg = self._load(os.path.join(TESTDATADIR,
                                      "index-access-for-jails.yml"))
        self.assertEqual(1, cfg.getvar_s(u"expand-me"))
        self.assertEqual(2, cfg.getvar_s(u"expand-me-2"))

    def test54_list_merge_replaces_by_default(self):
        cfg = self._load(os.path.join(TESTDATADIR, "conf10.toml"),
                         os.path.join(TESTDATADIR, "conf10.yml"))
        self.assertEqual([u"val1", u"val2", u"in the root namespace"],
                         cfg.getvarl_s(u"tree1", u"tree2", u"key8"))

    def test55_list_merge_extend(self):
        cfg = self._load(os.path.join(TESTDATADIR, "conf10.toml"),
                         os.path.join(TESTDATADIR, "conf10.yml"),
                         merge_lists="extend")
        self.assertEqual([u"val1", u"val2", u"in the root namespace",
                          u"val1", u"val2", u"in the root namespace"],
                         cfg.getvarl_s(u"tree1", u"tree2", u"key8"))

    def test56_list_merge_wrong_param(self):
        try:
            self._load(os.path.join(TESTDATADIR, "conf10.toml"),
                       os.path.join(TESTDATADIR, "conf10.yml"),
                       merge_lists="__unknown-merge-strategy__")
        except ValueError:
            pass
        else:
            self.fail("Should have thrown a `ValueError' exception")

    def test57_list_merge_extend(self):
        cfg = self._load(os.path.join(TESTDATADIR, "conf10.toml"),
                         os.path.join(TESTDATADIR, "conf10_list_extend.yml"),
                         merge_lists="extend")
        self.assertEqual([u"val1", u"val2", u"in the root namespace",
                          u"val4", u"val5", u"val6"],
                         cfg.getvarl_s(u"tree1", u"tree2", u"key8"))

    def test58_list_merge_prepend(self):
        cfg = self._load(os.path.join(TESTDATADIR, "conf10.toml"),
                         os.path.join(TESTDATADIR, "conf10_list_extend.yml"),
                         merge_lists="prepend")
        self.assertEqual([u"val4", u"val5", u"val6",
                          u"val1", u"val2", u"in the root namespace",],
                         cfg.getvarl_s(u"tree1", u"tree2", u"key8"))

    def test59_list_merge_default(self):
        cfg = self._load(os.path.join(TESTDATADIR, "conf10.toml"),
                         os.path.join(TESTDATADIR, "conf10_list_extend.yml"))
        self.assertEqual([u"val4", u"val5", u"val6",],
                         cfg.getvarl_s(u"tree1", u"tree2", u"key8"))


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"))

    def test05_expand_falsy_values(self):
        cfg = configmix.load(os.path.join(TESTDATADIR, "conf20.yml"))
        self.assertEqual(
            u"AFalseB0C",
            cfg.getvar_s("test-falsy-values.v"))


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_direct_attribute_access_to_expanded_references(self):
        self.assertEqual(
            u"werkzeug",
            self._cfg.wsgi.profiler.params.type)

    def test03b_dict_like_access_expands_references(self):
        self.assertEqual(
            u"werkzeug",
            self._cfg[(u"wsgi", u"profiler", u"params", u"type")])

    def test03c_dict_like_access_with_single_string_key(self):
        self.assertTrue(
            u"profiler" in self._cfg[u"wsgi"])

    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_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")
        self.assertRaises(
            KeyError, cfg.jailed, rootpath=(u"non", u"existing", u"tree"))

    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", u"key7", u"key8", u"key9"]),
            set(jcfg.getkeysl(u"tree2")))

        self.assertEqual(
            set([u"key4", u"key5", u"key6", u"key7", u"key8", u"key9"]),
            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"")))

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

        self.assertEqual(
            r"_JailedConfiguration(rootpath=())",
            repr(jcfg))

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

        if PY2:
            self.assertEqual(
                r"_JailedConfiguration(rootpath=(u'tree1', u'tree2'))",
                repr(jcfg))
        else:
            self.assertEqual(
                r"_JailedConfiguration(rootpath=('tree1', 'tree2'))",
                repr(jcfg))

    def test_dict_level_access_with_single_key(self):
        cfg = configmix.load(os.path.join(TESTDATADIR, "conf10.py"))
        jcfg = cfg.jailed(rootpath=(u"tree1", u"tree2"))
        self.assertEqual(
            u"off",
            jcfg[u"key6"])
        try:
            jcfg[u"key3"]
        except KeyError:
            pass
        else:
            self.fail("KeyError expected")

    def test_dict_level_access_with_path(self):
        cfg = configmix.load(os.path.join(TESTDATADIR, "conf10.py"))
        jcfg = cfg.jailed(rootpath=(u"tree1", u"tree2"))
        self.assertEqual(
            u"get this as `tree1.tree2.key4'",
            jcfg[(u"key4",)])
        self.assertEqual(
            u"get this as `tree1.tree2.key4'",
            jcfg[[u"key4"]])
        try:
            jcfg[(u"key3",)]
        except KeyError:
            pass
        else:
            self.fail("KeyError expected")
        try:
            jcfg[[u"key3"]]
        except KeyError:
            pass
        else:
            self.fail("KeyError expected")

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

        self.assertTrue(u"key3" in jcfg)
        self.assertFalse(u"key3-not" in jcfg)

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

        self.assertTrue((u"key3",) in jcfg)
        self.assertFalse((u"key3-not",) in jcfg)

        self.assertTrue((u"tree2", u"key5") in jcfg)
        self.assertFalse((u"tree2", u"no-key") in jcfg)

        self.assertTrue([u"tree2", u"key5"] in jcfg)
        self.assertFalse([u"tree2", u"no-key"] in jcfg)

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

        self.assertEqual(
            0x20,
            jcfg.get(u"key3"))
        self.assertEqual(
            0x2,
            jcfg.get(u"no-key3", default=0x2))

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

        self.assertEqual(
            0x20,
            jcfg.get((u"key3",)))
        self.assertEqual(
            0x3,
            jcfg.get((u"no-key",), default=0x3))
        self.assertEqual(
            u"off",
            jcfg.get((u"tree2", u"key6")))
        self.assertEqual(
            u"the default",
            jcfg.get((u"no", u"key"), default=u"the default"))
        self.assertTrue(
            jcfg.get((u"no", u"key")) is None)

        self.assertEqual(
            u"off",
            jcfg.get([u"tree2", u"key6"]))
        self.assertEqual(
            u"the default",
            jcfg.get([u"no", u"key"], default=u"the default"))
        self.assertTrue(
            jcfg.get([u"no", u"key"]) is None)

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

        self.assertEqual(0x20, jcfg.key3)
        self.assertEqual(u"off", jcfg.tree2.key6)

    def test_attribute_access_interpolates(self):
        cfg = configmix.load(os.path.join(TESTDATADIR, "conf10.py"))
        self.assertEqual(
            [u"val1", u"val2", u"in the root namespace"],
            cfg.tree1.tree2.key8)

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

        try:
            jcfg.non_existing
        except AttributeError:
            pass
        else:
            self.fail("AttributeError expected")

        try:
            jcfg.tree2.non_existing
        except AttributeError:
            pass
        else:
            self.fail("AttributeError expected")

    def test_iteration_dict(self):
        cfg = configmix.load(os.path.join(TESTDATADIR, "conf10.py"))
        jcfg = cfg.jailed(rootpath=(u"tree1",))
        s = []
        for k in jcfg:
            s.append(k)
        s.sort()
        self.assertEqual([u"key3", u"tree2"], s)

    def test_iteration_list(self):
        cfg = configmix.load(os.path.join(TESTDATADIR, "conf10.py"))
        jcfg = cfg.jailed(rootpath=(u"tree1", u"tree2", u"key8"))
        s = []
        for k in jcfg:
            s.append(k)
        s.sort()
        self.assertEqual([u"in the root namespace", u"val1", u"val2"], s)

    def test_boolean_context_list_false(self):
        cfg = configmix.load(os.path.join(TESTDATADIR, "conf10.py"))
        jcfg = cfg.jailed(rootpath=(u"tree1", u"tree2", u"key7"))
        self.assertFalse(jcfg)
        self.assertEqual(0, len(jcfg))

    def test_boolean_context_list_true(self):
        cfg = configmix.load(os.path.join(TESTDATADIR, "conf10.py"))
        jcfg = cfg.jailed(rootpath=(u"tree1", u"tree2", u"key8"))
        self.assertTrue(jcfg)
        self.assertEqual(3, len(jcfg))

    def test_boolean_context_dict_false(self):
        cfg = configmix.load(os.path.join(TESTDATADIR, "conf10.py"))
        jcfg = cfg.jailed(rootpath=(u"tree1", u"tree2", u"key9"))
        self.assertFalse(jcfg)
        self.assertEqual(0, len(jcfg))

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

    def test_list_by_index(self):
        cfg = configmix.load(os.path.join(TESTDATADIR, "conf10.py"))
        jcfg = cfg.jailed(rootpath=(u"tree1", u"tree2", u"key8"))
        self.assertEqual(u"val1", jcfg[0])
        self.assertEqual(u"val2", jcfg[1])
        self.assertEqual(u"in the root namespace", jcfg[2])

    def test_index_jail_access(self):
        cfg = configmix.load(os.path.join(
            TESTDATADIR, "index-access-for-jails.yml"))
        for idx in range(len(cfg.getvarl(u"the-list"))):
            jcfg = cfg.jailed(rootpath=(u"the-list", idx))
            self.assertEqual(1, len(jcfg))
            self.assertEqual(idx, jcfg.getvarl_s(u"entry"))
            self.assertEqual((u"the-list", idx), jcfg._path)
            self.assertEqual(u"the-list.~%d~." % (idx, ), jcfg._pathstr)

    def test_index_subjail_access(self):
        cfg = configmix.load(os.path.join(
            TESTDATADIR, "index-access-for-jails.yml"))
        jcfg = cfg.jailed(rootpath=(u"the-list",))
        for idx in range(len(jcfg)):
            jcfg2 = jcfg.jailed(rootpath=(idx,))
            self.assertEqual(1, len(jcfg2))
            self.assertEqual(idx, jcfg2.getvarl_s(u"entry"))
            self.assertEqual((u"the-list", idx), jcfg2._path)
            self.assertEqual(u"the-list.~%d~." % (idx, ), jcfg2._pathstr)

    def test_direct_iter_jailed(self):
        cfg = configmix.load(os.path.join(
            TESTDATADIR, "index-access-for-jails.yml"))
        testidx = -1
        for jcfg2 in cfg.iter_jailed(rootpath=(u"the-list", )):
            testidx += 1
            self.assertEqual(1, len(jcfg2))
            self.assertEqual(testidx, jcfg2.getvarl_s(u"entry"))
            self.assertEqual((u"the-list", testidx), jcfg2._path)
            self.assertEqual(u"the-list.~%d~." % (testidx, ), jcfg2._pathstr)

    def test_iter_jailed(self):
        cfg = configmix.load(os.path.join(
            TESTDATADIR, "index-access-for-jails.yml"))
        jcfg = cfg.jailed(rootpath=(u"the-list",))
        testidx = -1
        for jcfg2 in jcfg.iter_jailed():
            testidx += 1
            self.assertEqual(1, len(jcfg2))
            self.assertEqual(testidx, jcfg2.getvarl_s(u"entry"))
            self.assertEqual((u"the-list", testidx), jcfg2._path)
            self.assertEqual(u"the-list.~%d~." % (testidx, ), jcfg2._pathstr)

    def test_negative_index_jail_access(self):
        cfg = configmix.load(os.path.join(
            TESTDATADIR, "index-access-for-jails.yml"))
        jcfg = cfg.jailed(rootpath=(u"the-list", -1))
        self.assertEqual(2, jcfg.getvarl_s("entry"))
        self.assertEqual((u"the-list", -1), jcfg._path)
        self.assertEqual(u"the-list.~-1~.", jcfg._pathstr)

    def test_index_jail_access_with_strpath(self):
        cfg = configmix.load(os.path.join(
            TESTDATADIR, "index-access-for-jails.yml"))
        for idx in range(len(cfg.getvarl(u"the-list"))):
            jcfg = cfg.jailed(root=u"the-list.~%d~" % (idx, ))
            self.assertEqual(1, len(jcfg))
            self.assertEqual(idx, jcfg.getvarl_s(u"entry"))
            self.assertEqual((u"the-list", idx), jcfg._path)
            self.assertEqual(u"the-list.~%d~." % (idx, ), jcfg._pathstr)

    def test_index_subjail_access_with_strpath(self):
        cfg = configmix.load(os.path.join(
            TESTDATADIR, "index-access-for-jails.yml"))
        jcfg = cfg.jailed(root=u"the-list")
        for idx in range(len(jcfg)):
            jcfg2 = jcfg.jailed(root=u"~%d~" % (idx, ))
            self.assertEqual(1, len(jcfg2))
            self.assertEqual(idx, jcfg2.getvarl_s(u"entry"))
            self.assertEqual((u"the-list", idx), jcfg2._path)
            self.assertEqual(u"the-list.~%d~." % (idx, ), jcfg2._pathstr)

    def test_referenced_root(self):
        cfg = configmix.load(os.path.join(
            TESTDATADIR, "jail-root-ref.yml"))
        jcfg = cfg.jailed(rootpath=("the-root", "ref1"))
        print(repr(jcfg.getvarl_s()))
        self.assertEqual("v3", jcfg.getvarl_s("ks"))

    def test_referenced_root_2(self):
        cfg = configmix.load(os.path.join(
            TESTDATADIR, "jail-root-ref.yml"))
        jcfg = cfg.jailed(rootpath=("the-root", "ref1"))
        print(repr(jcfg.getvarl_s()))
        self.assertEqual("v3", jcfg.getvarl_s("ks"))
        self.assertEqual("v1", jcfg.getvarl_s("k1"))

    def test_referenced_root_in_subjail(self):
        cfg = configmix.load(os.path.join(
            TESTDATADIR, "jail-root-ref.yml"))
        jcfg = cfg.jailed(rootpath=("the-root", "ref2"))
        jcfg2 = jcfg.jailed(rootpath=("sub-tree1",))
        self.assertEqual("v6", jcfg2.getvarl_s("k6"))
        self.assertEqual("v3", jcfg2.getvarl_s("ks"))

    def test_jails_and_refs_tree(self):
        cfg = configmix.load(os.path.join(
            TESTDATADIR, "jail-root-ref.yml"))
        for flavour in ("access", "job"):
            tokcfg = cfg.jailed(rootpath=("realworld", "token", flavour))
            self.assertEqual(-2, tokcfg.getintvar_s("nbf"))
            for idx, keysetcfg in enumerate(
                    tokcfg.jailed(rootpath=("sign",)).iter_jailed()):
                self.assertEqual("key-%d" % (idx+1,),
                                 keysetcfg.getvarl_s("name"))
                self.assertEqual("something%d" % (idx+1,),
                                 keysetcfg.getvarl_s("type"))
                self.assertIsNone(keysetcfg.getvarl_s("data"))


class _TParserMixin:
    def test_quote_and_unquote_empty(self):
        e = self.quote(u"")
        self.assertEqual(u"", e)
        self.assertEqual(u"", self.unquote(e))

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

    def test_quoting_and_unquoting_are_inverse_all(self):
        c = u"""%.:#|"'{}[]~"""
        qc = self.quote(c)
        self.assertEqual(len(c)*4, len(qc))
        self.assertEqual(c, self.unquote(qc))

    def test_quoting_and_unquoting_are_identical(self):
        # other characters
        for c in configmix.config._QUOTE_SAFE:
            qc = self.quote(c)
            self.assertEqual(c, qc)
            self.assertEqual(c, self.unquote(qc))

    def test_quoting_and_unquoting_are_identical_all(self):
        # other characters
        qc = self.quote(configmix.config._QUOTE_SAFE)
        self.assertEqual(configmix.config._QUOTE_SAFE, qc)
        self.assertEqual(configmix.config._QUOTE_SAFE, self.unquote(qc))

    def test_quote_index_to_tilde(self):
        self.assertEqual(u"~4~", self.quote(4))

    def test_unquote_index_with_tilde(self):
        self.assertEqual(4, self.unquote(u"~4~"))

    def test_unquote_empty_tilde(self):
        self.assertEqual(u"~~", self.unquote(u"~~"))

    def test_unquote_invalid_number_tilde(self):
        self.assertEqual(u"~0x4~", self.unquote(u"~0x4~"))

    def test_unquote_invalid_number_tilde_2(self):
        self.assertEqual(u"~\U00019001~", self.unquote(u"~%U00019001~"))

    def test_quote_unquote_indexes(self):
        for idx in range(0, 10000):
            self.assertEqual(idx, self.unquote(self.quote(idx)))

    def test_quote_unquote_negative_index(self):
        for idx in (-1, -2, -3):
            self.assertEqual(idx, self.unquote(self.quote(idx)))

    def test_index_overflow_border(self):
        self.assertEqual(32759, self.unquote(u"~32759~"))
        self.assertEqual(u"~32760~", self.unquote(u"~32760~"))

    def test_unquote_unimax(self):
        self.assertEqual(u"\U00019001", self.unquote(u"%U00019001"))
        self.assertEqual(u"X\U00019AF1Z", self.unquote(u"X%U00019aF1Z"))

    def test_unquote_base_plane(self):
        self.assertEqual(u"\uFFFF", self.unquote(u"%uffff"))
        self.assertEqual(u"X\uFFFFZ", self.unquote(u"X%uffffZ"))

    def test_unquote_latin(self):
        self.assertEqual(u"\xFF", self.unquote(u"%xff"))
        self.assertEqual(u"X\xFFZ", self.unquote(u"X%xffZ"))

    def test_unquote_zero(self):
        self.assertEqual(u"\x00", self.unquote(u"%x00"))
        self.assertEqual(u"X\x00Z", self.unquote(u"X%x00Z"))

    def test_unquote_adjacent_x(self):
        self.assertEqual(u"\x00\x01\xA0\xB0\xFF",
                         self.unquote(u"%x00%x01%xA0%xB0%xFF"))

    def test_unquote_adjacent_u(self):
        self.assertEqual(u"\u0000\u0001\u00A0\uABCD\uFEFE",
                         self.unquote(u"%u0000%u0001%u00A0%uabCD%ufeFE"))

    def test_unquote_adjacent_U(self):
        self.assertEqual(
            u"\U00000000\U00000001\U000000A0\U0001ABCD\U0001FEFE",
            self.unquote(u"%U00000000%U00000001%U000000A0%U0001abCD%U0001feFE"))

    def test_invalid_hex_digits(self):
        self.assertRaises(
            ValueError,
            self.unquote,
            u"%xgG")
        self.assertRaises(
            ValueError,
            self.unquote,
            u"%ugGGG")
        self.assertRaises(
            ValueError,
            self.unquote,
            u"%UgGGGgGGG")

    def test_invalid_too_short(self):
        self.assertRaises(
            ValueError,
            self.unquote,
            u"%x0")
        self.assertRaises(
            ValueError,
            self.unquote,
            u"%u000")
        self.assertRaises(
            ValueError,
            self.unquote,
            u"%U0000000")

    def test_invalid_too_short_no_sigil(self):
        self.assertRaises(
            ValueError,
            self.unquote,
            u"%")

    def test_empty_pathstr(self):
        # an empty path string returns an empty path tuple
        self.assertEqual(tuple(), self.pathstr2path(u""))

    def test_split(self):
        p = self.pathstr2path(u"abc.def.hik.jkl")
        self.assertEqual((u"abc", u"def", u"hik", u"jkl"), p)

    def test_split_all_empty_parts(self):
        p = self.pathstr2path(u"....")
        self.assertEqual((u"", u"", u"", u"", u""), p)

    def test_split_all_empty_tail(self):
        p = self.pathstr2path(u"1.2.")
        self.assertEqual((u"1", u"2", u""), p)

    def test_split_unquote(self):
        p = self.pathstr2path(u"a%x2Eb.c%u002Ed.e%U0000002Ef")
        self.assertEqual((u"a.b", u"c.d", u"e.f"), p)

    def test_split_unquote_with_index(self):
        p = self.pathstr2path(u"a%x2Eb.~555~.c%u002Ed.e%U0000002Ef.~6~")
        self.assertEqual((u"a.b", 555, u"c.d", u"e.f", 6), p)

    def test_split_ns_empty(self):
        self.assertEqual((None, u""), self.split_ns(u""))

    def test_split_ns_empty_parts(self):
        self.assertEqual((u"", u""), self.split_ns(u":"))

    def test_split_ns_no_ns(self):
        self.assertEqual((None, u"the-varname"), self.split_ns(u"the-varname"))

    def test_split_ns_non_quoted(self):
        self.assertEqual(
            (u"the-ns", "the-rest:with:colons|filter1|filter2"),
            self.split_ns(u"the-ns:the-rest:with:colons|filter1|filter2"))

    def test_split_ns_quoting(self):
        self.assertEqual((u":", u"%x3a"), self.split_ns(u"%x3a:%x3a"))

    def test_split_filters_empty(self):
        self.assertEqual((u"", []), self.split_filters(u"", 1))

    def test_split_filters_varname_only_no_stripping(self):
        self.assertEqual((u" varname ", []),
                         self.split_filters(u" varname ", 1))

    def test_split_filters_single_no_stripping(self):
        self.assertEqual((u" the-varname ", []),
                         self.split_filters(u" the-varname |  ", 1))

    def test_split_filters_one(self):
        self.assertEqual((u"the-varname", [u"None"]),
                         self.split_filters(u"the-varname|None", 1))

    def test_split_filters_many(self):
        self.assertEqual((u"the-varname", [u"Empty", u"None"]),
                         self.split_filters(u"the-varname|Empty|None", 1))

    def test_split_filters_many_alt(self):
        self.assertEqual((u"the-varname", [u"Empty", u"None"]),
                         self.split_filters(u"the-varname|Empty,None", 1))

    def test_split_filters_many_alt_preferred(self):
        # "," is preferred
        self.assertEqual((u"the-varname", [u"Empty|", u"None"]),
                         self.split_filters(u"the-varname|Empty|,None", 1))

    def test_None_filter_single(self):
        cfg = configmix.load()
        x = getattr(cfg, self.interpolate_meth)(u"{{non-existing|None}}")
        self.assertIsNone(x)
        # caching should have no effect
        y = getattr(cfg, self.interpolate_meth)(u"{{non-existing|None}}")
        self.assertIsNone(y)

    def test_None_filter_embedded(self):
        cfg = configmix.load()
        x = getattr(cfg, self.interpolate_meth)(u"A{{non-existing|None}}Z")
        self.assertEqual(u"AZ", x)
        # caching should have no effect
        y = getattr(cfg, self.interpolate_meth)(u"A{{non-existing|None}}Z")
        self.assertEqual(u"AZ", y)

    def test_Empty_filtersingle(self):
        cfg = configmix.load()
        x = getattr(cfg, self.interpolate_meth)(u"{{non-existing|Empty}}")
        self.assertEqual("", x)
        # caching should have no effect
        y = getattr(cfg, self.interpolate_meth)(u"{{non-existing|Empty}}")
        self.assertEqual("", y)

    def test_None_filter_pass_through(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"))
        x = getattr(cfg, self.interpolate_meth)(u"{{intl.cache.items|None}}")
        self.assertEqual(10, x)
        # caching should have no effect
        y = getattr(cfg, self.interpolate_meth)(u"{{intl.cache.items|None}}")
        self.assertEqual(10, y)

    def test_Empty_filter_pass_through(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"))
        x = getattr(cfg, self.interpolate_meth)(u"{{intl.cache.items|Empty}}")
        self.assertEqual(10, x)
        # caching should have no effect
        y = getattr(cfg, self.interpolate_meth)(u"{{intl.cache.items|Empty}}")
        self.assertEqual(10, y)

    def test_Empty_filter_no_pass_through_2(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"))
        x = getattr(cfg, self.interpolate_meth)(u"{{intl.cache.items|Empty}}{{intl.cache.items}}")
        self.assertEqual(u"1010", x)
        # caching should have no effect
        y = getattr(cfg, self.interpolate_meth)(u"{{intl.cache.items|Empty}}{{intl.cache.items}}")
        self.assertEqual(u"1010", y)

    def test_interpolate_wrong_syntax(self):
        cfg = configmix.load()
        x1 = getattr(cfg, self.interpolate_meth)(u"{{the-variable}")
        self.assertEqual(u"{{the-variable}", x1)
        x2 = getattr(cfg, self.interpolate_meth)(u"A{{the-variable}Z")
        self.assertEqual(u"A{{the-variable}Z", x2)
        x3 = getattr(cfg, self.interpolate_meth)(u"A{{1{{2{{3}Z")
        self.assertEqual(u"A{{1{{2{{3}Z", x3)
        # caching should have no effect
        y1 = getattr(cfg, self.interpolate_meth)(u"{{the-variable}")
        self.assertEqual(u"{{the-variable}", y1)
        y2 = getattr(cfg, self.interpolate_meth)(u"A{{the-variable}Z")
        self.assertEqual(u"A{{the-variable}Z", y2)
        y3 = getattr(cfg, self.interpolate_meth)(u"A{{1{{2{{3}Z")
        self.assertEqual(u"A{{1{{2{{3}Z", y3)

    def test_interpolate_empty_str(self):
        cfg = configmix.load()
        x = getattr(cfg, self.interpolate_meth)(u"")
        self.assertEqual(u"", x)
        # caching should have no effect
        y = getattr(cfg, self.interpolate_meth)(u"")
        self.assertEqual(u"", y)

    def test_interpolate_no_interpolation(self):
        cfg = configmix.load()
        x = getattr(cfg, self.interpolate_meth)(u"no-interpolation-here")
        self.assertEqual(u"no-interpolation-here", x)
        # caching should have no effect
        y = getattr(cfg, self.interpolate_meth)(u"no-interpolation-here")
        self.assertEqual(u"no-interpolation-here", y)

    def test_single_load_removes_DEL_VALUE(self):
        cfg = configmix.load(os.path.join(TESTDATADIR, "conf20.yml"))
        self.assertRaises(
            KeyError,
            cfg.getvar,
            u("not-deleted"))

    def test_single_safeload_removes_DEL_VALUE(self):
        cfg = configmix.safe_load(os.path.join(TESTDATADIR, "conf20.yml"))
        self.assertRaises(
            KeyError,
            cfg.getvar,
            u("not-deleted"))

    def test_never_expanding_lone_DEL_VALUE(self):
        cfg = {
            u("key1"): u("{{::DEL::}}"),
            u("subkey2"): {
                u("key3"): u("{{::DEL::}}")
            }
        }
        cfg = configmix.config.Configuration(cfg)
        print(repr(cfg))
        self.assertEqual(u("{{::DEL::}}"), cfg.getvar_s(u("key1")))
        self.assertEqual(u("{{::DEL::}}"), cfg.getvar_s(u("subkey2.key3")))

    def test_merge_does_never_expand(self):
        cfg1 = configmix.load(os.path.join(TESTDATADIR, "conf20.yml"))
        self.assertEqual(u("{{appdir}}/locale"), cfg1.getvar(u("intl.localedir")))
        self.assertRaises(
            KeyError,
            cfg1.getvar_s,
            u("intl.localedir"))

        cfg2 = {
            u("process"): u("{{::DEL::}}"),
            u("intl"): {
                u("localedir"): u("{{appdir}}/other-locale"),
                u("cache"): u("{{::DEL::}}")
            }
        }
        cfg = configmix.merge(configmix.config.Configuration(cfg2), cfg1)
        self.assertEqual(
            u("{{appdir}}/other-locale"),
            cfg.getvar(u("intl.localedir")))
        self.assertRaises(
            KeyError,
            cfg.getvar,
            u("process"))
        self.assertRaises(
            KeyError,
            cfg.getvar,
            u("intl.cache"))

    def test_safemerge_does_never_expand(self):
        cfg1 = configmix.safe_load(os.path.join(TESTDATADIR, "conf20.yml"))
        self.assertEqual(u("{{appdir}}/locale"), cfg1.getvar(u("intl.localedir")))
        self.assertRaises(
            KeyError,
            cfg1.getvar_s,
            u("intl.localedir"))

        cfg2 = {
            u("process"): u("{{::DEL::}}"),
            u("intl"): {
                u("localedir"): u("{{appdir}}/other-locale"),
                u("cache"): u("{{::DEL::}}")
            }
        }
        cfg = configmix.safe_merge(configmix.config.Configuration(cfg2), cfg1)
        self.assertEqual(
            u("{{appdir}}/other-locale"),
            cfg.getvar(u("intl.localedir")))
        self.assertRaises(
            KeyError,
            cfg.getvar,
            u("process"))
        self.assertRaises(
            KeyError,
            cfg.getvar,
            u("intl.cache"))

    def test_nested_filter_wrong_syntax(self):
        cfg = configmix.load()
        try:
            y = getattr(cfg, self.interpolate_meth)(u"{{|{{not-existing}}|None}}")
        except ValueError:
            pass
        else:
            self.fail("`ValueError' should have been raised")

    def test_nested_filter_None(self):
        cfg = configmix.load()
        y = getattr(cfg, self.interpolate_meth)(u"{{|{{not-existing}}|None|}}")
        self.assertTrue(y is None)

    def test_nested_filter_empty(self):
        cfg = configmix.load()
        y = getattr(cfg, self.interpolate_meth)(u"{{|{{not-existing}}|Empty|}}")
        self.assertEqual(u(""), y)

    def test_nested_nested_filter_empty(self):
        cfg = configmix.load()
        y = getattr(cfg, self.interpolate_meth)(u"{{|pre-{{not-existing|Empty}}-post|upper|}}")
        self.assertEqual(u("PRE--POST"), y)

    def test_nested_filter_upper(self):
        cfg1 = {
            u("key-1"): u("Value for key-1")
        }
        cfg = cfg = configmix.config.Configuration(cfg1)
        y = getattr(cfg, self.interpolate_meth)(u"{{|pre_{{key-1}}_post|upper|}}")
        self.assertEqual(u("PRE_VALUE FOR KEY-1_POST"), y)

    def test_nested_filter_normpath(self):
        p1 = u(os.path.join("a", "b", "c"))
        cfg1 = {
            u("path1"): p1,
            u("path2"): u("{{|{{path1}}/../d|normpath|}}")
        }
        cfg = cfg = configmix.config.Configuration(cfg1)
        np = cfg.getvarl_s(u("path2"))
        self.assertEqual(
            os.path.normpath(u(os.path.join("a", "b", "d"))),
            np)

    def test_nested_filter_normpath_posixpath(self):
        p1 = u(os.path.join("a", "b", "c"))
        cfg1 = {
            u("path1"): p1,
            u("path2"): u("{{|{{path1}}/../d|normpath,posixpath|}}")
        }
        cfg = cfg = configmix.config.Configuration(cfg1)
        np = cfg.getvarl_s(u("path2"))
        self.assertEqual(
            u(os.path.join("a", "b", "d")),
            np)

    def test_nested_filter_normpath_posixpath_2(self):
        p1 = u(os.path.join("a", "b", "C", "d"))
        cfg1 = {
            u("path1"): p1,
            u("path2"): u("{{|{{path1}}/../e|normpath,posixpath|}}")
        }
        cfg = cfg = configmix.config.Configuration(cfg1)
        np = cfg.getvarl_s(u("path2"))
        self.assertEqual(
            u(os.path.join("a", "b", "C", "e")),
            np)

    def test_nested_filter_normpath_posixpath_upper(self):
        p1 = u(os.path.join("a", "b", "C", "d"))
        cfg1 = {
            u("path1"): p1,
            u("path2"): u("{{|{{path1}}/../e|normpath,posixpath,upper|}}")
        }
        cfg = cfg = configmix.config.Configuration(cfg1)
        np = cfg.getvarl_s(u("path2"))
        self.assertEqual(
            u(os.path.join("A", "B", "C", "E")),
            np)


class T09Parser(_TParserMixin, unittest.TestCase):

    def setUp(self):
        self.unquote = configmix.config.py_unquote
        self.quote = configmix.config.py_quote
        self.pathstr2path = configmix.config.py_pathstr2path
        self.split_ns = configmix.config._py_split_ns
        self.split_filters = configmix.config._py_split_filters
        self.interpolate_meth = "py_interpolate_variables"

    def test_split_ns_wrong_type(self):
        self.assertRaises(
            AttributeError,    # no .partition
            self.split_ns,
            1)

    def test_quote_wrong_type(self):
        self.assertRaises(
            AttributeError,    # no .lstrip
            self.quote,
            1.0)

    def test_unquote_wrong_type(self):
        self.assertRaises(
            TypeError,    # argument of type "int" is not iterable
            self.unquote,
            1)


if configmix.config.fast_unquote is not None:
    class T10FastParser(_TParserMixin, unittest.TestCase):

        def setUp(self):
            self.unquote = configmix.config.fast_unquote
            self.quote = configmix.config.fast_quote
            self.pathstr2path = configmix.config.fast_pathstr2path
            self.split_ns = configmix.config._fast_split_ns
            self.split_filters = configmix.config._fast_split_filters
            self.interpolate_meth = "fast_interpolate_variables"

        def test_split_ns_wrong_type(self):
            self.assertRaises(
                TypeError,
                self.split_ns,
                b":")

        def test_quote_wrong_type(self):
            self.assertRaises(
                TypeError,
                self.quote,
                b":")

        def test_unquote_wrong_type(self):
            self.assertRaises(
                TypeError,
                self.unquote,
                b":")


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