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__()