• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1import itertools
2
3
4class PseudoStr(str):
5    pass
6
7
8class StrProxy:
9    def __init__(self, value):
10        self.value = value
11    def __str__(self):
12        return self.value
13    def __bool__(self):
14        return bool(self.value)
15
16
17class Object:
18    def __repr__(self):
19        return '<object>'
20
21
22def wrapped_arg_combos(*args,
23                       wrappers=(PseudoStr, StrProxy),
24                       skip=(lambda w, i, v: not isinstance(v, str)),
25                       ):
26    """Yield every possible combination of wrapped items for the given args.
27
28    Effectively, the wrappers are applied to the args according to the
29    powerset of the args indicies.  So the result includes the args
30    completely unwrapped.
31
32    If "skip" is supplied (default is to skip all non-str values) and
33    it returns True for a given arg index/value then that arg will
34    remain unwrapped,
35
36    Only unique results are returned.  If an arg was skipped for one
37    of the combinations then it could end up matching one of the other
38    combinations.  In that case only one of them will be yielded.
39    """
40    if not args:
41        return
42    indices = list(range(len(args)))
43    # The powerset (from recipe in the itertools docs).
44    combos = itertools.chain.from_iterable(itertools.combinations(indices, r)
45                                           for r in range(len(indices)+1))
46    seen = set()
47    for combo in combos:
48        for wrap in wrappers:
49            indexes = []
50            applied = list(args)
51            for i in combo:
52                arg = args[i]
53                if skip and skip(wrap, i, arg):
54                    continue
55                indexes.append(i)
56                applied[i] = wrap(arg)
57            key = (wrap, tuple(indexes))
58            if key not in seen:
59                yield tuple(applied)
60                seen.add(key)
61