# Path: houston/configuration.py
# class Configuration(object, metaclass=ConfigurationMeta):
#     @classmethod
#     def from_dict(cls: Type['Configuration'],
#                   dkt: Dict[str, Any]
#                   ) -> 'Configuration':
#         return cls(**dkt)
# 
#     def __init__(self, *args, **kwargs) -> None:
#         cls_name = self.__class__.__name__
#         options = self.__class__.options  # type: FrozenSet[Option]
# 
#         # were any positional arguments passed to the constructor?
#         if args:
#             msg = "constructor [{}] accepts no positional arguments but {} {} given"  # noqa: pycodestyle
#             msg = msg.format(cls_name,
#                              "was" if len(args) == 1 else "were",
#                              len(args))
#             raise TypeError(msg)
# 
#         # set values for each option
#         for opt in options:
#             try:
#                 val = kwargs[opt.name]
#             except KeyError:
#                 msg = "missing keyword argument [{}] to constructor [{}]"
#                 msg = msg.format(opt.name, cls_name)
#                 raise TypeError(msg)
#             setattr(self, opt._field, val)
# 
#         # were any unexpected keyword arguments provided?
#         if len(kwargs) > len(options):
#             actual_args = set(n for n in kwargs)
#             expected_args = set(opt.name for opt in options)
#             unexpected_arguments = list(actual_args - expected_args)
#             msg = "unexpected keyword arguments [{}] supplied to constructor [{}]"  # noqa: pycodestyle
#             msg = msg.format('; '.join(unexpected_arguments), cls_name)
#             raise TypeError(msg)
# 
#     def __getitem__(self, name: str) -> Any:
#         # FIXME use frozendict
#         try:
#             options = self.__class__.options
#             opt = next(opt for opt in options if opt.name == name)
#         except StopIteration:
#             msg = "no option [{}] in state [{}]"
#             msg.format(name, self.__class__.__name__)
#             raise KeyError(msg)
#         return getattr(self, opt._field)
# 
#     def __hash__(self) -> int:
#         all_opts = [self[opt.name] for opt in self.__class__.options]
#         all_opts.insert(0, self.__class__.__name__)
#         return hash(tuple(all_opts))
# 
#     def __eq__(self, other: 'Configuration') -> bool:
#         if type(self) != type(other):
#             msg = "illegal comparison of configurations: [{}] vs. [{}]"
#             msg = msg.format(self.__class__.__name__, other.__class__.__name__)
#             raise exceptions.HoustonException(msg)
#         return self.__dict__ == other.__dict__
# 
#     def to_dict(self) -> Dict[str, Any]:
#         fields = {}  # type: Dict[str, Any]
#         for opt in self.__class__.options:
#             fields[opt.name] = getattr(self, opt._field)
#         return fields
# 
#     def __repr__(self) -> str:
#         fields = self.to_dict()
#         for (name, val) in fields.items():
#             if isinstance(val, float):
#                 s = "{:.3f}".format(val)
#             else:
#                 s = str(val)
#             fields[name] = val
#         s = '; '.join(["{}: {}".format(k, v) for (k, v) in fields.items()])
#         s = "{}({})".format(self.__class__.__name__, s)
#         return s
# 
# def option(*args, **kwargs) -> Option.Builder:
#     return Option.Builder(*args, **kwargs)


import pytest
from houston.configuration import Configuration, option

    c1 = X(foo=0)
    c2 = X(foo=1)
    c3 = X(foo=0)
    c4 = X(foo=1)

    s = {c1, c2, c3, c4}
    assert s == {X(foo=0), X(foo=1)}

    c5 = Y(foo=0)
    assert len({c1, c5}) == 2
    with pytest.raises(Exception):
        assert c1 != c5



def test_is_frozen():
    class X(Configuration):
        foo = option(int)
    conf = X(foo=0)
    with pytest.raises(AttributeError):
        conf.foo = 10
        pytest.fail("expected AttributeError (can't set foo)")


def test_eq():
    class X(Configuration):
        foo = option(int)
        bar = option(int)


