Mercurial > piecrust2
comparison piecrust/sources/pageref.py @ 411:e7b865f8f335
bake: Enable multiprocess baking.
Baking is now done by running a worker per CPU, and sending jobs to them.
This changes several things across the codebase:
* Ability to not cache things related to pages other than the 'main' page
(i.e. the page at the bottom of the execution stack).
* Decouple the baking process from the bake records, so only the main process
keeps track (and modifies) the bake record.
* Remove the need for 'batch page getters' and loading a page directly from
the page factories.
There are various smaller changes too included here, including support for
scope performance timers that are saved with the bake record and can be
printed out to the console. Yes I got carried away.
For testing, the in-memory 'mock' file-system doesn't work anymore, since
we're spawning processes, so this is replaced by a 'tmpfs' file-system which
is saved in temporary files on disk and deleted after tests have run.
author | Ludovic Chabant <ludovic@chabant.com> |
---|---|
date | Fri, 12 Jun 2015 17:09:19 -0700 |
parents | 4b1019bb2533 |
children | ab5c6a8ae90a |
comparison
equal
deleted
inserted
replaced
410:d1a472464e57 | 411:e7b865f8f335 |
---|---|
40 except PageNotFoundError: | 40 except PageNotFoundError: |
41 return False | 41 return False |
42 | 42 |
43 @property | 43 @property |
44 def source_name(self): | 44 def source_name(self): |
45 self._checkHits() | |
46 return self._first_valid_hit.source_name | 45 return self._first_valid_hit.source_name |
47 | 46 |
48 @property | 47 @property |
49 def source(self): | 48 def source(self): |
50 return self.app.getSource(self.source_name) | 49 return self.app.getSource(self.source_name) |
51 | 50 |
52 @property | 51 @property |
53 def rel_path(self): | 52 def rel_path(self): |
54 self._checkHits() | |
55 return self._first_valid_hit.rel_path | 53 return self._first_valid_hit.rel_path |
56 | 54 |
57 @property | 55 @property |
58 def path(self): | 56 def path(self): |
59 self._checkHits() | |
60 return self._first_valid_hit.path | 57 return self._first_valid_hit.path |
61 | 58 |
62 @property | 59 @property |
63 def metadata(self): | 60 def metadata(self): |
64 self._checkHits() | |
65 return self._first_valid_hit.metadata | 61 return self._first_valid_hit.metadata |
66 | 62 |
67 @property | 63 @property |
68 def possible_rel_paths(self): | 64 def possible_ref_specs(self): |
69 self._load() | 65 self._load() |
70 return [h.rel_path for h in self._hits] | 66 return ['%s:%s' % (h.source_name, h.rel_path) for h in self._hits] |
67 | |
68 @property | |
69 def possible_split_ref_specs(self): | |
70 self._load() | |
71 return [(h.source_name, h.rel_path) for h in self._hits] | |
71 | 72 |
72 @property | 73 @property |
73 def possible_paths(self): | 74 def possible_paths(self): |
74 self._load() | 75 self._load() |
75 return [h.path for h in self._hits] | 76 return [h.path for h in self._hits] |
78 return PageFactory(self.source, self.rel_path, | 79 return PageFactory(self.source, self.rel_path, |
79 copy.deepcopy(self.metadata)) | 80 copy.deepcopy(self.metadata)) |
80 | 81 |
81 @property | 82 @property |
82 def _first_valid_hit(self): | 83 def _first_valid_hit(self): |
84 self._checkHits() | |
83 return self._hits[self._first_valid_hit_index] | 85 return self._hits[self._first_valid_hit_index] |
84 | 86 |
85 def _load(self): | 87 def _load(self): |
86 if self._hits is not None: | 88 if self._hits is not None: |
87 return | 89 return |
88 | 90 |
91 self._hits = [] | |
92 | |
93 if self._page_ref is None: | |
94 self._first_valid_hit_index = self._INDEX_NOT_FOUND | |
95 return | |
96 | |
89 it = list(page_ref_pattern.finditer(self._page_ref)) | 97 it = list(page_ref_pattern.finditer(self._page_ref)) |
90 if len(it) == 0: | 98 if len(it) == 0: |
91 raise Exception("Invalid page ref: %s" % self._page_ref) | 99 raise Exception("Invalid page ref: %s" % self._page_ref) |
92 | 100 |
93 self._hits = [] | |
94 for m in it: | 101 for m in it: |
95 source_name = m.group('src') | 102 source_name = m.group('src') |
96 source = self.app.getSource(source_name) | 103 source = self.app.getSource(source_name) |
97 if source is None: | 104 if source is None: |
98 raise Exception("No such source: %s" % source_name) | 105 raise Exception("No such source: %s" % source_name) |
109 self._HitInfo(source_name, rel_path, path, metadata)) | 116 self._HitInfo(source_name, rel_path, path, metadata)) |
110 | 117 |
111 def _checkHits(self): | 118 def _checkHits(self): |
112 if self._first_valid_hit_index >= 0: | 119 if self._first_valid_hit_index >= 0: |
113 return | 120 return |
121 | |
122 if self._first_valid_hit_index == self._INDEX_NEEDS_LOADING: | |
123 self._load() | |
124 self._first_valid_hit_index = self._INDEX_NOT_FOUND | |
125 for i, hit in enumerate(self._hits): | |
126 if os.path.isfile(hit.path): | |
127 self._first_valid_hit_index = i | |
128 break | |
129 | |
114 if self._first_valid_hit_index == self._INDEX_NOT_FOUND: | 130 if self._first_valid_hit_index == self._INDEX_NOT_FOUND: |
115 raise PageNotFoundError( | 131 raise PageNotFoundError( |
116 "No valid paths were found for page reference: %s" % | 132 "No valid paths were found for page reference: %s" % |
117 self._page_ref) | 133 self._page_ref) |
118 | 134 |
119 self._load() | |
120 self._first_valid_hit_index = self._INDEX_NOT_FOUND | |
121 for i, hit in enumerate(self._hits): | |
122 if os.path.isfile(hit.path): | |
123 self._first_valid_hit_index = i | |
124 break | |
125 |