Пример #1
0
 def test_attribute_expand_env(self) -> None:
     """Test transparent environment variable expansion."""
     os.environ['CMDKIT_TEST_A'] = 'foo-bar'
     ns = Namespace({'test_env': 'CMDKIT_TEST_A'})
     assert ns.get('test') is None
     assert ns.get('test_env') == 'CMDKIT_TEST_A'
     assert ns.test == 'foo-bar'
Пример #2
0
 def test_set(self) -> None:
     """Test Namespace[] setter."""
     ns = Namespace()
     for key, value in zip(KEYS, VALUES):
         ns[key] = value
     assert list(ns.keys()) == list(DATA.keys())
     assert list(ns.values()) == list(DATA.values())
Пример #3
0
    def test_duplicates(self) -> None:
        """Configuration can find duplicate leaves in the trees."""

        one = Namespace({'a': {'x': 1, 'y': 2}, 'b': {'x': 3, 'z': 4}})
        two = Namespace({'b': {'x': 4, 'z': 2}, 'c': {'j': True, 'k': 3.14}})
        cfg = Configuration(one=one, two=two)

        assert cfg.duplicates() == {'x': {'one': [('a',), ('b',)], 'two': [('b',)]},
                                    'z': {'one': [('b',)], 'two': [('b',)]}}
Пример #4
0
    def test_popitem(self) -> None:
        """Configuration cannot use inherited popitem method."""

        one = Namespace({'a': {'x': 1, 'y': 2}, 'b': {'x': 3, 'z': 4}})
        two = Namespace({'b': {'x': 4, 'z': 2}, 'c': {'j': True, 'k': 3.14}})
        cfg = Configuration(one=one, two=two)

        with pytest.raises(NotImplementedError):
            cfg.popitem()
Пример #5
0
    def test_whereis(self) -> None:
        """Configuration can find paths to leaves in the tree."""

        one = Namespace({'a': {'x': 1, 'y': 2}, 'b': {'x': 3, 'z': 4}})
        two = Namespace({'b': {'x': 4}, 'c': {'j': True, 'k': 3.14}})
        cfg = Configuration(one=one, two=two)

        assert cfg.whereis('x') == {'one': [('a',), ('b',)], 'two': [('b',)]}
        assert cfg.whereis('x', 1) == {'one': [('a',)], 'two': []}
        assert cfg.whereis('x', lambda v: v % 3 == 0) == {'one': [('b',)], 'two': []}
Пример #6
0
    def run(self) -> None:
        """Business logic for `refitt config get`."""

        path = PATH[SITE].config
        for site in ('local', 'user', 'system'):
            if getattr(self, site) is True:
                path = PATH[site].config

        if not os.path.exists(path):
            raise RuntimeError(f'{path} does not exist')

        config = Namespace.from_local(path)

        if self.varpath == '.':
            self.print_result(config)
            return

        if '.' not in self.varpath:
            if self.varpath in config:
                self.print_result(config[self.varpath])
                return
            else:
                raise RuntimeError(f'"{self.varpath}" not found in {path}')

        if self.varpath.startswith('.'):
            raise RuntimeError(f'section name cannot start with "."')

        section, *subsections, variable = self.varpath.split('.')
        if section not in config:
            raise RuntimeError(f'"{section}" is not a section')

        config_section = config[section]
        if subsections:
            subpath = f'{section}'
            try:
                for subsection in subsections:
                    subpath += f'.{subsection}'
                    if not isinstance(config_section[subsection], Mapping):
                        raise RuntimeError(
                            f'"{subpath}" not a section in {path}')
                    else:
                        config_section = config_section[subsection]
            except KeyError as error:
                raise RuntimeError(
                    f'"{subpath}" not found in {path}') from error

        if self.expand:
            try:
                value = getattr(config_section, variable)
            except ValueError as error:
                raise RuntimeError(*error.args) from error
            if value is None:
                raise RuntimeError(f'"{variable}" not found in {path}')
            self.print_result(value)
            return

        if variable not in config_section:
            raise RuntimeError(f'"{self.varpath}" not found in {path}')

        self.print_result(config_section[variable])
