comparison piecrust/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 e88e330eb8dc
children 9e4c2e68a129
comparison
equal deleted inserted replaced
119:0811f92cbdc7 120:133845647083
1 import os 1 import os
2 import os.path 2 import os.path
3 import pickle 3 import pickle
4 import logging 4 import logging
5 from piecrust import APP_VERSION
5 from piecrust.events import Event 6 from piecrust.events import Event
6 7
7 8
8 logger = logging.getLogger(__name__) 9 logger = logging.getLogger(__name__)
9 10
10 11
11 class Record(object): 12 class Record(object):
12 def __init__(self): 13 def __init__(self):
13 self.entries = [] 14 self.entries = []
14 self.entry_added = Event() 15 self.entry_added = Event()
16 self.app_version = APP_VERSION
17 self.record_version = self.__class__.RECORD_VERSION
18
19 def hasLatestVersion(self):
20 return (self.app_version == APP_VERSION and
21 self.record_version == self.__class__.RECORD_VERSION)
15 22
16 def addEntry(self, entry): 23 def addEntry(self, entry):
17 self.entries.append(entry) 24 self.entries.append(entry)
18 self.entry_added.fire(entry) 25 self.entry_added.fire(entry)
19 26
29 odict = self.__dict__.copy() 36 odict = self.__dict__.copy()
30 del odict['entry_added'] 37 del odict['entry_added']
31 return odict 38 return odict
32 39
33 def __setstate__(self, state): 40 def __setstate__(self, state):
34 for k, v in state.items(): 41 state['entry_added'] = Event()
35 setattr(self, k, v) 42 self.__dict__.update(state)
36 self.entry_added = Event()
37 43
38 @staticmethod 44 @staticmethod
39 def load(path): 45 def load(path):
40 logger.debug("Loading bake record from: %s" % path) 46 logger.debug("Loading bake record from: %s" % path)
41 with open(path, 'rb') as fp: 47 with open(path, 'rb') as fp:
42 return pickle.load(fp) 48 return pickle.load(fp)
43 49
50
51 class TransitionalRecord(object):
52 def __init__(self, record_class, previous_path=None):
53 self._record_class = record_class
54 self.transitions = {}
55 self.incremental_count = 0
56 self.current = record_class()
57 if previous_path:
58 self.loadPrevious(previous_path)
59 else:
60 self.previous = record_class()
61 self.current.entry_added += self._onCurrentEntryAdded
62
63 def loadPrevious(self, previous_path):
64 previous_record_valid = True
65 try:
66 self.previous = self._record_class.load(previous_path)
67 except Exception as ex:
68 logger.debug("Error loading previous record: %s" % ex)
69 logger.debug("Will reset to an empty one.")
70 previous_record_valid = False
71
72 if self.previous.record_version != self._record_class.RECORD_VERSION:
73 logger.debug("Previous record has old version %d." %
74 self.previous.record_version)
75 logger.debug("Will reset to an empty one.")
76 previous_record_valid = False
77
78 if not previous_record_valid:
79 self.previous = self._record_class()
80 return
81
82 for e in self.previous.entries:
83 key = self.getTransitionKey(e)
84 self.transitions[key] = (e, None)
85
86 def clearPrevious(self):
87 self.previous = self._record_class()
88
89 def saveCurrent(self, current_path):
90 self.current.save(current_path)
91
92 def addEntry(self, entry):
93 self.current.addEntry(entry)
94
95 def getTransitionKey(self, entry):
96 raise NotImplementedError()
97
98 def _onCurrentEntryAdded(self, entry):
99 key = self.getTransitionKey(entry)
100 te = self.transitions.get(key)
101 if te is None:
102 logger.debug("Adding new record entry: %s" % key)
103 self.transitions[key] = (None, entry)
104 return
105
106 if te[1] is not None:
107 raise Exception("A current entry already exists for: %s" %
108 key)
109 logger.debug("Setting current record entry: %s" % key)
110 self.transitions[key] = (te[0], entry)
111