Unmarshal @obj into @cls

    Args:
        obj:              dict, The dict to unmarshal into @cls
        cls:              type, The class to unmarshal into
        allow_extra_keys: bool, False to raise an exception when extra
                          keys are present, True to ignore
    Returns:
        instance of @cls
    Raises:
        ExtraKeysError: If allow_extra_keys == False, and extra keys
                        are present in @obj and not in @cls.__init__
        ValueError:     If @cls.__init__ does not contain a self argument

def unmarshal_dict(
    obj,
    cls,
    allow_extra_keys=True,
    ctor=None,
):
    """ Unmarshal @obj into @cls

    Args:
        obj:              dict, The dict to unmarshal into @cls
        cls:              type, The class to unmarshal into
        allow_extra_keys: bool, False to raise an exception when extra
                          keys are present, True to ignore
    Returns:
        instance of @cls
    Raises:
        ExtraKeysError: If allow_extra_keys == False, and extra keys
                        are present in @obj and not in @cls.__init__
        ValueError:     If @cls.__init__ does not contain a self argument
    """
    if not ctor:
        ctor = cls
    args = init_args(ctor)
    obj = key_swap(obj, cls, False)
    kwargs = {k: v for k, v in obj.items() if k in args}

    # If either is set to False, do not allow extra keys
    # to be present in obj but not in cls.__init__
    allow_extra_keys = (
        getattr(cls, '_unmarshal_allow_extra_keys', True)
        and
        allow_extra_keys
    )

    if not allow_extra_keys and len(obj) > len(kwargs):
        diff = {k: v for k, v in obj.items() if k not in args}
        raise ExtraKeysError(cls, diff)

    try:
        return ctor(**kwargs)
    except ExtraKeysError as ex:
        raise ex
    except Exception as ex:
        raise InitArgsError(
            cls,
            args,
            kwargs,
            ex,
        )