comparison piecrust/baking/single.py @ 702:c62d83e17abf

bake: Some more optimizations. Write the output files in a background thread to get away from the GIL.
author Ludovic Chabant <ludovic@chabant.com>
date Sat, 16 Apr 2016 22:50:07 -0700
parents 3e4049022869
children ab5c6a8ae90a
comparison
equal deleted inserted replaced
701:066d6156525c 702:c62d83e17abf
1 import os.path 1 import os.path
2 import queue
2 import shutil 3 import shutil
3 import logging 4 import logging
5 import threading
4 import urllib.parse 6 import urllib.parse
5 from piecrust import ASSET_DIR_SUFFIX 7 from piecrust import ASSET_DIR_SUFFIX
6 from piecrust.baking.records import SubPageBakeInfo 8 from piecrust.baking.records import SubPageBakeInfo
7 from piecrust.rendering import ( 9 from piecrust.rendering import (
8 QualifiedPage, PageRenderingContext, render_page, 10 QualifiedPage, PageRenderingContext, render_page,
13 logger = logging.getLogger(__name__) 15 logger = logging.getLogger(__name__)
14 16
15 17
16 class BakingError(Exception): 18 class BakingError(Exception):
17 pass 19 pass
20
21
22 def _text_writer(q):
23 while True:
24 item = q.get()
25 if item is not None:
26 out_path, txt = item
27 out_dir = os.path.dirname(out_path)
28 _ensure_dir_exists(out_dir)
29
30 with open(out_path, 'w', encoding='utf8') as fp:
31 fp.write(txt)
32
33 q.task_done()
34 else:
35 # Sentinel object, terminate the thread.
36 q.task_done()
37 break
18 38
19 39
20 class PageBaker(object): 40 class PageBaker(object):
21 def __init__(self, app, out_dir, force=False, copy_assets=True): 41 def __init__(self, app, out_dir, force=False, copy_assets=True):
22 self.app = app 42 self.app = app
23 self.out_dir = out_dir 43 self.out_dir = out_dir
24 self.force = force 44 self.force = force
25 self.copy_assets = copy_assets 45 self.copy_assets = copy_assets
26 self.site_root = app.config.get('site/root') 46 self.site_root = app.config.get('site/root')
27 self.pretty_urls = app.config.get('site/pretty_urls') 47 self.pretty_urls = app.config.get('site/pretty_urls')
48 self._writer_queue = queue.Queue()
49 self._writer = threading.Thread(
50 name='PageSerializer',
51 target=_text_writer,
52 args=(self._writer_queue,))
53 self._writer.start()
54
55 def shutdown(self):
56 self._writer_queue.put_nowait(None)
57 self._writer.join()
28 58
29 def getOutputPath(self, uri): 59 def getOutputPath(self, uri):
30 uri_root, uri_path = split_uri(self.app, uri) 60 uri_root, uri_path = split_uri(self.app, uri)
31 61
32 bake_path = [self.out_dir] 62 bake_path = [self.out_dir]
160 190
161 with self.app.env.timerScope("PageRender"): 191 with self.app.env.timerScope("PageRender"):
162 rp = render_page(ctx) 192 rp = render_page(ctx)
163 193
164 with self.app.env.timerScope("PageSerialize"): 194 with self.app.env.timerScope("PageSerialize"):
165 out_dir = os.path.dirname(out_path) 195 self._writer_queue.put_nowait((out_path, rp.content))
166 _ensure_dir_exists(out_dir)
167
168 with open(out_path, 'w', encoding='utf8') as fp:
169 fp.write(rp.content)
170 196
171 return rp 197 return rp
172 198
173 199
174 def _compute_force_flags(prev_sub_entry, sub_entry, dirty_source_names): 200 def _compute_force_flags(prev_sub_entry, sub_entry, dirty_source_names):