comparison piecrust/pipelines/_pagerecords.py @ 989:8adc27285d93

bake: Big pass on bake performance. - Reduce the amount of data passed between processes. - Make inter-process data simple objects to make it easier to test with alternatives to pickle. - Make sources have the basic requirement to be able to find a content item from an item spec (path). - Make Hoedown the default Markdown formatter.
author Ludovic Chabant <ludovic@chabant.com>
date Sun, 19 Nov 2017 14:29:17 -0800
parents 45ad976712ec
children 1857dbd4580f
comparison
equal deleted inserted replaced
988:f83ae0a5d793 989:8adc27285d93
1 import copy 1 import copy
2 from piecrust.pipelines.records import RecordEntry, get_flag_descriptions 2 from piecrust.pipelines.records import RecordEntry, get_flag_descriptions
3 3
4 4
5 class SubPagePipelineRecordEntry: 5 class SubPageFlags:
6 FLAG_NONE = 0 6 FLAG_NONE = 0
7 FLAG_BAKED = 2**0 7 FLAG_BAKED = 2**0
8 FLAG_FORCED_BY_SOURCE = 2**1 8 FLAG_FORCED_BY_SOURCE = 2**1
9 FLAG_FORCED_BY_NO_PREVIOUS = 2**2 9 FLAG_FORCED_BY_NO_PREVIOUS = 2**2
10 FLAG_FORCED_BY_PREVIOUS_ERRORS = 2**3 10 FLAG_FORCED_BY_PREVIOUS_ERRORS = 2**3
11 FLAG_FORCED_BY_GENERAL_FORCE = 2**4 11 FLAG_FORCED_BY_GENERAL_FORCE = 2**4
12 FLAG_FORMATTING_INVALIDATED = 2**5 12 FLAG_RENDER_CACHE_INVALIDATED = 2**5
13 13
14 def __init__(self, out_uri, out_path):
15 self.out_uri = out_uri
16 self.out_path = out_path
17 self.flags = self.FLAG_NONE
18 self.errors = []
19 self.render_info = [None, None] # Same length as RENDER_PASSES
20 14
21 @property 15 def create_subpage_job_result(out_uri, out_path):
22 def was_clean(self): 16 return {
23 return (self.flags & self.FLAG_BAKED) == 0 and len(self.errors) == 0 17 'out_uri': out_uri,
18 'out_path': out_path,
19 'flags': SubPageFlags.FLAG_NONE,
20 'errors': [],
21 'render_info': None
22 }
24 23
25 @property
26 def was_baked(self):
27 return (self.flags & self.FLAG_BAKED) != 0
28 24
29 @property 25 def was_subpage_clean(sub):
30 def was_baked_successfully(self): 26 return ((sub['flags'] & SubPageFlags.FLAG_BAKED) == 0 and
31 return self.was_baked and len(self.errors) == 0 27 len(sub['errors']) == 0)
32 28
33 def anyPass(self, func):
34 for pinfo in self.render_info:
35 if pinfo and func(pinfo):
36 return True
37 return False
38 29
39 def copyRenderInfo(self): 30 def was_subpage_baked(sub):
40 return copy.deepcopy(self.render_info) 31 return (sub['flags'] & SubPageFlags.FLAG_BAKED) != 0
32
33
34 def was_subpage_baked_successfully(sub):
35 return was_subpage_baked(sub) and len(sub['errors']) == 0
41 36
42 37
43 class PagePipelineRecordEntry(RecordEntry): 38 class PagePipelineRecordEntry(RecordEntry):
44 FLAG_NONE = 0 39 FLAG_NONE = 0
45 FLAG_NEW = 2**0 40 FLAG_NEW = 2**0
46 FLAG_SOURCE_MODIFIED = 2**1 41 FLAG_SOURCE_MODIFIED = 2**1
47 FLAG_OVERRIDEN = 2**2 42 FLAG_OVERRIDEN = 2**2
48 FLAG_COLLAPSED_FROM_LAST_RUN = 2**3 43 FLAG_COLLAPSED_FROM_LAST_RUN = 2**3
49 FLAG_IS_DRAFT = 2**4 44 FLAG_IS_DRAFT = 2**4
45 FLAG_ABORTED_FOR_SOURCE_USE = 2**5
50 46
51 def __init__(self): 47 def __init__(self):
52 super().__init__() 48 super().__init__()
53 self.flags = self.FLAG_NONE 49 self.flags = self.FLAG_NONE
54 self.config = None 50 self.config = None
51 self.route_params = None
55 self.timestamp = None 52 self.timestamp = None
56 self.subs = [] 53 self.subs = []
57 54
58 @property 55 @property
59 def was_touched(self): 56 def was_touched(self):
68 return len(self.subs) 65 return len(self.subs)
69 66
70 @property 67 @property
71 def was_any_sub_baked(self): 68 def was_any_sub_baked(self):
72 for o in self.subs: 69 for o in self.subs:
73 if o.was_baked: 70 if was_subpage_baked(o):
74 return True 71 return True
75 return False 72 return False
76 73
77 @property 74 @property
78 def has_any_error(self): 75 def has_any_error(self):
79 if len(self.errors) > 0: 76 if len(self.errors) > 0:
80 return True 77 return True
81 for o in self.subs: 78 for o in self.subs:
82 if len(o.errors) > 0: 79 if len(o['errors']) > 0:
83 return True 80 return True
84 return False 81 return False
85 82
86 def getSub(self, page_num): 83 def getSub(self, page_num):
87 return self.subs[page_num - 1] 84 return self.subs[page_num - 1]
88 85
89 def getAllErrors(self): 86 def getAllErrors(self):
90 yield from self.errors 87 yield from self.errors
91 for o in self.subs: 88 for o in self.subs:
92 yield from o.errors 89 yield from o['errors']
93 90
94 def getAllUsedSourceNames(self): 91 def getAllUsedSourceNames(self):
95 res = set() 92 res = set()
96 for o in self.subs: 93 for o in self.subs:
97 for pinfo in o.render_info: 94 pinfo = o.get('render_info')
98 if pinfo: 95 if pinfo:
99 res |= pinfo.used_source_names 96 res |= pinfo['used_source_names']
100 return res 97 return res
101 98
102 def getAllOutputPaths(self): 99 def getAllOutputPaths(self):
103 for o in self.subs: 100 for o in self.subs:
104 yield o.out_path 101 yield o['out_path']
105 102
106 def describe(self): 103 def describe(self):
107 d = super().describe() 104 d = super().describe()
108 d['Flags'] = get_flag_descriptions(self.flags, flag_descriptions) 105 d['Flags'] = get_flag_descriptions(self.flags, flag_descriptions)
109 for i, sub in enumerate(self.subs): 106 for i, sub in enumerate(self.subs):
110 d['Sub%02d' % i] = { 107 d['Sub%02d' % i] = {
111 'URI': sub.out_uri, 108 'URI': sub['out_uri'],
112 'Path': sub.out_path, 109 'Path': sub['out_path'],
113 'Flags': get_flag_descriptions( 110 'Flags': get_flag_descriptions(
114 sub.flags, sub_flag_descriptions), 111 sub['flags'], sub_flag_descriptions),
115 'RenderInfo': [ 112 'RenderInfo': _describe_render_info(sub['render_info'])
116 _describe_render_info(sub.render_info[0]),
117 _describe_render_info(sub.render_info[1])
118 ]
119 } 113 }
120 return d 114 return d
115
116
117 def add_page_job_result(result):
118 result.update({
119 'flags': PagePipelineRecordEntry.FLAG_NONE,
120 'errors': [],
121 'subs': []
122 })
123
124
125 def merge_job_result_into_record_entry(record_entry, result):
126 record_entry.flags |= result['flags']
127 record_entry.errors += result['errors']
128 record_entry.subs += result['subs']
121 129
122 130
123 flag_descriptions = { 131 flag_descriptions = {
124 PagePipelineRecordEntry.FLAG_NEW: 'new', 132 PagePipelineRecordEntry.FLAG_NEW: 'new',
125 PagePipelineRecordEntry.FLAG_SOURCE_MODIFIED: 'touched', 133 PagePipelineRecordEntry.FLAG_SOURCE_MODIFIED: 'touched',
126 PagePipelineRecordEntry.FLAG_OVERRIDEN: 'overriden', 134 PagePipelineRecordEntry.FLAG_OVERRIDEN: 'overriden',
127 PagePipelineRecordEntry.FLAG_COLLAPSED_FROM_LAST_RUN: 'from last run', 135 PagePipelineRecordEntry.FLAG_COLLAPSED_FROM_LAST_RUN: 'from last run',
128 PagePipelineRecordEntry.FLAG_IS_DRAFT: 'draft'} 136 PagePipelineRecordEntry.FLAG_IS_DRAFT: 'draft',
137 PagePipelineRecordEntry.FLAG_ABORTED_FOR_SOURCE_USE: 'aborted for source use'}
129 138
130 139
131 sub_flag_descriptions = { 140 sub_flag_descriptions = {
132 SubPagePipelineRecordEntry.FLAG_BAKED: 'baked', 141 SubPageFlags.FLAG_BAKED: 'baked',
133 SubPagePipelineRecordEntry.FLAG_FORCED_BY_SOURCE: 'forced by source', 142 SubPageFlags.FLAG_FORCED_BY_SOURCE: 'forced by source',
134 SubPagePipelineRecordEntry.FLAG_FORCED_BY_NO_PREVIOUS: 'forced b/c new', 143 SubPageFlags.FLAG_FORCED_BY_NO_PREVIOUS: 'forced b/c new',
135 SubPagePipelineRecordEntry.FLAG_FORCED_BY_PREVIOUS_ERRORS: 144 SubPageFlags.FLAG_FORCED_BY_PREVIOUS_ERRORS: 'forced by errors',
136 'forced by errors', 145 SubPageFlags.FLAG_FORCED_BY_GENERAL_FORCE: 'manually forced',
137 SubPagePipelineRecordEntry.FLAG_FORCED_BY_GENERAL_FORCE: 146 SubPageFlags.FLAG_RENDER_CACHE_INVALIDATED: 'cache invalidated'
138 'manually forced',
139 SubPagePipelineRecordEntry.FLAG_FORMATTING_INVALIDATED:
140 'formatting invalidated'
141 } 147 }
142 148
143 149
144 def _describe_render_info(ri): 150 def _describe_render_info(ri):
145 if ri is None: 151 if ri is None:
146 return '<null>' 152 return '<null>'
147 return { 153 return {
148 'UsedPagination': ri.used_pagination, 154 'UsedPagination': ri['used_pagination'],
149 'PaginationHasMore': ri.pagination_has_more, 155 'PaginationHasMore': ri['pagination_has_more'],
150 'UsedAssets': ri.used_assets, 156 'UsedAssets': ri['used_assets'],
151 'UsedSourceNames': ri.used_source_names 157 'UsedSourceNames': ri['used_source_names']
152 } 158 }