diff 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
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/piecrust/processing/records.py	Sun Nov 09 14:46:23 2014 -0800
@@ -0,0 +1,86 @@
+import os.path
+from piecrust.records import Record, TransitionalRecord
+
+
+class ProcessorPipelineRecord(Record):
+    RECORD_VERSION = 2
+
+    def __init__(self):
+        super(ProcessorPipelineRecord, self).__init__()
+        self.out_dir = None
+        self.process_time = None
+
+    def hasOverrideEntry(self, rel_path):
+        return self.findEntry(rel_path) is not None
+
+    def findEntry(self, rel_path):
+        rel_path = rel_path.lower()
+        for entry in self.entries:
+            for out_path in entry.rel_outputs:
+                if out_path.lower() == rel_path:
+                    return entry
+        return None
+
+
+FLAG_NONE = 0
+FLAG_PROCESSED = 2**0
+FLAG_OVERRIDEN = 2**1
+
+
+class ProcessorPipelineRecordEntry(object):
+    def __init__(self, base_dir, rel_input):
+        self.base_dir = base_dir
+        self.rel_input = rel_input
+
+        self.flags = FLAG_NONE
+        self.rel_outputs = []
+        self.errors = []
+
+    @property
+    def path(self):
+        return os.path.join(self.base_dir, self.rel_input)
+
+    @property
+    def was_processed(self):
+        return bool(self.flags & FLAG_PROCESSED)
+
+    @property
+    def was_processed_successfully(self):
+        return self.was_processed and not self.errors
+
+
+class TransitionalProcessorPipelineRecord(TransitionalRecord):
+    def __init__(self, previous_path=None):
+        super(TransitionalProcessorPipelineRecord, self).__init__(
+                ProcessorPipelineRecord, previous_path)
+
+    def getTransitionKey(self, entry):
+        return entry.rel_input
+
+    def getPreviousEntry(self, rel_path):
+        pair = self.transitions.get(rel_path)
+        if pair is not None:
+            return pair[0]
+        return None
+
+    def collapseRecords(self):
+        for prev, cur in self.transitions.values():
+            if prev and cur and not cur.was_processed:
+                # This asset wasn't processed, so the information from
+                # last time is still valid.
+                cur.flags = prev.flags
+                cur.rel_outputs = list(prev.rel_outputs)
+                cur.errors = list(prev.errors)
+
+    def getDeletions(self):
+        for prev, cur in self.transitions.values():
+            if prev and not cur:
+                for p in prev.rel_outputs:
+                    abs_p = os.path.join(self.previous.out_dir, p)
+                    yield (abs_p, 'previous asset was removed')
+            elif prev and cur and cur.was_processed_successfully:
+                diff = set(prev.rel_outputs) - set(cur.rel_outputs)
+                for p in diff:
+                    abs_p = os.path.join(self.previous.out_dir, p)
+                    yield (abs_p, 'asset changed outputs')
+