Mercurial > piecrust2
comparison piecrust/data/base.py @ 12:30a42341cfa8
Define page slugs properly, avoid recursions with debug data.
author | Ludovic Chabant <ludovic@chabant.com> |
---|---|
date | Mon, 18 Aug 2014 16:49:54 -0700 |
parents | f5ca5c5bed85 |
children | ab6e7e0e9d44 |
comparison
equal
deleted
inserted
replaced
11:617191dec18e | 12:30a42341cfa8 |
---|---|
1 import time | 1 import time |
2 import logging | 2 import logging |
3 from piecrust.data.assetor import Assetor | 3 from piecrust.data.assetor import Assetor |
4 from piecrust.uriutil import get_slug | |
4 | 5 |
5 | 6 |
6 logger = logging.getLogger(__name__) | 7 logger = logging.getLogger(__name__) |
7 | 8 |
8 | 9 |
9 class IPaginationSource(object): | 10 class IPaginationSource(object): |
11 """ Defines the interface for a source that can be used as the data | |
12 for an iterator or a pagination. | |
13 """ | |
10 def getItemsPerPage(self): | 14 def getItemsPerPage(self): |
11 raise NotImplementedError() | 15 raise NotImplementedError() |
12 | 16 |
13 def getSourceIterator(self): | 17 def getSourceIterator(self): |
14 raise NotImplementedError() | 18 raise NotImplementedError() |
26 class LazyPageConfigData(object): | 30 class LazyPageConfigData(object): |
27 """ An object that represents the configuration header of a page, | 31 """ An object that represents the configuration header of a page, |
28 but also allows for additional data. It's meant to be exposed | 32 but also allows for additional data. It's meant to be exposed |
29 to the templating system. | 33 to the templating system. |
30 """ | 34 """ |
35 debug_render = [] | |
36 debug_render_dynamic = ['_debugRenderKeys'] | |
37 | |
31 def __init__(self, page): | 38 def __init__(self, page): |
32 self._page = page | 39 self._page = page |
33 self._values = None | 40 self._values = None |
34 self._loaders = None | 41 self._loaders = None |
35 | 42 |
36 @property | 43 @property |
37 def page(self): | 44 def page(self): |
38 return self._page | 45 return self._page |
39 | 46 |
47 def __getattr__(self, name): | |
48 try: | |
49 return self.getValue(name) | |
50 except KeyError: | |
51 raise AttributeError | |
52 | |
40 def __getitem__(self, name): | 53 def __getitem__(self, name): |
54 return self.getValue(name) | |
55 | |
56 def getValue(self, name): | |
41 self._load() | 57 self._load() |
42 | 58 |
43 if self._loaders: | 59 if self._loaders: |
44 loader = self._loaders.get(name) | 60 loader = self._loaders.get(name) |
45 if loader is not None: | 61 if loader is not None: |
46 try: | 62 try: |
47 self._values[name] = loader(self, name) | 63 self._values[name] = loader(self, name) |
48 except Exception as ex: | 64 except Exception as ex: |
49 logger.error("Error while loading attribute '%s' for: %s" | 65 raise Exception("Error while loading attribute '%s' for: %s" |
50 % (name, self._page.path)) | 66 % (name, self._page.rel_path)) from ex |
51 logger.exception(ex) | |
52 raise Exception("Internal Error: %s" % ex) | |
53 | 67 |
54 # We need to double-check `_loaders` here because | 68 # We need to double-check `_loaders` here because |
55 # the loader could have removed all loaders, which | 69 # the loader could have removed all loaders, which |
56 # would set this back to `None`. | 70 # would set this back to `None`. |
57 if self._loaders is not None: | 71 if self._loaders is not None: |
85 return | 99 return |
86 self._values = dict(self._page.config.get()) | 100 self._values = dict(self._page.config.get()) |
87 try: | 101 try: |
88 self._loadCustom() | 102 self._loadCustom() |
89 except Exception as ex: | 103 except Exception as ex: |
90 logger.error("Error while loading data for: %s" % self._page.path) | 104 raise Exception("Error while loading data for: %s" % self._page.rel_path) from ex |
91 logger.exception(ex) | |
92 raise Exception("Internal Error: %s" % ex) | |
93 | 105 |
94 def _loadCustom(self): | 106 def _loadCustom(self): |
95 pass | 107 pass |
108 | |
109 def _debugRenderKeys(self): | |
110 self._load() | |
111 keys = set(self._values.keys()) | |
112 if self._loaders: | |
113 keys |= set(self._loaders.keys()) | |
114 return list(keys) | |
96 | 115 |
97 | 116 |
98 class PaginationData(LazyPageConfigData): | 117 class PaginationData(LazyPageConfigData): |
99 def __init__(self, page): | 118 def __init__(self, page): |
100 super(PaginationData, self).__init__(page) | 119 super(PaginationData, self).__init__(page) |
107 return route.getUri(page.source_metadata) | 126 return route.getUri(page.source_metadata) |
108 | 127 |
109 def _loadCustom(self): | 128 def _loadCustom(self): |
110 page_url = self._get_uri() | 129 page_url = self._get_uri() |
111 self.setValue('url', page_url) | 130 self.setValue('url', page_url) |
112 self.setValue('slug', page_url) | 131 self.setValue('slug', get_slug(self._page.app, page_url)) |
113 self.setValue('timestamp', | 132 self.setValue('timestamp', |
114 time.mktime(self.page.datetime.timetuple())) | 133 time.mktime(self.page.datetime.timetuple())) |
115 date_format = self.page.app.config.get('site/date_format') | 134 date_format = self.page.app.config.get('site/date_format') |
116 if date_format: | 135 if date_format: |
117 self.setValue('date', self.page.datetime.strftime(date_format)) | 136 self.setValue('date', self.page.datetime.strftime(date_format)) |
122 segment_names = self.page.config.get('segments') | 141 segment_names = self.page.config.get('segments') |
123 for name in segment_names: | 142 for name in segment_names: |
124 self.mapLoader(name, self._load_rendered_segment) | 143 self.mapLoader(name, self._load_rendered_segment) |
125 | 144 |
126 def _load_rendered_segment(self, data, name): | 145 def _load_rendered_segment(self, data, name): |
127 from piecrust.rendering import PageRenderingContext, render_page_segments | 146 do_render = True |
147 eis = self._page.app.env.exec_info_stack | |
148 if eis is not None and eis.hasPage(self._page): | |
149 # This is the pagination data for the page that is currently | |
150 # being rendered! Inception! But this is possible... so just | |
151 # prevent infinite recursion. | |
152 do_render = False | |
128 | 153 |
129 assert self is data | 154 assert self is data |
130 uri = self._get_uri() | 155 |
131 try: | 156 if do_render: |
132 ctx = PageRenderingContext(self._page, uri) | 157 uri = self._get_uri() |
133 segs = render_page_segments(ctx) | 158 try: |
134 except Exception as e: | 159 from piecrust.rendering import (PageRenderingContext, |
135 logger.exception("Error rendering segments for '%s': %s" % (uri, e)) | 160 render_page_segments) |
136 raise | 161 ctx = PageRenderingContext(self._page, uri) |
162 segs = render_page_segments(ctx) | |
163 except Exception as e: | |
164 raise Exception("Error rendering segments for '%s'" % uri) from e | |
165 else: | |
166 segs = {} | |
167 for name in self.page.config.get('segments'): | |
168 segs[name] = "<unavailable: current page>" | |
137 | 169 |
138 for k, v in segs.items(): | 170 for k, v in segs.items(): |
139 self.mapLoader(k, None) | 171 self.mapLoader(k, None) |
140 self.setValue(k, v) | 172 self.setValue(k, v) |
141 | 173 |