<commit_before>import collections

import six
import yaml

from lymph.utils import import_object


class ConfigView(collections.Mapping):
    def __init__(self, config, path):
        self.root = config
        self.path = path

    def __getitem__(self, key):
        return self.get(key)

    def __len__(self):
        return len(self.root.get_raw(self.path))

    def get(self, key, default=None):
        return self.root.get('%s.%s' % (self.path, key), default)

    def set(self, key, value):
        return self.root.set('%s.%s' % (self.path, key), value)

    def setdefault(self, key, value):
        return self.root.setdefault('%s.%s' % (self.path, key), value)

    def __iter__(self):
        return iter(self.root.get_raw(self.path))


class Configuration(object):
    def __init__(self, values=None):
        self.values = values or {}
        self._instances_cache = {}

    def load_file(self, filename, sections=None):
        with open(filename, 'r') as f:
            self.load(f, sections=sections)

    def load(self, f, sections=None):
        for section, values in six.iteritems(yaml.load(f)):
            if sections is None or section in sections:
                self.values[section] = values

    def update(self, data):
        self.values.update(data)

    def set(self, key, data):
        path = key.split('.')
        values = self.values
        for bit in path[:-1]:
            new_values = values.setdefault(bit, {})
            if new_values is None:
                values[bit] = {}
                values = values[bit]
            else:
                values = new_values
        values[path[-1]] = data

    def setdefault(self, key, default):
        value = self.get(key)
        if value is None:
            self.set(key, default)
            return default
        return value

    def create_instance(self, key, default_class=None, **kwargs):
        config = self.get(key, {})
        path = config.get('class', default_class)
        cls = import_object(path)
        return cls.from_config(config, **kwargs)

    def get_instance(self, key, default_class=None, **kwargs):
        instance = self._instances_cache.get(key)
        if not instance:
            instance = self.create_instance(
                key, default_class=default_class, **kwargs)
            self._instances_cache[key] = instance
        return instance

    def get_raw(self, key, default=None):
        path = key.split('.')
        values = self.values
        for bit in path[:-1]:
            values = values[bit]
            if values is None:
                raise KeyError
        return values[path[-1]]

    def get(self, key, default=None):
        try:
            value = self.get_raw(key)
        except KeyError:
            return default
        if isinstance(value, dict):
            value = ConfigView(self, key)
        return value

    def __repr__(self):
        return "lymph.config.Configuration(values={values})".format(
            values=self.values)
<commit_msg>Added Configuration.root property to unify ConfigView and Configuration interfaces<commit_after>import collections

import six
import yaml

from lymph.utils import import_object


class ConfigView(collections.Mapping):
    def __init__(self, config, path):
        self.root = config
        self.path = path

    def __getitem__(self, key):
        return self.get(key)

    def __len__(self):
        return len(self.root.get_raw(self.path))

    def get(self, key, default=None):
        return self.root.get('%s.%s' % (self.path, key), default)

    def set(self, key, value):
        return self.root.set('%s.%s' % (self.path, key), value)

    def setdefault(self, key, value):
        return self.root.setdefault('%s.%s' % (self.path, key), value)

    def __iter__(self):
        return iter(self.root.get_raw(self.path))


class Configuration(object):
    def __init__(self, values=None):
        self.values = values or {}
        self._instances_cache = {}

    @property
    def root(self):
        return self

    def load_file(self, filename, sections=None):
        with open(filename, 'r') as f:
            self.load(f, sections=sections)

    def load(self, f, sections=None):
        for section, values in six.iteritems(yaml.load(f)):
            if sections is None or section in sections:
                self.values[section] = values

    def update(self, data):
        self.values.update(data)

    def set(self, key, data):
        path = key.split('.')
        values = self.values
        for bit in path[:-1]:
            new_values = values.setdefault(bit, {})
            if new_values is None:
                values[bit] = {}
                values = values[bit]
            else:
                values = new_values
        values[path[-1]] = data

    def setdefault(self, key, default):
        value = self.get(key)
        if value is None:
            self.set(key, default)
            return default
        return value

    def create_instance(self, key, default_class=None, **kwargs):
        config = self.get(key, {})
        path = config.get('class', default_class)
        cls = import_object(path)
        return cls.from_config(config, **kwargs)

    def get_instance(self, key, default_class=None, **kwargs):
        instance = self._instances_cache.get(key)
        if not instance:
            instance = self.create_instance(
                key, default_class=default_class, **kwargs)
            self._instances_cache[key] = instance
        return instance

    def get_raw(self, key, default=None):
        path = key.split('.')
        values = self.values
        for bit in path[:-1]:
            values = values[bit]
            if values is None:
                raise KeyError
        return values[path[-1]]

    def get(self, key, default=None):
        try:
            value = self.get_raw(key)
        except KeyError:
            return default
        if isinstance(value, dict):
            value = ConfigView(self, key)
        return value

    def __repr__(self):
        return "lymph.config.Configuration(values={values})".format(
            values=self.values)

{'fraction_non_alphanumeric': 0.1137987012987013, 'fraction_numerical': 0.0012987012987012987, 'mean_word_length': 2.1210739614994933, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4576607', 'n_tokens_mistral': 1806, 'n_tokens_neox': 1749, 'n_words': 463}