Пример #7
0
    def test_to_local(self) -> None:
        """Test Namespace.to_local dispatch method."""

        # test round trip
        for ftype in FACTORIES:
            ns = Namespace(TEST_DICT)
            ns.to_local(f'{TMPDIR}/{ftype}.{ftype}')
            assert ns == Namespace.from_local(f'{TMPDIR}/{ftype}.{ftype}')

        # test not implemented
        with pytest.raises(NotImplementedError):
            ns = Namespace(TEST_DICT)
            ns.to_local(f'{TMPDIR}/config.special')
Пример #8
0
    def test_init(self) -> None:
        """Test environment variable initialization along with Environ.reduce()."""

        # clean environment of any existing variables with the item
        prefix = 'CMDKIT'
        for var in dict(os.environ):
            if var.startswith(prefix):
                os.environ.pop(var)

        # populate environment with test variables
        for line in TEST_ENV.strip().split('\n'):
            field, value = line.strip().split('=')
            os.environ[field] = value

        # test base level Namespace|Environ equivalence
        assert Namespace.from_env(prefix=prefix) == Environ(prefix=prefix)
        assert Environ(prefix=prefix).expand() == Namespace(TEST_DICT)
Пример #9
0
    def test_deep_inplace_assignment(self) -> None:
        """Test Namespace attribute access behavior."""

        ns = Namespace({'a': {'x': 1, 'y': 2}})
        assert isinstance(ns.a, Namespace)
        assert ns.a == {'x': 1, 'y': 2}
        assert ns.a.x == 1 and ns.a.y == 2

        ns.a.x = 3
        assert ns.a.x == 3
Пример #10
0
    def test_update_complex(self) -> None:
        """Test Namespace.update() behavior."""

        ns = Namespace({'a': {'x': 1, 'y': 2}})

        ns.update({'b': {'z': 3}})
        assert ns == {'a': {'x': 1, 'y': 2}, 'b': {'z': 3}}

        ns.update({'a': {'z': 3}})
        assert ns == {'a': {'x': 1, 'y': 2, 'z': 3}, 'b': {'z': 3}}

        ns.update({'a': {'x': 5}})
        assert ns == {'a': {'x': 5, 'y': 2, 'z': 3}, 'b': {'z': 3}}
Пример #11
0
    def test_from_local(self) -> None:
        """Test Configuration.from_local factory method."""

        # initial local files
        for label, data in CONFIG_SOURCES.items():
            with open(f'{TMPDIR}/{label}.toml', mode='w') as output:
                output.write(data)

        # clean environment of any existing variables with the item
        prefix = 'CMDKIT'
        for var in dict(os.environ):
            if var.startswith(prefix):
                os.environ.pop(var)

        # populate environment with test variables
        for line in CONFIG_ENVIRON.strip().split('\n'):
            field, value = line.strip().split('=')
            os.environ[field] = value

        # build configuration
        default = Namespace.from_toml(StringIO(CONFIG_DEFAULT))
        cfg = Configuration.from_local(default=default, env=True, prefix=prefix,
                                       system=f'{TMPDIR}/system.toml',
                                       user=f'{TMPDIR}/user.toml',
                                       local=f'{TMPDIR}/local.toml')

        # verify namespace isolation
        assert cfg.namespaces['default'] == Namespace.from_toml(StringIO(CONFIG_DEFAULT))
        assert cfg.namespaces['system'] == Namespace.from_toml(StringIO(CONFIG_SYSTEM))
        assert cfg.namespaces['user'] == Namespace.from_toml(StringIO(CONFIG_USER))
        assert cfg.namespaces['local'] == Namespace.from_toml(StringIO(CONFIG_LOCAL))
        assert cfg.namespaces['env'] == Environ(prefix).reduce()

        # verify parameter lineage
        assert cfg['a']['var0'] == 'default_var0' and cfg.which('a', 'var0') == 'default'
        assert cfg['a']['var1'] == 'system_var1' and cfg.which('a', 'var1') == 'system'
        assert cfg['a']['var2'] == 'user_var2' and cfg.which('a', 'var2') == 'user'
        assert cfg['b']['var3'] == 'local_var3' and cfg.which('b', 'var3') == 'local'
        assert cfg['c']['var4'] == 'env_var4' and cfg.which('c', 'var4') == 'env'
        assert cfg['c']['var5'] == 'env_var5' and cfg.which('c', 'var5') == 'env'
