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