comparison piecrust/pipelines/records.py @ 854:08e02c2a2a1a

core: Keep refactoring, this time to prepare for generator sources. - Make a few APIs simpler. - Content pipelines create their own jobs, so that generator sources can keep aborting in `getContents`, but rely on their pipeline to generate pages for baking.
author Ludovic Chabant <ludovic@chabant.com>
date Sun, 04 Jun 2017 23:34:28 -0700
parents f070a4fc033c
children 448710d84121
comparison
equal deleted inserted replaced
853:f070a4fc033c 854:08e02c2a2a1a
7 7
8 8
9 logger = logging.getLogger(__name__) 9 logger = logging.getLogger(__name__)
10 10
11 11
12 class RecordEntry:
13 """ An entry in a record, for a specific content item.
14 """
15 def __init__(self):
16 self.item_spec = None
17 self.errors = []
18
19 @property
20 def success(self):
21 return len(self.errors) == 0
22
23 def describe(self):
24 return {}
25
26 def getAllOutputPaths(self):
27 return None
28
29 def getAllErrors(self):
30 return self.errors
31
32
33 class Record:
34 """ A class that represents a 'record' of a bake operation on a
35 content source.
36 """
37 def __init__(self, name):
38 self.name = name
39 self.deleted_out_paths = []
40 self.success = True
41 self._entries = {}
42
43 def addEntry(self, entry):
44 if entry.item_spec in self._entries:
45 raise ValueError("Entry '%s' is already in the record." %
46 entry.item_spec)
47 self._entries[entry.item_spec] = entry
48
49 def getEntries(self):
50 return self._entries.values()
51
52 def getEntry(self, item_spec):
53 return self._entries[item_spec]
54
55
12 class MultiRecord: 56 class MultiRecord:
13 """ A container that includes multiple `Record` instances. 57 """ A container that includes multiple `Record` instances -- one for
58 each content source that was baked.
14 """ 59 """
15 RECORD_VERSION = 12 60 RECORD_VERSION = 12
16 61
17 def __init__(self): 62 def __init__(self):
18 self.records = [] 63 self.records = []
47 logger.debug("Loading bake records from: %s" % path) 92 logger.debug("Loading bake records from: %s" % path)
48 with open(path, 'rb') as fp: 93 with open(path, 'rb') as fp:
49 return pickle.load(fp) 94 return pickle.load(fp)
50 95
51 96
52 class Record: 97 def get_flag_descriptions(flags, flag_descriptions):
53 """ A basic class that represents a 'record' of a bake operation on a 98 res = []
54 content source. 99 for k, v in flag_descriptions.items():
55 """ 100 if flags & k:
56 def __init__(self, name): 101 res.append(v)
57 self.name = name 102 if res:
58 self.entries = [] 103 return ', '.join(res)
59 self.deleted_out_paths = [] 104 return 'none'
60 self.success = True
61
62
63 class RecordEntry:
64 """ An entry in a record, for a specific content item.
65 """
66 def __init__(self):
67 self.item_spec = None
68 self.out_paths = []
69 self.errors = []
70
71 @property
72 def success(self):
73 return len(self.errors) == 0
74
75 def describe(self):
76 return {}
77 105
78 106
79 def _are_records_valid(multi_record): 107 def _are_records_valid(multi_record):
80 return (multi_record._app_version == APP_VERSION and 108 return (multi_record._app_version == APP_VERSION and
81 multi_record._record_version == MultiRecord.RECORD_VERSION) 109 multi_record._record_version == MultiRecord.RECORD_VERSION)
104 multi_record.invalidated = was_invalid 132 multi_record.invalidated = was_invalid
105 133
106 return multi_record 134 return multi_record
107 135
108 136
109 def _build_diff_key(item_spec): 137 class RecordHistory:
110 return hashlib.md5(item_spec.encode('utf8')).hexdigest() 138 def __init__(self, previous, current):
139 if previous is None or current is None:
140 raise ValueError()
141
142 if previous.name != current.name:
143 raise Exception("The two records must have the same name! "
144 "Got '%s' and '%s'." %
145 (previous.name, current.name))
146
147 self._previous = previous
148 self._current = current
149 self._diffs = None
150
151 @property
152 def name(self):
153 return self._current.name
154
155 @property
156 def current(self):
157 return self._current
158
159 @property
160 def previous(self):
161 return self._previous
162
163 @property
164 def diffs(self):
165 if self._diffs is None:
166 raise Exception("This record history hasn't been built yet.")
167 return self._diffs.values()
168
169 def getPreviousEntry(self, item_spec):
170 key = _build_diff_key(item_spec)
171 return self._diffs[key][0]
172
173 def getCurrentEntry(self, item_spec):
174 key = _build_diff_key(item_spec)
175 return self._diffs[key][1]
176
177 def build(self):
178 if self._diffs is not None:
179 raise Exception("This record history has already been built.")
180
181 self._diffs = {}
182 if self._previous is not None:
183 for e in self._previous.getEntries():
184 key = _build_diff_key(e.item_spec)
185 self._diffs[key] = (e, None)
186
187 if self._current is not None:
188 for e in self._current.getEntries():
189 key = _build_diff_key(e.item_spec)
190 diff = self._diffs.get(key)
191 if diff is None:
192 self._diffs[key] = (None, e)
193 elif diff[1] is None:
194 self._diffs[key] = (diff[0], e)
195 else:
196 raise Exception(
197 "A current record entry already exists for '%s' "
198 "(%s)" % (key, diff[1].item_spec))
111 199
112 200
113 class MultiRecordHistory: 201 class MultiRecordHistory:
114 """ Tracks the differences between an 'old' and a 'new' record 202 """ Tracks the differences between an 'old' and a 'new' record
115 container. 203 container.
119 raise ValueError() 207 raise ValueError()
120 208
121 self.previous = previous 209 self.previous = previous
122 self.current = current 210 self.current = current
123 self.histories = [] 211 self.histories = []
124 self._buildHistories(previous, current) 212 self._linkHistories(previous, current)
213
214 def getPreviousRecord(self, record_name, auto_create=True):
215 return self.previous.getRecord(record_name, auto_create=auto_create)
216
217 def getCurrentRecord(self, record_name):
218 return self.current.getRecord(record_name)
125 219
126 def getHistory(self, record_name): 220 def getHistory(self, record_name):
127 for h in self.histories: 221 for h in self.histories:
128 if h.name == record_name: 222 if h.name == record_name:
129 return h 223 return h
224
130 rh = RecordHistory( 225 rh = RecordHistory(
131 Record(record_name), 226 Record(record_name),
132 Record(record_name)) 227 Record(record_name))
133 self.histories.append(rh) 228 self.histories.append(rh)
134 self.previous.records.append(rh.previous) 229 self.previous.records.append(rh.previous)
135 self.current.records.append(rh.current) 230 self.current.records.append(rh.current)
136 return rh 231 return rh
137 232
138 def _buildHistories(self, previous, current): 233 def _linkHistories(self, previous, current):
139 pairs = {} 234 pairs = {}
140 if previous: 235 if previous:
141 for r in previous.records: 236 for r in previous.records:
142 pairs[r.name] = (r, None) 237 pairs[r.name] = (r, None)
143 if current: 238 if current:
156 c = Record(name) 251 c = Record(name)
157 current.records.append(c) 252 current.records.append(c)
158 self.histories.append(RecordHistory(p, c)) 253 self.histories.append(RecordHistory(p, c))
159 254
160 255
161 class RecordHistory: 256 def _build_diff_key(item_spec):
162 def __init__(self, previous, current): 257 return hashlib.md5(item_spec.encode('utf8')).hexdigest()
163 if previous is None or current is None: 258
164 raise ValueError()
165
166 if previous.name != current.name:
167 raise Exception("The two records must have the same name! "
168 "Got '%s' and '%s'." %
169 (previous.name, current.name))
170
171 self._previous = previous
172 self._current = current
173 self._diffs = None
174
175 @property
176 def name(self):
177 return self._current.name
178
179 @property
180 def current(self):
181 return self._current
182
183 @property
184 def previous(self):
185 return self._previous
186
187 @property
188 def diffs(self):
189 if self._diffs is None:
190 raise Exception("This record history hasn't been built yet.")
191 return self._diffs.values()
192
193 def build(self):
194 if self._diffs is not None:
195 raise Exception("This record history has already been built.")
196
197 self._diffs = {}
198 if self._previous is not None:
199 for e in self._previous.entries:
200 key = _build_diff_key(e.item_spec)
201 self._diffs[key] = (e, None)
202
203 if self._current is not None:
204 for e in self._current.entries:
205 key = _build_diff_key(e.item_spec)
206 diff = self._diffs.get(key)
207 if diff is None:
208 self._diffs[key] = (None, e)
209 elif diff[1] is None:
210 self._diffs[key] = (diff[0], e)
211 else:
212 raise Exception(
213 "A current record entry already exists for: %s" % key)
214