Mercurial > piecrust2
comparison piecrust/data/provider.py @ 440:32c7c2d219d2
performance: Refactor how data is managed to reduce copying.
* Make use of `collections.abc.Mapping` to better identify things that are
supposed to look like dictionaries.
* Instead of handling "overlay" of data in a dict tree in each different data
object, make all objects `Mapping`s and handle merging at a higher level
with the new `MergedMapping` object.
* Since this new object is read-only, remove the need for deep-copying of
app and page configurations.
* Split data classes into separate modules.
author | Ludovic Chabant <ludovic@chabant.com> |
---|---|
date | Sun, 28 Jun 2015 08:22:39 -0700 |
parents | e7b865f8f335 |
children | 703ea5d76f33 |
comparison
equal
deleted
inserted
replaced
439:c0700c6d9545 | 440:32c7c2d219d2 |
---|---|
1 import time | 1 import time |
2 import collections.abc | |
2 from piecrust.data.iterators import PageIterator | 3 from piecrust.data.iterators import PageIterator |
3 from piecrust.sources.array import ArraySource | 4 from piecrust.sources.array import ArraySource |
4 | 5 |
5 | 6 |
6 class DataProvider(object): | 7 class DataProvider(object): |
7 debug_render_dynamic = ['_debugRenderUserData'] | 8 debug_render_dynamic = [] |
8 debug_render_invoke_dynamic = ['_debugRenderUserData'] | 9 debug_render_invoke_dynamic = [] |
9 | 10 |
10 def __init__(self, source, page, user_data): | 11 def __init__(self, source, page, override): |
11 if source.app is not page.app: | 12 if source.app is not page.app: |
12 raise Exception("The given source and page don't belong to " | 13 raise Exception("The given source and page don't belong to " |
13 "the same application.") | 14 "the same application.") |
14 self._source = source | 15 self._source = source |
15 self._page = page | 16 self._page = page |
16 self._user_data = user_data | |
17 | |
18 def __getattr__(self, name): | |
19 if self._user_data is not None: | |
20 try: | |
21 return self._user_data[name] | |
22 except KeyError: | |
23 pass | |
24 raise AttributeError() | |
25 | |
26 def __getitem__(self, name): | |
27 if self._user_data is not None: | |
28 return self._user_data[name] | |
29 raise KeyError() | |
30 | |
31 def _debugRenderUserData(self): | |
32 if self._user_data: | |
33 return list(self._user_data.keys()) | |
34 return [] | |
35 | 17 |
36 | 18 |
37 class IteratorDataProvider(DataProvider): | 19 class IteratorDataProvider(DataProvider): |
38 PROVIDER_NAME = 'iterator' | 20 PROVIDER_NAME = 'iterator' |
39 | 21 |
40 debug_render_doc_dynamic = ['_debugRenderDoc'] | 22 debug_render_doc_dynamic = ['_debugRenderDoc'] |
41 debug_render_not_empty = True | 23 debug_render_not_empty = True |
42 | 24 |
43 def __init__(self, source, page, user_data): | 25 def __init__(self, source, page, override): |
26 super(IteratorDataProvider, self).__init__(source, page, override) | |
27 | |
44 self._innerIt = None | 28 self._innerIt = None |
45 if isinstance(user_data, IteratorDataProvider): | 29 if isinstance(override, IteratorDataProvider): |
46 # Iterator providers can be chained, like for instance with | 30 # Iterator providers can be chained, like for instance with |
47 # `site.pages` listing both the theme pages and the user site's | 31 # `site.pages` listing both the theme pages and the user site's |
48 # pages. | 32 # pages. |
49 self._innerIt = user_data | 33 self._innerIt = override |
50 user_data = None | 34 |
51 | |
52 super(IteratorDataProvider, self).__init__(source, page, user_data) | |
53 self._pages = PageIterator(source, current_page=page) | 35 self._pages = PageIterator(source, current_page=page) |
54 self._pages._iter_event += self._onIteration | 36 self._pages._iter_event += self._onIteration |
55 self._ctx_set = False | 37 self._ctx_set = False |
56 | 38 |
57 def __len__(self): | 39 def __len__(self): |
73 | 55 |
74 def _debugRenderDoc(self): | 56 def _debugRenderDoc(self): |
75 return 'Provides a list of %d items' % len(self) | 57 return 'Provides a list of %d items' % len(self) |
76 | 58 |
77 | 59 |
78 class BlogDataProvider(DataProvider): | 60 class BlogDataProvider(DataProvider, collections.abc.Mapping): |
79 PROVIDER_NAME = 'blog' | 61 PROVIDER_NAME = 'blog' |
80 | 62 |
81 debug_render_doc = """Provides a list of blog posts and yearly/monthly | 63 debug_render_doc = """Provides a list of blog posts and yearly/monthly |
82 archives.""" | 64 archives.""" |
83 debug_render = ['posts', 'years', 'months'] | |
84 debug_render_dynamic = (['_debugRenderTaxonomies'] + | 65 debug_render_dynamic = (['_debugRenderTaxonomies'] + |
85 DataProvider.debug_render_dynamic) | 66 DataProvider.debug_render_dynamic) |
86 | 67 |
87 def __init__(self, source, page, user_data): | 68 def __init__(self, source, page, override): |
88 super(BlogDataProvider, self).__init__(source, page, user_data) | 69 super(BlogDataProvider, self).__init__(source, page, override) |
89 self._yearly = None | 70 self._yearly = None |
90 self._monthly = None | 71 self._monthly = None |
91 self._taxonomies = {} | 72 self._taxonomies = {} |
92 self._ctx_set = False | 73 self._ctx_set = False |
93 | 74 |
94 def __getattr__(self, name): | 75 def __getitem__(self, name): |
95 if self._source.app.getTaxonomy(name) is not None: | 76 if name == 'posts': |
77 return self._posts() | |
78 elif name == 'years': | |
79 return self._buildYearlyArchive() | |
80 elif name == 'months': | |
81 return self._buildMonthlyArchive() | |
82 elif self._source.app.getTaxonomy(name) is not None: | |
96 return self._buildTaxonomy(name) | 83 return self._buildTaxonomy(name) |
97 return super(BlogDataProvider, self).__getattr__(name) | 84 raise KeyError("No such item: %s" % name) |
98 | 85 |
99 @property | 86 def __iter__(self): |
100 def posts(self): | 87 keys = ['posts', 'years', 'months'] |
88 keys += [t.name for t in self._source.app.taxonomies] | |
89 return iter(keys) | |
90 | |
91 def __len__(self): | |
92 return 3 + len(self._source.app.taxonomies) | |
93 | |
94 def _debugRenderTaxonomies(self): | |
95 return [t.name for t in self._source.app.taxonomies] | |
96 | |
97 def _posts(self): | |
101 it = PageIterator(self._source, current_page=self._page) | 98 it = PageIterator(self._source, current_page=self._page) |
102 it._iter_event += self._onIteration | 99 it._iter_event += self._onIteration |
103 return it | 100 return it |
104 | |
105 @property | |
106 def years(self): | |
107 return self._buildYearlyArchive() | |
108 | |
109 @property | |
110 def months(self): | |
111 return self._buildMonthlyArchive() | |
112 | |
113 def _debugRenderTaxonomies(self): | |
114 return [t.name for t in self._source.app.taxonomies] | |
115 | 101 |
116 def _buildYearlyArchive(self): | 102 def _buildYearlyArchive(self): |
117 if self._yearly is not None: | 103 if self._yearly is not None: |
118 return self._yearly | 104 return self._yearly |
119 | 105 |