decorate a test method to run it as a set of subtests modeled after pytest parametrize wrap a function to be invoked for each usage decorate a test method to run it as a set of subtests modeled after pytest parametrize wrap a function to be invoked for each usage
import types
import functools

from ._itertools import always_iterable


def parameterize(names, value_groups):
    

    def decorator(func):
        @functools.wraps(func)
        def wrapped(self):
            for values in value_groups:
                resolved = map(Invoked.eval, always_iterable(values))
                params = dict(zip(always_iterable(names), resolved))
                with self.subTest(**params):
                    func(self, **params)

        return wrapped

    return decorator


class Invoked(types.SimpleNamespace):
    

    @classmethod
    def wrap(cls, func):
        return cls(func=func)

    @classmethod
    def eval(cls, cand):
        return cand.func() if isinstance(cand, cls) else cand