File size: 3,381 Bytes
07423df
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
from typing import Iterable, List, Optional


class Order:
    """
    A list-like structure to specify the order of items in a dictionary.
    The main characteristics are:
        - Append and insert only. Cannot remove elements. This is not strictly required
            by the use-case but probably good practice.
        - Elements must be unique. Inserting an element which is already in the list
            will throw an error.

    Primarily useful for specifying the order in which UI elements
    should be shown in Wave.
    """

    def __init__(self, keys: Optional[List[str]] = None):
        if keys is not None:
            self._list = list(keys)
        else:
            self._list = list()

    def _unique_guard(self, *keys: str):
        for key in keys:
            if key in self._list:
                raise ValueError(f"`{key}` is already in the list!")

    def append(self, key: str):
        """
        Append a key at the end of the list:

        Args:
            key: String to append.

        Raises:
            - `ValueError` if the key is already in the list.
        """

        self._unique_guard(key)

        self._list.append(key)

    def extend(self, keys: Iterable[str]):
        """
        Extend the list by multiple keys.

        Args:
            keys: Iterable of keys.

        Raises:
            - `ValueError` if one or more key is already in the list.
        """

        self._unique_guard(*keys)

        self._list.extend(keys)

    def insert(
        self, *keys: str, before: Optional[str] = None, after: Optional[str] = None
    ):
        """
        Insert one or more keys. Either `before` or `after`, but not both, must be set
        to determine position.

        Args:
            keys: One more keys to insert.
            after: A key immediately after which the `keys` will be inserted.
            before: A key immediately before which the `keys` are inserted.

        Raises:
            - `ValueError` if one or more key is already in the list.
            - `ValueError` if `before` / `after` does not exist in the list.
            - `ValueError` if an invalid combination of arguments is set.
        """

        self._unique_guard(*keys)

        if before is not None:
            for key in keys[::-1]:
                self._list.insert(self._list.index(before), key)

            if after is not None:
                raise ValueError("`after` must be None if `before` is set.")

        if after is not None:
            for key in keys[::-1]:
                self._list.insert(self._list.index(after) + 1, key)

            if before is not None:
                raise ValueError("`before` must be None if `after` is set.")

        if before is None and after is None:
            raise ValueError("Either `before` or `after` must be set.")

    def __getitem__(self, idx):
        return self._list[idx]

    def __len__(self):
        return len(self._list)

    def __iter__(self):
        return iter(self._list)


def test_order():
    order = Order(["dataset", "training", "validation", "logging"])

    order.insert("architecture", before="training")
    order.insert("environment", after="validation")

    assert [item for item in order] == [
        "dataset",
        "architecture",
        "training",
        "validation",
        "environment",
        "logging",
    ]