import itertools class PseudoStr(str): pass class StrProxy: def __init__(self, value): self.value = value def __str__(self): return self.value def __bool__(self): return bool(self.value) class Object: def __repr__(self): return '' def wrapped_arg_combos(*args, wrappers=(PseudoStr, StrProxy), skip=(lambda w, i, v: not isinstance(v, str)), ): """Yield every possible combination of wrapped items for the given args. Effectively, the wrappers are applied to the args according to the powerset of the args indicies. So the result includes the args completely unwrapped. If "skip" is supplied (default is to skip all non-str values) and it returns True for a given arg index/value then that arg will remain unwrapped, Only unique results are returned. If an arg was skipped for one of the combinations then it could end up matching one of the other combinations. In that case only one of them will be yielded. """ if not args: return indices = list(range(len(args))) # The powerset (from recipe in the itertools docs). combos = itertools.chain.from_iterable(itertools.combinations(indices, r) for r in range(len(indices)+1)) seen = set() for combo in combos: for wrap in wrappers: indexes = [] applied = list(args) for i in combo: arg = args[i] if skip and skip(wrap, i, arg): continue indexes.append(i) applied[i] = wrap(arg) key = (wrap, tuple(indexes)) if key not in seen: yield tuple(applied) seen.add(key)