comparison piecrust/serving.py @ 133:9e4c2e68a129

Optimize server for files that already exist. * Only try to find new assets if no previously existing asset or page could be used. * Tidy up a bit the API for passing and returning bake/process records. * Put the process record in its place.
author Ludovic Chabant <ludovic@chabant.com>
date Tue, 18 Nov 2014 21:32:04 -0800
parents 7f81c84f7ddb
children 50b65c700c96
comparison
equal deleted inserted replaced
132:3834e2ef0cf2 133:9e4c2e68a129
14 from jinja2 import FileSystemLoader, Environment 14 from jinja2 import FileSystemLoader, Environment
15 from piecrust.app import PieCrust 15 from piecrust.app import PieCrust
16 from piecrust.data.filters import (PaginationFilter, HasFilterClause, 16 from piecrust.data.filters import (PaginationFilter, HasFilterClause,
17 IsFilterClause) 17 IsFilterClause)
18 from piecrust.environment import StandardEnvironment 18 from piecrust.environment import StandardEnvironment
19 from piecrust.page import Page
20 from piecrust.processing.base import ProcessorPipeline 19 from piecrust.processing.base import ProcessorPipeline
21 from piecrust.rendering import PageRenderingContext, render_page 20 from piecrust.rendering import PageRenderingContext, render_page
22 from piecrust.sources.base import PageFactory, MODE_PARSING 21 from piecrust.sources.base import PageFactory, MODE_PARSING
23 22
24 23
123 # It's not an asset we know of... let's see if it can be a page asset. 122 # It's not an asset we know of... let's see if it can be a page asset.
124 response = self._try_serve_page_asset(app, environ, request) 123 response = self._try_serve_page_asset(app, environ, request)
125 if response is not None: 124 if response is not None:
126 return response(environ, start_response) 125 return response(environ, start_response)
127 126
128 # Nope. Let's hope it's an actual page. 127 # Nope. Let's see if it's an actual page.
128 # We trap any exception that says "there's no such page" so we can
129 # try another thing before bailing out. But we let any exception
130 # that says "something's wrong" through.
131 exc = None
129 try: 132 try:
130 response = self._try_serve_page(app, environ, request) 133 response = self._try_serve_page(app, environ, request)
131 return response(environ, start_response) 134 return response(environ, start_response)
132 except HTTPException as ex:
133 raise
134 except (RouteNotFoundError, SourceNotFoundError) as ex: 135 except (RouteNotFoundError, SourceNotFoundError) as ex:
135 logger.exception(ex) 136 logger.exception(ex)
136 raise NotFound() 137 exc = NotFound(str(ex))
138 except NotFound as ex:
139 exc = ex
140 except HTTPException:
141 raise
137 except Exception as ex: 142 except Exception as ex:
138 if app.debug: 143 if app.debug:
139 logger.exception(ex) 144 logger.exception(ex)
140 raise 145 raise
141 msg = str(ex) 146 msg = str(ex)
142 logger.error(msg) 147 logger.error(msg)
143 raise InternalServerError(msg) 148 raise InternalServerError(msg)
144 149
150 # Nothing worked so far... let's see if there's a new asset.
151 response = self._try_serve_new_asset(app, environ, request)
152 if response is not None:
153 return response(environ, start_response)
154
155 # Nope. Raise the exception we had in store.
156 raise exc
157
145 def _try_serve_asset(self, app, environ, request): 158 def _try_serve_asset(self, app, environ, request):
146 logger.debug("Searching for asset with path: %s" % request.path) 159 logger.debug("Searching %d entries for asset with path: %s" %
160 (len(self._asset_record.entries), request.path))
147 rel_req_path = request.path.lstrip('/').replace('/', os.sep) 161 rel_req_path = request.path.lstrip('/').replace('/', os.sep)
148 entry = self._asset_record.previous.findEntry(rel_req_path) 162 entry = self._asset_record.findEntry(rel_req_path)
149 do_synchronous_process = True 163 if entry is None:
164 # We don't know any asset that could have created this path.
165 # It could be a new asset that the user just created, but we'll
166 # check for that later.
167 return None
168
169 # Yep, we know about this URL because we processed an asset that
170 # maps to it... make sure it's up to date by re-processing it
171 # before serving.
150 mounts = app.assets_dirs 172 mounts = app.assets_dirs
151 if entry is None: 173 asset_in_path = entry.path
152 # We don't know any asset that could have created this path, 174 asset_out_path = os.path.join(self._out_dir, rel_req_path)
153 # but we'll see if there's a new asset that could fit. 175
176 if self.synchronous_asset_pipeline:
177 logger.debug("Making sure '%s' is up-to-date." % asset_in_path)
154 pipeline = ProcessorPipeline( 178 pipeline = ProcessorPipeline(
155 app, mounts, self._out_dir, 179 app, mounts, self._out_dir,
156 skip_patterns=self._skip_patterns, 180 skip_patterns=self._skip_patterns,
157 force_patterns=self._force_patterns) 181 force_patterns=self._force_patterns,
158 record = pipeline.run(new_only=True) 182 num_workers=1)
159 entry = record.current.findEntry(rel_req_path) 183 r = pipeline.run(asset_in_path, delete=False, save_record=False,
160 if entry is None: 184 previous_record=self._asset_record)
161 return None 185 assert len(r.entries) == 1
162 186 self._asset_record.replaceEntry(r.entries[0])
163 logger.debug("Found new asset: %s" % entry.path) 187
164 self._asset_record.addEntry(entry) 188 logger.debug("Serving %s" % asset_out_path)
165 do_synchronous_process = False 189 wrapper = wrap_file(environ, open(asset_out_path, 'rb'))
166 190 response = Response(wrapper)
167 # Yep, we know about this URL because we processed an asset that 191 _, ext = os.path.splitext(rel_req_path)
168 # maps to it... make sure it's up to date by re-processing it 192 response.mimetype = self._mimetype_map.get(
169 # before serving. 193 ext.lstrip('.'), 'text/plain')
170 asset_in_path = entry.path 194 return response
195
196 def _try_serve_new_asset(self, app, environ, request):
197 logger.debug("Searching for a new asset with path: %s" % request.path)
198 mounts = app.assets_dirs
199 pipeline = ProcessorPipeline(
200 app, mounts, self._out_dir,
201 skip_patterns=self._skip_patterns,
202 force_patterns=self._force_patterns)
203 r = pipeline.run(new_only=True, delete=False, save_record=False,
204 previous_record=self._asset_record)
205 for e in r.entries:
206 self._asset_record.addEntry(e)
207
208 rel_req_path = request.path.lstrip('/').replace('/', os.sep)
209 entry = self._asset_record.findEntry(rel_req_path)
210 if entry is None:
211 return None
212
171 asset_out_path = os.path.join(self._out_dir, rel_req_path) 213 asset_out_path = os.path.join(self._out_dir, rel_req_path)
172 214 logger.debug("Found new asset: %s" % entry.path)
173 if self.synchronous_asset_pipeline and do_synchronous_process:
174 pipeline = ProcessorPipeline(
175 app, mounts, self._out_dir,
176 skip_patterns=self._skip_patterns,
177 force_patterns=self._force_patterns)
178 pipeline.run(asset_in_path)
179
180 logger.debug("Serving %s" % asset_out_path) 215 logger.debug("Serving %s" % asset_out_path)
181 wrapper = wrap_file(environ, open(asset_out_path, 'rb')) 216 wrapper = wrap_file(environ, open(asset_out_path, 'rb'))
182 response = Response(wrapper) 217 response = Response(wrapper)
183 _, ext = os.path.splitext(rel_req_path) 218 _, ext = os.path.splitext(rel_req_path)
184 response.mimetype = self._mimetype_map.get( 219 response.mimetype = self._mimetype_map.get(