comparison piecrust/processing/records.py @ 120:133845647083

Better error management and removal support in baking/processing. * Baker and processor pipeline now store errors in their records. * They also support deleting output files that are no longer valid. * The basic transitional record class implements more boilerplate code. * The processor pipeline is run from the `bake` command directly. * New unit tests. * Unit test mocking now mocks `os.remove` too.
author Ludovic Chabant <ludovic@chabant.com>
date Sun, 09 Nov 2014 14:46:23 -0800
parents
children 9e4c2e68a129
comparison
equal deleted inserted replaced
119:0811f92cbdc7 120:133845647083
1 import os.path
2 from piecrust.records import Record, TransitionalRecord
3
4
5 class ProcessorPipelineRecord(Record):
6 RECORD_VERSION = 2
7
8 def __init__(self):
9 super(ProcessorPipelineRecord, self).__init__()
10 self.out_dir = None
11 self.process_time = None
12
13 def hasOverrideEntry(self, rel_path):
14 return self.findEntry(rel_path) is not None
15
16 def findEntry(self, rel_path):
17 rel_path = rel_path.lower()
18 for entry in self.entries:
19 for out_path in entry.rel_outputs:
20 if out_path.lower() == rel_path:
21 return entry
22 return None
23
24
25 FLAG_NONE = 0
26 FLAG_PROCESSED = 2**0
27 FLAG_OVERRIDEN = 2**1
28
29
30 class ProcessorPipelineRecordEntry(object):
31 def __init__(self, base_dir, rel_input):
32 self.base_dir = base_dir
33 self.rel_input = rel_input
34
35 self.flags = FLAG_NONE
36 self.rel_outputs = []
37 self.errors = []
38
39 @property
40 def path(self):
41 return os.path.join(self.base_dir, self.rel_input)
42
43 @property
44 def was_processed(self):
45 return bool(self.flags & FLAG_PROCESSED)
46
47 @property
48 def was_processed_successfully(self):
49 return self.was_processed and not self.errors
50
51
52 class TransitionalProcessorPipelineRecord(TransitionalRecord):
53 def __init__(self, previous_path=None):
54 super(TransitionalProcessorPipelineRecord, self).__init__(
55 ProcessorPipelineRecord, previous_path)
56
57 def getTransitionKey(self, entry):
58 return entry.rel_input
59
60 def getPreviousEntry(self, rel_path):
61 pair = self.transitions.get(rel_path)
62 if pair is not None:
63 return pair[0]
64 return None
65
66 def collapseRecords(self):
67 for prev, cur in self.transitions.values():
68 if prev and cur and not cur.was_processed:
69 # This asset wasn't processed, so the information from
70 # last time is still valid.
71 cur.flags = prev.flags
72 cur.rel_outputs = list(prev.rel_outputs)
73 cur.errors = list(prev.errors)
74
75 def getDeletions(self):
76 for prev, cur in self.transitions.values():
77 if prev and not cur:
78 for p in prev.rel_outputs:
79 abs_p = os.path.join(self.previous.out_dir, p)
80 yield (abs_p, 'previous asset was removed')
81 elif prev and cur and cur.was_processed_successfully:
82 diff = set(prev.rel_outputs) - set(cur.rel_outputs)
83 for p in diff:
84 abs_p = os.path.join(self.previous.out_dir, p)
85 yield (abs_p, 'asset changed outputs')
86