Unreviewed. Update W3C WebDriver imported tests.
[WebKit-https.git] / WebDriverTests / imported / w3c / tools / wptrunner / wptrunner / update / state.py
1 import os
2 import cPickle as pickle
3
4 here = os.path.abspath(os.path.split(__file__)[0])
5
6 class State(object):
7     filename = os.path.join(here, ".wpt-update.lock")
8
9     def __new__(cls, logger):
10         rv = cls.load(logger)
11         if rv is not None:
12             logger.debug("Existing state found")
13             return rv
14
15         logger.debug("No existing state found")
16         return object.__new__(cls, logger)
17
18     def __init__(self, logger):
19         """Object containing state variables created when running Steps.
20
21         On write the state is serialized to disk, such that it can be restored in
22         the event that the program is interrupted before all steps are complete.
23         Note that this only works well if the values are immutable; mutating an
24         existing value will not cause the data to be serialized.
25
26         Variables are set and get as attributes e.g. state_obj.spam = "eggs".
27
28         :param parent: Parent State object or None if this is the root object.
29         """
30
31         if hasattr(self, "_data"):
32             return
33
34         self._data = [{}]
35         self._logger = logger
36         self._index = 0
37
38     def __getstate__(self):
39         rv = self.__dict__.copy()
40         del rv["_logger"]
41         return rv
42
43     @classmethod
44     def load(cls, logger):
45         """Load saved state from a file"""
46         try:
47             if not os.path.isfile(cls.filename):
48                 return None
49             with open(cls.filename) as f:
50                 try:
51                     rv = pickle.load(f)
52                     logger.debug("Loading data %r" % (rv._data,))
53                     rv._logger = logger
54                     rv._index = 0
55                     return rv
56                 except EOFError:
57                     logger.warning("Found empty state file")
58         except IOError:
59             logger.debug("IOError loading stored state")
60
61     def push(self, init_values):
62         """Push a new clean state dictionary
63
64         :param init_values: List of variable names in the current state dict to copy
65                             into the new state dict."""
66
67         return StateContext(self, init_values)
68
69     def save(self):
70         """Write the state to disk"""
71         with open(self.filename, "w") as f:
72             pickle.dump(self, f)
73
74     def is_empty(self):
75         return len(self._data) == 1 and self._data[0] == {}
76
77     def clear(self):
78         """Remove all state and delete the stored copy."""
79         try:
80             os.unlink(self.filename)
81         except OSError:
82             pass
83         self._data = [{}]
84
85
86     def __setattr__(self, key, value):
87         if key.startswith("_"):
88             object.__setattr__(self, key, value)
89         else:
90             self._data[self._index][key] = value
91             self.save()
92
93     def __getattr__(self, key):
94         if key.startswith("_"):
95             raise AttributeError
96         try:
97             return self._data[self._index][key]
98         except KeyError:
99             raise AttributeError
100
101     def __contains__(self, key):
102         return key in self._data[self._index]
103
104     def update(self, items):
105         """Add a dictionary of {name: value} pairs to the state"""
106         self._data[self._index].update(items)
107         self.save()
108
109     def keys(self):
110         return self._data[self._index].keys()
111
112 class StateContext(object):
113     def __init__(self, state, init_values):
114         self.state = state
115         self.init_values = init_values
116
117     def __enter__(self):
118         if len(self.state._data) == self.state._index + 1:
119             # This is the case where there is no stored state
120             new_state = {}
121             for key in self.init_values:
122                 new_state[key] = self.state._data[self.state._index][key]
123             self.state._data.append(new_state)
124         self.state._index += 1
125         self.state._logger.debug("Incremented index to %s" % self.state._index)
126
127     def __exit__(self, *args, **kwargs):
128         if len(self.state._data) > 1:
129             assert self.state._index == len(self.state._data) - 1
130             self.state._data.pop()
131             self.state._index -= 1
132             self.state._logger.debug("Decremented index to %s" % self.state._index)
133             assert self.state._index >= 0
134         else:
135             raise ValueError("Tried to pop the top state")