import copy


def configurable(cls):
    """
    This class decorator will add the _read_config method to any class.
    _read_config() will take a struct and expand its contents into object
    attributes. The primary use of this is in conjunction with parsed
    config files or ROS parameters.

    A class attribute struct 'defaults' defines the set of admissible
    parameters and their default values. This means that ALL parameters
    are optional.

    Example:

        @configurable
        class Foo:
            defaults = {
                'bar': 'hoge',
                'opt': 42,
            }
            def __init__(self, cfg={}):
                self._read_config(cfg)

        # this will assign
        #   f.bar = "baz"
        #   f.opt = 42
        f = Foo({'foo': 37, 'bar': 'baz'})

    """
    class Configurable(cls):
        defaults = getattr(cls, 'defaults', {})

        def _read_config(self, cfg):
            for c in self.defaults:
                if c in cfg:
                    setattr(self, c, cfg[c])
                else:
                    setattr(self, c, copy.deepcopy(self.defaults[c]))
    return Configurable