comparison piecrust/data/assetor.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 ad8f48a31c62
children f070a4fc033c
comparison
equal deleted inserted replaced
851:2c7e57d80bba 852:4850f8c21b6e
1 import os 1 import os
2 import os.path 2 import os.path
3 import shutil 3 import shutil
4 import logging 4 import logging
5 from piecrust import ASSET_DIR_SUFFIX 5 from piecrust import ASSET_DIR_SUFFIX
6 from piecrust.sources.base import REL_ASSETS
6 from piecrust.uriutil import multi_replace 7 from piecrust.uriutil import multi_replace
7 8
8 9
9 logger = logging.getLogger(__name__) 10 logger = logging.getLogger(__name__)
10 11
12 class UnsupportedAssetsError(Exception): 13 class UnsupportedAssetsError(Exception):
13 pass 14 pass
14 15
15 16
16 def build_base_url(app, uri, rel_assets_path): 17 def build_base_url(app, uri, rel_assets_path):
17 base_url_format = app.env.base_asset_url_format 18 base_url_format = app.config.get('site/base_asset_url_format')
18 rel_assets_path = rel_assets_path.replace('\\', '/') 19 rel_assets_path = rel_assets_path.replace('\\', '/')
19 20
20 # Remove any extension since we'll be copying assets into the 1st 21 # Remove any extension since we'll be copying assets into the 1st
21 # sub-page's folder. 22 # sub-page's folder.
22 pretty = app.config.get('site/pretty_urls') 23 pretty = app.config.get('site/pretty_urls')
23 if not pretty: 24 if not pretty:
24 uri, _ = os.path.splitext(uri) 25 uri, _ = os.path.splitext(uri)
25 26
26 base_url = multi_replace( 27 base_url = multi_replace(
27 base_url_format, 28 base_url_format,
28 { 29 {
29 '%path%': rel_assets_path, 30 '%path%': rel_assets_path,
30 '%uri%': uri}) 31 '%uri%': uri})
31 32
32 return base_url.rstrip('/') + '/' 33 return base_url.rstrip('/') + '/'
33 34
34 35
35 class AssetorBase(object): 36 class Assetor(object):
37 debug_render_doc = """Helps render URLs to files in the current page's
38 asset folder."""
39 debug_render = []
40 debug_render_dynamic = ['_debugRenderAssetNames']
41
36 def __init__(self, page, uri): 42 def __init__(self, page, uri):
37 self._page = page 43 self._page = page
38 self._uri = uri 44 self._uri = uri
39 self._cache = None 45 self._cache = None
40 46
63 69
64 def _cacheAssets(self): 70 def _cacheAssets(self):
65 if self._cache is not None: 71 if self._cache is not None:
66 return 72 return
67 73
68 self._cache = dict(self.findAssets()) 74 self._cache = self.findAssets() or {}
69 75
70 def findAssets(self): 76 def findAssets(self):
71 raise NotImplementedError() 77 content_item = self._page.content_item
78 source = content_item.source
79 assets = source.getRelatedContent(content_item, REL_ASSETS)
80 if assets is None:
81 return {}
72 82
73 def copyAssets(self, dest_dir): 83 app = source.app
74 raise NotImplementedError() 84 stack = app.env.render_ctx_stack
85 cur_ctx = stack.current_ctx
86 if cur_ctx is not None:
87 cur_ctx.current_pass_info.used_assets = True
75 88
76 class Assetor(AssetorBase): 89 # base_url = build_base_url(app, self._uri, rel_assets_dir)
77 debug_render_doc = """Helps render URLs to files in the current page's
78 asset folder."""
79 debug_render = []
80 debug_render_dynamic = ['_debugRenderAssetNames']
81
82 def findAssets(self):
83 assets = {}
84 name, ext = os.path.splitext(self._page.path)
85 assets_dir = name + ASSET_DIR_SUFFIX
86 if not os.path.isdir(assets_dir):
87 return assets
88
89 rel_assets_dir = os.path.relpath(assets_dir, self._page.app.root_dir)
90 base_url = build_base_url(self._page.app, self._uri, rel_assets_dir)
91 for fn in os.listdir(assets_dir):
92 full_fn = os.path.join(assets_dir, fn)
93 if not os.path.isfile(full_fn):
94 raise Exception("Skipping: %s" % full_fn)
95 continue
96
97 name, ext = os.path.splitext(fn)
98 if name in assets:
99 raise UnsupportedAssetsError(
100 "Multiple asset files are named '%s'." % name)
101 assets[name] = (base_url + fn, full_fn)
102
103 cpi = self._page.app.env.exec_info_stack.current_page_info
104 if cpi is not None:
105 cpi.render_ctx.current_pass_info.used_assets = True
106 90
107 return assets 91 return assets
108 92
109 def copyAssets(self, dest_dir): 93 def copyAssets(self, dest_dir):
110 page_pathname, _ = os.path.splitext(self._page.path) 94 page_pathname, _ = os.path.splitext(self._page.path)
113 full_fn = os.path.join(in_assets_dir, fn) 97 full_fn = os.path.join(in_assets_dir, fn)
114 if os.path.isfile(full_fn): 98 if os.path.isfile(full_fn):
115 dest_ap = os.path.join(dest_dir, fn) 99 dest_ap = os.path.join(dest_dir, fn)
116 logger.debug(" %s -> %s" % (full_fn, dest_ap)) 100 logger.debug(" %s -> %s" % (full_fn, dest_ap))
117 shutil.copy(full_fn, dest_ap) 101 shutil.copy(full_fn, dest_ap)
102