Mercurial > piecrust2
comparison piecrust/templating/jinja/environment.py @ 851:2c7e57d80bba
optimize: Don't load Jinja unless we need to.
author | Ludovic Chabant <ludovic@chabant.com> |
---|---|
date | Sat, 29 Apr 2017 21:42:22 -0700 |
parents | |
children | 08e02c2a2a1a |
comparison
equal
deleted
inserted
replaced
850:370e74941d32 | 851:2c7e57d80bba |
---|---|
1 import re | |
2 import time | |
3 import email.utils | |
4 import hashlib | |
5 import strict_rfc3339 | |
6 from jinja2 import Environment | |
7 from .extensions import get_highlight_css | |
8 from piecrust.data.paginator import Paginator | |
9 from piecrust.rendering import format_text | |
10 from piecrust.uriutil import multi_replace | |
11 | |
12 | |
13 class PieCrustEnvironment(Environment): | |
14 def __init__(self, app, *args, **kwargs): | |
15 self.app = app | |
16 | |
17 # Before we create the base Environement, let's figure out the options | |
18 # we want to pass to it. | |
19 twig_compatibility_mode = app.config.get('jinja/twig_compatibility') | |
20 | |
21 # Disable auto-reload when we're baking. | |
22 if app.config.get('baker/is_baking'): | |
23 kwargs.setdefault('auto_reload', False) | |
24 | |
25 # Let the user override most Jinja options via the site config. | |
26 for name in ['block_start_string', 'block_end_string', | |
27 'variable_start_string', 'variable_end_string', | |
28 'comment_start_string', 'comment_end_string', | |
29 'line_statement_prefix', 'line_comment_prefix', | |
30 'trim_blocks', 'lstrip_blocks', | |
31 'newline_sequence', 'keep_trailing_newline']: | |
32 val = app.config.get('jinja/' + name) | |
33 if val is not None: | |
34 kwargs.setdefault(name, val) | |
35 | |
36 # Twig trims blocks. | |
37 if twig_compatibility_mode is True: | |
38 kwargs['trim_blocks'] = True | |
39 | |
40 # All good! Create the Environment. | |
41 super(PieCrustEnvironment, self).__init__(*args, **kwargs) | |
42 | |
43 # Now add globals and filters. | |
44 self.globals.update({ | |
45 'now': get_now_date(), | |
46 'fail': raise_exception, | |
47 'highlight_css': get_highlight_css}) | |
48 | |
49 self.filters.update({ | |
50 'keys': get_dict_keys, | |
51 'values': get_dict_values, | |
52 'paginate': self._paginate, | |
53 'formatwith': self._formatWith, | |
54 'markdown': lambda v: self._formatWith(v, 'markdown'), | |
55 'textile': lambda v: self._formatWith(v, 'textile'), | |
56 'nocache': add_no_cache_parameter, | |
57 'wordcount': get_word_count, | |
58 'stripoutertag': strip_outer_tag, | |
59 'stripslash': strip_slash, | |
60 'titlecase': title_case, | |
61 'md5': make_md5, | |
62 'atomdate': get_xml_date, | |
63 'xmldate': get_xml_date, | |
64 'emaildate': get_email_date, | |
65 'date': get_date}) | |
66 | |
67 # Backwards compatibility with Twig. | |
68 if twig_compatibility_mode is True: | |
69 self.filters['raw'] = self.filters['safe'] | |
70 self.globals['pcfail'] = raise_exception | |
71 | |
72 def _paginate(self, value, items_per_page=5): | |
73 cpi = self.app.env.exec_info_stack.current_page_info | |
74 if cpi is None or cpi.page is None or cpi.render_ctx is None: | |
75 raise Exception("Can't paginate when no page has been pushed " | |
76 "on the execution stack.") | |
77 return Paginator(cpi.page, value, | |
78 page_num=cpi.render_ctx.page_num, | |
79 items_per_page=items_per_page) | |
80 | |
81 def _formatWith(self, value, format_name): | |
82 return format_text(self.app, format_name, value) | |
83 | |
84 | |
85 def raise_exception(msg): | |
86 raise Exception(msg) | |
87 | |
88 | |
89 def get_dict_keys(value): | |
90 if isinstance(value, list): | |
91 return [i[0] for i in value] | |
92 return value.keys() | |
93 | |
94 | |
95 def get_dict_values(value): | |
96 if isinstance(value, list): | |
97 return [i[1] for i in value] | |
98 return value.values() | |
99 | |
100 | |
101 def add_no_cache_parameter(value, param_name='t', param_value=None): | |
102 if not param_value: | |
103 param_value = time.time() | |
104 if '?' in value: | |
105 value += '&' | |
106 else: | |
107 value += '?' | |
108 value += '%s=%s' % (param_name, param_value) | |
109 return value | |
110 | |
111 | |
112 def get_word_count(value): | |
113 return len(value.split()) | |
114 | |
115 | |
116 def strip_outer_tag(value, tag=None): | |
117 tag_pattern = '[a-z]+[a-z0-9]*' | |
118 if tag is not None: | |
119 tag_pattern = re.escape(tag) | |
120 pat = r'^\<' + tag_pattern + r'\>(.*)\</' + tag_pattern + '>$' | |
121 m = re.match(pat, value) | |
122 if m: | |
123 return m.group(1) | |
124 return value | |
125 | |
126 | |
127 def strip_slash(value): | |
128 return value.rstrip('/') | |
129 | |
130 | |
131 def title_case(value): | |
132 return value.title() | |
133 | |
134 | |
135 def make_md5(value): | |
136 return hashlib.md5(value.lower().encode('utf8')).hexdigest() | |
137 | |
138 | |
139 def get_xml_date(value): | |
140 """ Formats timestamps like 1985-04-12T23:20:50.52Z | |
141 """ | |
142 if value == 'now': | |
143 value = time.time() | |
144 return strict_rfc3339.timestamp_to_rfc3339_localoffset(int(value)) | |
145 | |
146 | |
147 def get_email_date(value, localtime=False): | |
148 """ Formats timestamps like Fri, 09 Nov 2001 01:08:47 -0000 | |
149 """ | |
150 if value == 'now': | |
151 value = time.time() | |
152 return email.utils.formatdate(value, localtime=localtime) | |
153 | |
154 | |
155 def get_now_date(): | |
156 return time.time() | |
157 | |
158 | |
159 def get_date(value, fmt): | |
160 if value == 'now': | |
161 value = time.time() | |
162 if '%' not in fmt: | |
163 suggest = php_format_to_strftime_format(fmt) | |
164 if suggest != fmt: | |
165 suggest_message = ("You probably want a format that looks " | |
166 "like: '%s'." % suggest) | |
167 else: | |
168 suggest_message = ("We can't suggest a proper date format " | |
169 "for you right now, though.") | |
170 raise Exception("Got incorrect date format: '%s\n" | |
171 "PieCrust 1 date formats won't work in PieCrust 2. " | |
172 "%s\n" | |
173 "Please check the `strftime` formatting page here: " | |
174 "https://docs.python.org/3/library/datetime.html" | |
175 "#strftime-and-strptime-behavior" % | |
176 (fmt, suggest_message)) | |
177 return time.strftime(fmt, time.localtime(value)) | |
178 | |
179 | |
180 def php_format_to_strftime_format(fmt): | |
181 replacements = { | |
182 'd': '%d', | |
183 'D': '%a', | |
184 'j': '%d', | |
185 'l': '%A', | |
186 'w': '%w', | |
187 'z': '%j', | |
188 'W': '%W', | |
189 'F': '%B', | |
190 'm': '%m', | |
191 'M': '%b', | |
192 'n': '%m', | |
193 'y': '%Y', | |
194 'Y': '%y', | |
195 'g': '%I', | |
196 'G': '%H', | |
197 'h': '%I', | |
198 'H': '%H', | |
199 'i': '%M', | |
200 's': '%S', | |
201 'e': '%Z', | |
202 'O': '%z', | |
203 'c': '%Y-%m-%dT%H:%M:%SZ'} | |
204 return multi_replace(fmt, replacements) |