import inspect


def instantiate_class_and_inject_attributes(cls, **kwargs):
    """
    instantiates a class with the given kwargs, picking those arguments that are in the signature of cls to
    use for the __init__, and adding attributes to the constructed object with the remaining.
    :param cls: class to insantiate
    :param kwargs: keyword args (some for the class __init__, and others to inject)
    :return: An (possibly enhanced) class instance
    >>> class C:
    ...     def __init__(self, a, b=3):
    ...         self.a = a
    ...         self.b = b
    ...
    >>> c = instantiate_class_and_inject_attributes(C, a=10, foo='bar')
    >>> c.__dict__
    {'a': 10, 'b': 3, 'foo': 'bar'}
    >>> c = instantiate_class_and_inject_attributes(C, a=10, foo='bar', bar='foo', b=1000)
    >>> c.__dict__
    {'a': 10, 'b': 1000, 'foo': 'bar', 'bar': 'foo'}
    >>> try:
    ...     c = instantiate_class_and_inject_attributes(C, foo='bar', bar='foo', b=1000)
    ... except TypeError:  # expected
    ...     pass
    """
    # cls_signature_args = inspect.signature(cls).parameters
    cls_signature_args = inspect.getfullargspec(cls.__init__).args[1:]

    cls_kwargs = dict()
    other_kwargs = dict()
    for k, v in kwargs.items():
        if k in cls_signature_args:
            cls_kwargs[k] = v
        else:
            other_kwargs[k] = v
    o = cls(**cls_kwargs)
    o.__dict__.update(other_kwargs)
    return o