Пример #12
0
 def test_update_simple(self) -> None:
     """Test Namespace.update() behavior"""
     ns = Namespace()
     for i, (key, value) in enumerate(DATA.items()):
         ns.update({key: value})
     assert list(ns.keys()) == list(DATA.keys())
     assert list(ns.values()) == list(DATA.values())
Пример #13
0
 def test_setdefault(self) -> None:
     """Test Namespace.setdefault()."""
     ns = Namespace()
     for key, value in zip(KEYS, VALUES):
         ns.setdefault(key, value)
     assert list(ns.keys()) == list(DATA.keys())
     assert list(ns.values()) == list(DATA.values())
Пример #14
0
    def test_blending(self) -> None:
        """Test that configuration applied depth-first blending of namespaces."""

        ns_a = Namespace({'a': {'x': 1, 'y': 2}})
        ns_b = Namespace({'b': {'z': 3}})  # new section
        ns_c = Namespace({'a': {'z': 4}})  # new value in existing section
        ns_d = Namespace({'a': {'x': 5}})  # altered value in existing section

        # configuration blends nested namespaces
        cfg = Configuration(A=ns_a, B=ns_b, C=ns_c, D=ns_d)

        # confirm separate namespaces
        assert cfg.namespaces['A'] == ns_a
        assert cfg.namespaces['B'] == ns_b
        assert cfg.namespaces['C'] == ns_c
        assert cfg.namespaces['D'] == ns_d

        # confirm d << c << b << a look up
        assert cfg['a']['x'] == 5
        assert cfg['a']['y'] == 2
        assert cfg['a']['z'] == 4
        assert cfg['b']['z'] == 3
Пример #15
0
    def test_factories(self) -> None:
        """Test all implemented factory equivalents."""

        # write all test data to local files
        for ftype, data in FACTORIES.items():
            with open(f'{TMPDIR}/{ftype}.{ftype}', mode='w') as output:
                output.write(data)

        # test both file-like object and file path modes of construction
        assert (Namespace(TEST_DICT) ==
                Namespace.from_toml(StringIO(TEST_TOML)) == Namespace.from_toml(f'{TMPDIR}/toml.toml') ==
                Namespace.from_yaml(StringIO(TEST_YAML)) == Namespace.from_yaml(f'{TMPDIR}/yaml.yaml') ==
                Namespace.from_json(StringIO(TEST_JSON)) == Namespace.from_json(f'{TMPDIR}/json.json'))
Пример #16
0
    def test_flatten(self) -> None:
        """Test round-trip Environ('...').expand().flatten()."""

        # clean environment of any existing variables with the item
        prefix = 'CMDKIT'
        for var in dict(os.environ):
            if var.startswith(prefix):
                os.environ.pop(var)

        # populate environment with test variables
        for line in TEST_ENV_TYPES.strip().split('\n'):
            field, value = line.strip().split('=')
            os.environ[field] = value

        env = Namespace.from_env(prefix)
        assert env == env.expand().flatten(prefix=prefix)
Пример #17
0
def update_config(site: str, data: dict) -> None:
    """
    Extend the current configuration and commit it to disk.

    Parameters:
        site (str):
            Either "local", "user", or "system"
        data (dict):
            Sectioned mappable to update configuration file.

    Example:
        >>> update_config('user', {
        ...    'database': {
        ...        'user': '******'
        ...    }
        ... })
    """
    init_config(site)
    new_config = Namespace.from_local(CONF_PATH[site])
    new_config.update(data)
    new_config.to_local(CONF_PATH[site])