# Path: houston/configuration.py
# class Configuration(object, metaclass=ConfigurationMeta):
#     @classmethod
#     def from_dict(cls: Type['Configuration'],
#                   dkt: Dict[str, Any]
#                   ) -> 'Configuration':
#         return cls(**dkt)
# 
#     def __init__(self, *args, **kwargs) -> None:
#         cls_name = self.__class__.__name__
#         options = self.__class__.options  # type: FrozenSet[Option]
# 
#         # were any positional arguments passed to the constructor?
#         if args:
#             msg = "constructor [{}] accepts no positional arguments but {} {} given"  # noqa: pycodestyle
#             msg = msg.format(cls_name,
#                              "was" if len(args) == 1 else "were",
#                              len(args))
#             raise TypeError(msg)
# 
#         # set values for each option
#         for opt in options:
#             try:
#                 val = kwargs[opt.name]
#             except KeyError:
#                 msg = "missing keyword argument [{}] to constructor [{}]"
#                 msg = msg.format(opt.name, cls_name)
#                 raise TypeError(msg)
#             setattr(self, opt._field, val)
# 
#         # were any unexpected keyword arguments provided?
#         if len(kwargs) > len(options):
#             actual_args = set(n for n in kwargs)
#             expected_args = set(opt.name for opt in options)
#             unexpected_arguments = list(actual_args - expected_args)
#             msg = "unexpected keyword arguments [{}] supplied to constructor [{}]"  # noqa: pycodestyle
#             msg = msg.format('; '.join(unexpected_arguments), cls_name)
#             raise TypeError(msg)
# 
#     def __getitem__(self, name: str) -> Any:
#         # FIXME use frozendict
#         try:
#             options = self.__class__.options
#             opt = next(opt for opt in options if opt.name == name)
#         except StopIteration:
#             msg = "no option [{}] in state [{}]"
#             msg.format(name, self.__class__.__name__)
#             raise KeyError(msg)
#         return getattr(self, opt._field)
# 
#     def __hash__(self) -> int:
#         all_opts = [self[opt.name] for opt in self.__class__.options]
#         all_opts.insert(0, self.__class__.__name__)
#         return hash(tuple(all_opts))
# 
#     def __eq__(self, other: 'Configuration') -> bool:
#         if type(self) != type(other):
#             msg = "illegal comparison of configurations: [{}] vs. [{}]"
#             msg = msg.format(self.__class__.__name__, other.__class__.__name__)
#             raise exceptions.HoustonException(msg)
#         return self.__dict__ == other.__dict__
# 
#     def to_dict(self) -> Dict[str, Any]:
#         fields = {}  # type: Dict[str, Any]
#         for opt in self.__class__.options:
#             fields[opt.name] = getattr(self, opt._field)
#         return fields
# 
#     def __repr__(self) -> str:
#         fields = self.to_dict()
#         for (name, val) in fields.items():
#             if isinstance(val, float):
#                 s = "{:.3f}".format(val)
#             else:
#                 s = str(val)
#             fields[name] = val
#         s = '; '.join(["{}: {}".format(k, v) for (k, v) in fields.items()])
#         s = "{}({})".format(self.__class__.__name__, s)
#         return s
# 
# def option(*args, **kwargs) -> Option.Builder:
#     return Option.Builder(*args, **kwargs)


# Path: test/test_configuration.py
import pytest
from houston.configuration import Configuration, option


    c1 = X(foo=0)
    c2 = X(foo=1)
    c3 = X(foo=0)
    c4 = X(foo=1)

    s = {c1, c2, c3, c4}
    assert s == {X(foo=0), X(foo=1)}

    c5 = Y(foo=0)
    assert len({c1, c5}) == 2
    with pytest.raises(Exception):
        assert c1 != c5



def test_is_frozen():
    class X(Configuration):
        foo = option(int)
    conf = X(foo=0)
    with pytest.raises(AttributeError):
        conf.foo = 10
        pytest.fail("expected AttributeError (can't set foo)")


def test_eq():
    class X(Configuration):
        foo = option(int)
        bar = option(int)


    class Y(Configuration):