1from itertools import filterfalse
2
3
4def unique_everseen(iterable, key=None):
5    "List unique elements, preserving order. Remember all elements ever seen."
6    # unique_everseen('AAAABBBCCDAABBB') --> A B C D
7    # unique_everseen('ABBCcAD', str.lower) --> A B C D
8    seen = set()
9    seen_add = seen.add
10    if key is None:
11        for element in filterfalse(seen.__contains__, iterable):
12            seen_add(element)
13            yield element
14    else:
15        for element in iterable:
16            k = key(element)
17            if k not in seen:
18                seen_add(k)
19                yield element
20
21
22# copied from more_itertools 8.8
23def always_iterable(obj, base_type=(str, bytes)):
24    """If *obj* is iterable, return an iterator over its items::
25
26        >>> obj = (1, 2, 3)
27        >>> list(always_iterable(obj))
28        [1, 2, 3]
29
30    If *obj* is not iterable, return a one-item iterable containing *obj*::
31
32        >>> obj = 1
33        >>> list(always_iterable(obj))
34        [1]
35
36    If *obj* is ``None``, return an empty iterable:
37
38        >>> obj = None
39        >>> list(always_iterable(None))
40        []
41
42    By default, binary and text strings are not considered iterable::
43
44        >>> obj = 'foo'
45        >>> list(always_iterable(obj))
46        ['foo']
47
48    If *base_type* is set, objects for which ``isinstance(obj, base_type)``
49    returns ``True`` won't be considered iterable.
50
51        >>> obj = {'a': 1}
52        >>> list(always_iterable(obj))  # Iterate over the dict's keys
53        ['a']
54        >>> list(always_iterable(obj, base_type=dict))  # Treat dicts as a unit
55        [{'a': 1}]
56
57    Set *base_type* to ``None`` to avoid any special handling and treat objects
58    Python considers iterable as iterable:
59
60        >>> obj = 'foo'
61        >>> list(always_iterable(obj, base_type=None))
62        ['f', 'o', 'o']
63    """
64    if obj is None:
65        return iter(())
66
67    if (base_type is not None) and isinstance(obj, base_type):
68        return iter((obj,))
69
70    try:
71        return iter(obj)
72    except TypeError:
73        return iter((obj,))
74