Пример #18
0
def update_config(site: str, data: dict) -> None:
    """
    Extend the current configuration and commit it to disk.

    Args:
        site (str):
            Either "local", "user", or "system"
        data (dict):
            Sectioned mappable to update configuration file.

    Example:
        >>> update_config('user', {
        ...    'database': {
        ...        'user': '******'
        ...    }
        ... })
    """
    path = get_site(site).config
    new_config = Namespace.from_local(path, ignore_if_missing=True)
    new_config.update(data)
    new_config.to_local(path)
Пример #19
0
    def test_live_update(self) -> None:
        """Test direct modification of configuration data."""

        cfg = Configuration(a=Namespace(x=1))
        assert repr(cfg) == 'Configuration(a=Namespace({\'x\': 1}))'
        assert dict(cfg) == {'x': 1}
        assert cfg.which('x') == 'a'

        cfg.x = 2
        assert repr(cfg) == 'Configuration(a=Namespace({\'x\': 1}), _=Namespace({\'x\': 2}))'
        assert dict(cfg) == {'x': 2}
        assert cfg.which('x') == '_'

        cfg.update(y=2)
        assert dict(cfg) == {'x': 2, 'y': 2}
        assert repr(cfg) == 'Configuration(a=Namespace({\'x\': 1}), _=Namespace({\'x\': 2, \'y\': 2}))'
        assert cfg.which('y') == '_'

        cfg.update(y=3)
        assert dict(cfg) == {'x': 2, 'y': 3}
        assert repr(cfg) == 'Configuration(a=Namespace({\'x\': 1}), _=Namespace({\'x\': 2, \'y\': 3}))'
        assert cfg.which('y') == '_'
Пример #20
0
    def test_init(self) -> None:
        """Test initialization."""

        # test namespaces
        ns_a = Namespace(TEST_DICT)
        ns_b = Namespace.from_toml(StringIO(TEST_TOML))
        ns_c = Namespace.from_yaml(StringIO(TEST_YAML))
        ns_d = Namespace.from_json(StringIO(TEST_JSON))

        # initialize construction results in member namespaces
        cfg = Configuration(A=ns_a, B=ns_b)
        assert dict(cfg) == cfg.namespaces['A'] == ns_a == cfg.namespaces['B'] == ns_b

        # extend the configuration
        cfg.extend(C=ns_c, D=ns_d)
        assert (cfg.namespaces['A'] == ns_a ==
                cfg.namespaces['B'] == ns_b ==
                cfg.namespaces['C'] == ns_c ==
                cfg.namespaces['D'] == ns_d ==
                dict(cfg))

        # keys() and values() are available
        assert list(cfg.keys()) == list(ns_a.keys())
        assert list(cfg.values()) == list(ns_a.values())
Пример #21
0
 def test_attribute_expand_eval(self) -> None:
     """Test transparent shell expression expansion."""
     ns = Namespace({'test_eval': 'echo foo-bar'})
     assert ns.get('test') is None
     assert ns.get('test_eval') == 'echo foo-bar'
     assert ns.test == 'foo-bar'
Пример #22
0
from cmdkit.config import Namespace, Configuration, ConfigurationError  # noqa: unused

# internal libs
from ..assets import load_asset

# initialize module level logger
log = logging.getLogger(__name__)

# environment variables and configuration files are automatically
# depth-first merged with defaults
DEFAULT: Namespace = Namespace({
    'database': {
        'backend': 'sqlite',
        'database': ':memory:'
    },
    'logging': {
        'level': 'warning',
        'format':
        '%(asctime)s %(hostname)s %(levelname)-8s [%(name)s] %(msg)s',
        'datefmt': '%Y-%m-%d %H:%M:%S'
    }
})

CWD: str = os.getcwd()
HOME: str = os.getenv('HOME')
if os.name == 'nt':
    ROOT: bool = ctypes.windll.shell32.IsUserAnAdmin() == 1
    SITE: str = 'system' if ROOT else 'user'
    ROOT_SITE: str = os.path.join(os.getenv('ProgramData'), 'StreamKit')
    USER_SITE: str = os.path.join(os.getenv('AppData'), 'StreamKit')
