Mercurial > piecrust2
comparison piecrust/environment.py @ 852:4850f8c21b6e
core: Start of the big refactor for PieCrust 3.0.
* Everything is a `ContentSource`, including assets directories.
* Most content sources are subclasses of the base file-system source.
* A source is processed by a "pipeline", and there are 2 built-in pipelines,
one for assets and one for pages. The asset pipeline is vaguely functional,
but the page pipeline is completely broken right now.
* Rewrite the baking process as just running appropriate pipelines on each
content item. This should allow for better parallelization.
author | Ludovic Chabant <ludovic@chabant.com> |
---|---|
date | Wed, 17 May 2017 00:11:48 -0700 |
parents | 61d606fbc313 |
children | 08e02c2a2a1a |
comparison
equal
deleted
inserted
replaced
851:2c7e57d80bba | 852:4850f8c21b6e |
---|---|
1 import time | 1 import time |
2 import logging | 2 import logging |
3 import contextlib | 3 import contextlib |
4 from piecrust.cache import MemCache | |
5 | 4 |
6 | 5 |
7 logger = logging.getLogger(__name__) | 6 logger = logging.getLogger(__name__) |
8 | 7 |
9 | 8 |
10 class AbortedSourceUseError(Exception): | 9 class ExecutionStats: |
11 pass | |
12 | |
13 | |
14 class ExecutionInfo(object): | |
15 def __init__(self, page, render_ctx): | |
16 self.page = page | |
17 self.render_ctx = render_ctx | |
18 self.was_cache_valid = False | |
19 self.start_time = time.perf_counter() | |
20 | |
21 | |
22 class ExecutionInfoStack(object): | |
23 def __init__(self): | |
24 self._page_stack = [] | |
25 | |
26 @property | |
27 def current_page_info(self): | |
28 if len(self._page_stack) == 0: | |
29 return None | |
30 return self._page_stack[-1] | |
31 | |
32 @property | |
33 def is_main_page(self): | |
34 return len(self._page_stack) == 1 | |
35 | |
36 def hasPage(self, page): | |
37 for ei in self._page_stack: | |
38 if ei.page == page: | |
39 return True | |
40 return False | |
41 | |
42 def pushPage(self, page, render_ctx): | |
43 if len(self._page_stack) > 0: | |
44 top = self._page_stack[-1] | |
45 assert top.page is not page | |
46 self._page_stack.append(ExecutionInfo(page, render_ctx)) | |
47 | |
48 def popPage(self): | |
49 del self._page_stack[-1] | |
50 | |
51 def clear(self): | |
52 self._page_stack = [] | |
53 | |
54 | |
55 class ExecutionStats(object): | |
56 def __init__(self): | 10 def __init__(self): |
57 self.timers = {} | 11 self.timers = {} |
58 self.counters = {} | 12 self.counters = {} |
59 self.manifests = {} | 13 self.manifests = {} |
60 | 14 |
61 def registerTimer(self, category, *, raise_if_registered=True): | 15 def registerTimer(self, category, *, |
16 raise_if_registered=True, time=0): | |
62 if raise_if_registered and category in self.timers: | 17 if raise_if_registered and category in self.timers: |
63 raise Exception("Timer '%s' has already been registered." % | 18 raise Exception("Timer '%s' has already been registered." % |
64 category) | 19 category) |
65 self.timers[category] = 0 | 20 self.timers[category] = time |
66 | 21 |
67 @contextlib.contextmanager | 22 @contextlib.contextmanager |
68 def timerScope(self, category): | 23 def timerScope(self, category): |
69 start = time.perf_counter() | 24 start = time.perf_counter() |
70 yield | 25 yield |
104 for oc, ov in other.manifests.items(): | 59 for oc, ov in other.manifests.items(): |
105 v = self.manifests.setdefault(oc, []) | 60 v = self.manifests.setdefault(oc, []) |
106 self.manifests[oc] = v + ov | 61 self.manifests[oc] = v + ov |
107 | 62 |
108 | 63 |
109 class Environment(object): | 64 class Environment: |
110 def __init__(self): | 65 def __init__(self): |
66 from piecrust.cache import MemCache | |
67 from piecrust.rendering import RenderingContextStack | |
68 | |
111 self.app = None | 69 self.app = None |
112 self.start_time = None | 70 self.start_time = None |
113 self.exec_info_stack = ExecutionInfoStack() | |
114 self.was_cache_cleaned = False | 71 self.was_cache_cleaned = False |
115 self.base_asset_url_format = '%uri%' | |
116 self.page_repository = MemCache() | 72 self.page_repository = MemCache() |
117 self.rendered_segments_repository = MemCache() | 73 self.rendered_segments_repository = MemCache() |
118 self.fs_caches = { | 74 self.render_ctx_stack = RenderingContextStack() |
119 'renders': self.rendered_segments_repository} | |
120 self.fs_cache_only_for_main_page = False | 75 self.fs_cache_only_for_main_page = False |
121 self.abort_source_use = False | 76 self.abort_source_use = False |
122 self._default_layout_extensions = None | 77 self._default_layout_extensions = None |
123 self._stats = ExecutionStats() | 78 self._stats = ExecutionStats() |
124 | 79 |
125 @property | 80 @property |
126 def default_layout_extensions(self): | 81 def stats(self): |
127 if self._default_layout_extensions is not None: | 82 return self._stats |
128 return self._default_layout_extensions | |
129 | |
130 if self.app is None: | |
131 raise Exception("This environment has not been initialized yet.") | |
132 | |
133 from piecrust.rendering import get_template_engine | |
134 dte = get_template_engine(self.app, None) | |
135 self._default_layout_extensions = ['.' + e.lstrip('.') | |
136 for e in dte.EXTENSIONS] | |
137 return self._default_layout_extensions | |
138 | 83 |
139 def initialize(self, app): | 84 def initialize(self, app): |
140 self.app = app | 85 self.app = app |
141 self.start_time = time.perf_counter() | 86 self.start_time = time.perf_counter() |
142 self.exec_info_stack.clear() | |
143 self.was_cache_cleaned = False | |
144 self.base_asset_url_format = '%uri%' | |
145 | 87 |
146 for name, repo in self.fs_caches.items(): | 88 self.rendered_segments_repository.fs_cache = \ |
147 cache = app.cache.getCache(name) | 89 app.cache.getCache('renders') |
148 repo.fs_cache = cache | |
149 | 90 |
150 def registerTimer(self, category, *, raise_if_registered=True): | 91 def _mergeCacheStats(self): |
151 self._stats.registerTimer( | |
152 category, raise_if_registered=raise_if_registered) | |
153 | |
154 def timerScope(self, category): | |
155 return self._stats.timerScope(category) | |
156 | |
157 def stepTimer(self, category, value): | |
158 self._stats.stepTimer(category, value) | |
159 | |
160 def stepTimerSince(self, category, since): | |
161 self._stats.stepTimerSince(category, since) | |
162 | |
163 def registerCounter(self, category, *, raise_if_registered=True): | |
164 self._stats.registerCounter( | |
165 category, raise_if_registered=raise_if_registered) | |
166 | |
167 def stepCounter(self, category, inc=1): | |
168 self._stats.stepCounter(category, inc) | |
169 | |
170 def registerManifest(self, name, *, raise_if_registered=True): | |
171 self._stats.registerManifest( | |
172 name, raise_if_registered=raise_if_registered) | |
173 | |
174 def addManifestEntry(self, name, entry): | |
175 self._stats.addManifestEntry(name, entry) | |
176 | |
177 def getStats(self): | |
178 repos = [ | 92 repos = [ |
179 ('RenderedSegmentsRepo', self.rendered_segments_repository), | 93 ('RenderedSegmentsRepo', self.rendered_segments_repository), |
180 ('PagesRepo', self.page_repository)] | 94 ('PagesRepo', self.page_repository)] |
181 for name, repo in repos: | 95 for name, repo in repos: |
182 self._stats.counters['%s_hit' % name] = repo._hits | 96 self._stats.counters['%s_hit' % name] = repo._hits |
183 self._stats.counters['%s_miss' % name] = repo._misses | 97 self._stats.counters['%s_miss' % name] = repo._misses |
184 self._stats.manifests['%s_missedKeys' % name] = list(repo._missed_keys) | 98 self._stats.manifests['%s_missedKeys' % name] = \ |
185 return self._stats | 99 list(repo._missed_keys) |
186 | 100 |
187 | 101 |
188 class StandardEnvironment(Environment): | 102 class StandardEnvironment(Environment): |
189 def __init__(self): | 103 def __init__(self): |
190 super(StandardEnvironment, self).__init__() | 104 super(StandardEnvironment, self).__init__() |