comparison piecrust/pipelines/records.py @ 853:f070a4fc033c

core: Continue PieCrust3 refactor, simplify pages. The asset pipeline is still the only function pipeline at this point. * No more `QualifiedPage`, and several other pieces of code deleted. * Data providers are simpler and more focused. For instance, the page iterator doesn't try to support other types of items. * Route parameters are proper known source metadata to remove the confusion between the two. * Make the baker and pipeline more correctly manage records and record histories. * Add support for record collapsing and deleting stale outputs in the asset pipeline.
author Ludovic Chabant <ludovic@chabant.com>
date Sun, 21 May 2017 00:06:59 -0700
parents 4850f8c21b6e
children 08e02c2a2a1a
comparison
equal deleted inserted replaced
852:4850f8c21b6e 853:f070a4fc033c
8 8
9 logger = logging.getLogger(__name__) 9 logger = logging.getLogger(__name__)
10 10
11 11
12 class MultiRecord: 12 class MultiRecord:
13 """ A container that includes multiple `Record` instances.
14 """
13 RECORD_VERSION = 12 15 RECORD_VERSION = 12
14 16
15 def __init__(self): 17 def __init__(self):
16 self.records = [] 18 self.records = []
17 self.success = True 19 self.success = True
26 for r in self.records: 28 for r in self.records:
27 if r.name == record_name: 29 if r.name == record_name:
28 return r 30 return r
29 if not auto_create: 31 if not auto_create:
30 return None 32 return None
31 record = Record() 33 record = Record(record_name)
32 self.records.append(record) 34 self.records.append(record)
33 return record 35 return record
34 36
35 def save(self, path): 37 def save(self, path):
36 path_dir = os.path.dirname(path) 38 path_dir = os.path.dirname(path)
46 with open(path, 'rb') as fp: 48 with open(path, 'rb') as fp:
47 return pickle.load(fp) 49 return pickle.load(fp)
48 50
49 51
50 class Record: 52 class Record:
51 def __init__(self): 53 """ A basic class that represents a 'record' of a bake operation on a
52 self.name = None 54 content source.
55 """
56 def __init__(self, name):
57 self.name = name
53 self.entries = [] 58 self.entries = []
54 self.stats = {} 59 self.deleted_out_paths = []
55 self.out_dir = None
56 self.success = True 60 self.success = True
57 61
58 62
59 class RecordEntry: 63 class RecordEntry:
64 """ An entry in a record, for a specific content item.
65 """
60 def __init__(self): 66 def __init__(self):
61 self.item_spec = None 67 self.item_spec = None
68 self.out_paths = []
62 self.errors = [] 69 self.errors = []
63 70
64 @property 71 @property
65 def success(self): 72 def success(self):
66 return len(self.errors) == 0 73 return len(self.errors) == 0
74
75 def describe(self):
76 return {}
67 77
68 78
69 def _are_records_valid(multi_record): 79 def _are_records_valid(multi_record):
70 return (multi_record._app_version == APP_VERSION and 80 return (multi_record._app_version == APP_VERSION and
71 multi_record._record_version == MultiRecord.RECORD_VERSION) 81 multi_record._record_version == MultiRecord.RECORD_VERSION)
99 def _build_diff_key(item_spec): 109 def _build_diff_key(item_spec):
100 return hashlib.md5(item_spec.encode('utf8')).hexdigest() 110 return hashlib.md5(item_spec.encode('utf8')).hexdigest()
101 111
102 112
103 class MultiRecordHistory: 113 class MultiRecordHistory:
114 """ Tracks the differences between an 'old' and a 'new' record
115 container.
116 """
104 def __init__(self, previous, current): 117 def __init__(self, previous, current):
105 if previous is None or current is None: 118 if previous is None or current is None:
106 raise ValueError() 119 raise ValueError()
107 120
108 self.previous = previous 121 self.previous = previous
112 125
113 def getHistory(self, record_name): 126 def getHistory(self, record_name):
114 for h in self.histories: 127 for h in self.histories:
115 if h.name == record_name: 128 if h.name == record_name:
116 return h 129 return h
117 return None 130 rh = RecordHistory(
131 Record(record_name),
132 Record(record_name))
133 self.histories.append(rh)
134 self.previous.records.append(rh.previous)
135 self.current.records.append(rh.current)
136 return rh
118 137
119 def _buildHistories(self, previous, current): 138 def _buildHistories(self, previous, current):
120 pairs = {} 139 pairs = {}
121 if previous: 140 if previous:
122 for r in previous.records: 141 for r in previous.records:
126 p = pairs.get(r.name, (None, None)) 145 p = pairs.get(r.name, (None, None))
127 if p[1] is not None: 146 if p[1] is not None:
128 raise Exception("Got several records named: %s" % r.name) 147 raise Exception("Got several records named: %s" % r.name)
129 pairs[r.name] = (p[0], r) 148 pairs[r.name] = (p[0], r)
130 149
131 for p, c in pairs.values(): 150 for name, pair in pairs.items():
151 p, c = pair
152 if p is None:
153 p = Record(name)
154 previous.records.append(p)
155 if c is None:
156 c = Record(name)
157 current.records.append(c)
132 self.histories.append(RecordHistory(p, c)) 158 self.histories.append(RecordHistory(p, c))
133 159
134 160
135 class RecordHistory: 161 class RecordHistory:
136 def __init__(self, previous, current): 162 def __init__(self, previous, current):
137 self._diffs = {} 163 if previous is None or current is None:
138 self._previous = previous 164 raise ValueError()
139 self._current = current 165
140 166 if previous.name != current.name:
141 if previous and current and previous.name != current.name:
142 raise Exception("The two records must have the same name! " 167 raise Exception("The two records must have the same name! "
143 "Got '%s' and '%s'." % 168 "Got '%s' and '%s'." %
144 (previous.name, current.name)) 169 (previous.name, current.name))
145 170
146 self._buildDiffs() 171 self._previous = previous
172 self._current = current
173 self._diffs = None
147 174
148 @property 175 @property
149 def name(self): 176 def name(self):
150 return self._current.name 177 return self._current.name
151 178
157 def previous(self): 184 def previous(self):
158 return self._previous 185 return self._previous
159 186
160 @property 187 @property
161 def diffs(self): 188 def diffs(self):
189 if self._diffs is None:
190 raise Exception("This record history hasn't been built yet.")
162 return self._diffs.values() 191 return self._diffs.values()
163 192
164 def _buildDiffs(self): 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 = {}
165 if self._previous is not None: 198 if self._previous is not None:
166 for e in self._previous.entries: 199 for e in self._previous.entries:
167 key = _build_diff_key(e.item_spec) 200 key = _build_diff_key(e.item_spec)
168 self._diffs[key] = (e, None) 201 self._diffs[key] = (e, None)
169 202