else:
    ROOT: bool = os.getuid() == 0
Пример #23
0
 def test_get(self) -> None:
     """Test Namespace[], Namespace.get()."""
     ns = Namespace(DATA)
     for key, value in zip(KEYS, VALUES):
         assert ns[key] == ns.get(key) == value
         assert ns.get(f'{key}_', False) is False
Пример #24
0
 def test_iterate(self) -> None:
     """Test that Namespace can use [] syntax."""
     ns = Namespace(DATA)
     for i, (key, value) in enumerate(ns.items()):
         assert key == KEYS[i]
         assert value == VALUES[i]
Пример #25
0
    def test_from_local(self) -> None:
        """Test automatic file type deduction and allow for missing files."""

        # clear existing files
        for ftype in FACTORIES:
            filepath = f'{TMPDIR}/{ftype}.{ftype}'
            if os.path.exists(filepath):
                os.remove(filepath)

        with pytest.raises(FileNotFoundError):
            Namespace.from_local(f'{TMPDIR}/toml.toml')
        with pytest.raises(FileNotFoundError):
            Namespace.from_local(f'{TMPDIR}/yaml.yaml')
        with pytest.raises(FileNotFoundError):
            Namespace.from_local(f'{TMPDIR}/json.json')

        assert (Namespace() ==
                Namespace.from_local(f'{TMPDIR}/toml.toml', ignore_if_missing=True) ==
                Namespace.from_local(f'{TMPDIR}/yaml.yaml', ignore_if_missing=True) ==
                Namespace.from_local(f'{TMPDIR}/json.json', ignore_if_missing=True))

        with pytest.raises(NotImplementedError):
            Namespace.from_local(f'{TMPDIR}/config.special')

        # write all test data to local files
        for ftype, data in FACTORIES.items():
            with open(f'{TMPDIR}/{ftype}.{ftype}', mode='w') as output:
                output.write(data)

        assert (Namespace(TEST_DICT) ==
                Namespace.from_local(f'{TMPDIR}/toml.toml') == Namespace.from_local(f'{TMPDIR}/tml.tml') ==
                Namespace.from_local(f'{TMPDIR}/yaml.yaml') == Namespace.from_local(f'{TMPDIR}/yml.yml') ==
                Namespace.from_local(f'{TMPDIR}/json.json'))
Пример #26
0
 def test_attribute(self) -> None:
     """Test attribute access is the same as getitem."""
     ns = Namespace({'a': 1, 'b': 'foo', 'c': {'x': 3.14}})
     assert 1 == ns['a'] == ns.a
     assert 'foo' == ns['b'] == ns.b
     assert 3.14 == ns['c']['x'] == ns.c.x
Пример #27
0
 def test_keys_and_values(self) -> None:
     """Ensure .keys() functions as expected."""
     assert list(Namespace(DATA).keys()) == list(DATA.keys())
     assert list(Namespace(DATA).values()) == list(DATA.values())
Пример #28
0
 def test_whereis(self) -> None:
     """Namespace can find paths to leaves in the tree."""
     ns = Namespace({'a': {'x': 1, 'y': 2}, 'b': {'x': 3, 'z': 4}})
     assert ns.whereis('x') == [('a',), ('b',)]
     assert ns.whereis('x', 1) == [('a',)]
     assert ns.whereis('x', lambda v: v % 3 == 0) == [('b',)]
Пример #29
0
def test_shared_group_option(capsys) -> None:
    app = SharedGroup.from_cmdline([CMD2, 'foo', '--debug'])
    assert isinstance(app, ApplicationGroup)
    assert app.command == CMD2
    assert app.shared == Namespace({'opt': False})
    assert app.cmdline == ['foo', '--debug']
Пример #30
0
 def test_duplicates(self) -> None:
     """Namespace can find duplicate leaves in the tree."""
     ns = Namespace({'a': {'x': 1, 'y': 2}, 'b': {'x': 3, 'z': 4}})
     assert ns.duplicates() == {'x': [('a',), ('b',)]}