Armin Ronacher | 07bc684 | 2008-03-31 14:18:49 +0200 | [diff] [blame] | 1 | # -*- coding: utf-8 -*- |
| 2 | """ |
Armin Ronacher | 82b3f3d | 2008-03-31 20:01:08 +0200 | [diff] [blame] | 3 | jinja2.environment |
| 4 | ~~~~~~~~~~~~~~~~~~ |
Armin Ronacher | 07bc684 | 2008-03-31 14:18:49 +0200 | [diff] [blame] | 5 | |
| 6 | Provides a class that holds runtime and parsing time options. |
| 7 | |
| 8 | :copyright: 2007 by Armin Ronacher. |
| 9 | :license: BSD, see LICENSE for more details. |
| 10 | """ |
Armin Ronacher | ba3757b | 2008-04-16 19:43:16 +0200 | [diff] [blame] | 11 | import sys |
Armin Ronacher | 82b3f3d | 2008-03-31 20:01:08 +0200 | [diff] [blame] | 12 | from jinja2.lexer import Lexer |
Armin Ronacher | 0553093 | 2008-04-20 13:27:49 +0200 | [diff] [blame] | 13 | from jinja2.parser import Parser |
Armin Ronacher | bcb7c53 | 2008-04-11 16:30:34 +0200 | [diff] [blame] | 14 | from jinja2.optimizer import optimize |
| 15 | from jinja2.compiler import generate |
Armin Ronacher | c63243e | 2008-04-14 22:53:58 +0200 | [diff] [blame] | 16 | from jinja2.runtime import Undefined |
Armin Ronacher | ba3757b | 2008-04-16 19:43:16 +0200 | [diff] [blame] | 17 | from jinja2.debug import translate_exception |
Armin Ronacher | f59bac2 | 2008-04-20 13:11:43 +0200 | [diff] [blame] | 18 | from jinja2.utils import import_string |
Armin Ronacher | 82b3f3d | 2008-03-31 20:01:08 +0200 | [diff] [blame] | 19 | from jinja2.defaults import DEFAULT_FILTERS, DEFAULT_TESTS, DEFAULT_NAMESPACE |
Armin Ronacher | 07bc684 | 2008-03-31 14:18:49 +0200 | [diff] [blame] | 20 | |
| 21 | |
| 22 | class Environment(object): |
Armin Ronacher | 8edbe49 | 2008-04-10 20:43:43 +0200 | [diff] [blame] | 23 | """The Jinja environment. |
Armin Ronacher | 07bc684 | 2008-03-31 14:18:49 +0200 | [diff] [blame] | 24 | |
| 25 | The core component of Jinja is the `Environment`. It contains |
| 26 | important shared variables like configuration, filters, tests, |
| 27 | globals and others. |
| 28 | """ |
| 29 | |
Armin Ronacher | c63243e | 2008-04-14 22:53:58 +0200 | [diff] [blame] | 30 | #: if this environment is sandboxed. Modifying this variable won't make |
| 31 | #: the environment sandboxed though. For a real sandboxed environment |
| 32 | #: have a look at jinja2.sandbox |
| 33 | sandboxed = False |
| 34 | |
Armin Ronacher | 07bc684 | 2008-03-31 14:18:49 +0200 | [diff] [blame] | 35 | def __init__(self, |
| 36 | block_start_string='{%', |
| 37 | block_end_string='%}', |
| 38 | variable_start_string='{{', |
| 39 | variable_end_string='}}', |
| 40 | comment_start_string='{#', |
| 41 | comment_end_string='#}', |
Armin Ronacher | bf7c4ad | 2008-04-12 12:02:36 +0200 | [diff] [blame] | 42 | line_statement_prefix=None, |
Armin Ronacher | 07bc684 | 2008-03-31 14:18:49 +0200 | [diff] [blame] | 43 | trim_blocks=False, |
Armin Ronacher | fed44b5 | 2008-04-13 19:42:53 +0200 | [diff] [blame] | 44 | optimized=True, |
Armin Ronacher | c63243e | 2008-04-14 22:53:58 +0200 | [diff] [blame] | 45 | undefined=Undefined, |
Armin Ronacher | 18c6ca0 | 2008-04-17 10:03:29 +0200 | [diff] [blame] | 46 | loader=None, |
Armin Ronacher | 0553093 | 2008-04-20 13:27:49 +0200 | [diff] [blame] | 47 | extensions=(), |
Armin Ronacher | 18c6ca0 | 2008-04-17 10:03:29 +0200 | [diff] [blame] | 48 | finalize=unicode): |
Armin Ronacher | 8edbe49 | 2008-04-10 20:43:43 +0200 | [diff] [blame] | 49 | """Here the possible initialization parameters: |
Armin Ronacher | 07bc684 | 2008-03-31 14:18:49 +0200 | [diff] [blame] | 50 | |
| 51 | ========================= ============================================ |
| 52 | `block_start_string` the string marking the begin of a block. |
| 53 | this defaults to ``'{%'``. |
| 54 | `block_end_string` the string marking the end of a block. |
| 55 | defaults to ``'%}'``. |
| 56 | `variable_start_string` the string marking the begin of a print |
| 57 | statement. defaults to ``'{{'``. |
| 58 | `comment_start_string` the string marking the begin of a |
| 59 | comment. defaults to ``'{#'``. |
| 60 | `comment_end_string` the string marking the end of a comment. |
| 61 | defaults to ``'#}'``. |
Armin Ronacher | bf7c4ad | 2008-04-12 12:02:36 +0200 | [diff] [blame] | 62 | `line_statement_prefix` If given and a string, this will be used as |
| 63 | prefix for line based statements. See the |
| 64 | documentation for more details. |
Armin Ronacher | 07bc684 | 2008-03-31 14:18:49 +0200 | [diff] [blame] | 65 | `trim_blocks` If this is set to ``True`` the first newline |
| 66 | after a block is removed (block, not |
| 67 | variable tag!). Defaults to ``False``. |
Armin Ronacher | fed44b5 | 2008-04-13 19:42:53 +0200 | [diff] [blame] | 68 | `optimized` should the optimizer be enabled? Default is |
| 69 | ``True``. |
Armin Ronacher | c63243e | 2008-04-14 22:53:58 +0200 | [diff] [blame] | 70 | `undefined` a subclass of `Undefined` that is used to |
| 71 | represent undefined variables. |
Armin Ronacher | bcb7c53 | 2008-04-11 16:30:34 +0200 | [diff] [blame] | 72 | `loader` the loader which should be used. |
Armin Ronacher | 0553093 | 2008-04-20 13:27:49 +0200 | [diff] [blame] | 73 | `extensions` List of Jinja extensions to use. |
Armin Ronacher | f59bac2 | 2008-04-20 13:11:43 +0200 | [diff] [blame] | 74 | `finalize` A callable that finalizes the variable. Per |
| 75 | default this is `unicode`, other useful |
| 76 | builtin finalizers are `escape`. |
Armin Ronacher | 07bc684 | 2008-03-31 14:18:49 +0200 | [diff] [blame] | 77 | ========================= ============================================ |
| 78 | """ |
Armin Ronacher | 2e9396b | 2008-04-16 14:21:57 +0200 | [diff] [blame] | 79 | |
| 80 | # santity checks |
Armin Ronacher | c63243e | 2008-04-14 22:53:58 +0200 | [diff] [blame] | 81 | assert issubclass(undefined, Undefined), 'undefined must be ' \ |
| 82 | 'a subclass of undefined because filters depend on it.' |
Armin Ronacher | 2e9396b | 2008-04-16 14:21:57 +0200 | [diff] [blame] | 83 | assert block_start_string != variable_start_string != \ |
| 84 | comment_start_string, 'block, variable and comment ' \ |
| 85 | 'start strings must be different' |
Armin Ronacher | 07bc684 | 2008-03-31 14:18:49 +0200 | [diff] [blame] | 86 | |
| 87 | # lexer / parser information |
| 88 | self.block_start_string = block_start_string |
| 89 | self.block_end_string = block_end_string |
| 90 | self.variable_start_string = variable_start_string |
| 91 | self.variable_end_string = variable_end_string |
| 92 | self.comment_start_string = comment_start_string |
| 93 | self.comment_end_string = comment_end_string |
Armin Ronacher | bf7c4ad | 2008-04-12 12:02:36 +0200 | [diff] [blame] | 94 | self.line_statement_prefix = line_statement_prefix |
Armin Ronacher | 07bc684 | 2008-03-31 14:18:49 +0200 | [diff] [blame] | 95 | self.trim_blocks = trim_blocks |
Armin Ronacher | 0553093 | 2008-04-20 13:27:49 +0200 | [diff] [blame] | 96 | self.extensions = [] |
| 97 | for extension in extensions: |
Armin Ronacher | f59bac2 | 2008-04-20 13:11:43 +0200 | [diff] [blame] | 98 | if isinstance(extension, basestring): |
| 99 | extension = import_string(extension) |
Armin Ronacher | 0553093 | 2008-04-20 13:27:49 +0200 | [diff] [blame] | 100 | self.extensions.append(extension(self)) |
Armin Ronacher | f59bac2 | 2008-04-20 13:11:43 +0200 | [diff] [blame] | 101 | |
| 102 | # runtime information |
Armin Ronacher | c63243e | 2008-04-14 22:53:58 +0200 | [diff] [blame] | 103 | self.undefined = undefined |
Armin Ronacher | fed44b5 | 2008-04-13 19:42:53 +0200 | [diff] [blame] | 104 | self.optimized = optimized |
Armin Ronacher | 18c6ca0 | 2008-04-17 10:03:29 +0200 | [diff] [blame] | 105 | self.finalize = finalize |
Armin Ronacher | 07bc684 | 2008-03-31 14:18:49 +0200 | [diff] [blame] | 106 | |
| 107 | # defaults |
| 108 | self.filters = DEFAULT_FILTERS.copy() |
| 109 | self.tests = DEFAULT_TESTS.copy() |
| 110 | self.globals = DEFAULT_NAMESPACE.copy() |
Armin Ronacher | 0553093 | 2008-04-20 13:27:49 +0200 | [diff] [blame] | 111 | for extension in self.extensions: |
| 112 | extension.update_globals(self.globals) |
Armin Ronacher | 07bc684 | 2008-03-31 14:18:49 +0200 | [diff] [blame] | 113 | |
Armin Ronacher | bcb7c53 | 2008-04-11 16:30:34 +0200 | [diff] [blame] | 114 | # set the loader provided |
| 115 | self.loader = loader |
| 116 | |
Armin Ronacher | 07bc684 | 2008-03-31 14:18:49 +0200 | [diff] [blame] | 117 | # create lexer |
| 118 | self.lexer = Lexer(self) |
| 119 | |
Armin Ronacher | c63243e | 2008-04-14 22:53:58 +0200 | [diff] [blame] | 120 | def subscribe(self, obj, argument): |
| 121 | """Get an item or attribute of an object.""" |
| 122 | try: |
| 123 | return getattr(obj, str(argument)) |
| 124 | except (AttributeError, UnicodeError): |
| 125 | try: |
| 126 | return obj[argument] |
| 127 | except (TypeError, LookupError): |
Armin Ronacher | 9a82205 | 2008-04-17 18:44:07 +0200 | [diff] [blame] | 128 | return self.undefined(obj=obj, name=argument) |
Armin Ronacher | c63243e | 2008-04-14 22:53:58 +0200 | [diff] [blame] | 129 | |
Armin Ronacher | ba3757b | 2008-04-16 19:43:16 +0200 | [diff] [blame] | 130 | def parse(self, source, name=None): |
Armin Ronacher | 8edbe49 | 2008-04-10 20:43:43 +0200 | [diff] [blame] | 131 | """Parse the sourcecode and return the abstract syntax tree. This tree |
| 132 | of nodes is used by the compiler to convert the template into |
Armin Ronacher | 07bc684 | 2008-03-31 14:18:49 +0200 | [diff] [blame] | 133 | executable source- or bytecode. |
Armin Ronacher | 07bc684 | 2008-03-31 14:18:49 +0200 | [diff] [blame] | 134 | """ |
Armin Ronacher | 9a82205 | 2008-04-17 18:44:07 +0200 | [diff] [blame] | 135 | return Parser(self, source, name).parse() |
Armin Ronacher | 07bc684 | 2008-03-31 14:18:49 +0200 | [diff] [blame] | 136 | |
Armin Ronacher | ba3757b | 2008-04-16 19:43:16 +0200 | [diff] [blame] | 137 | def lex(self, source, name=None): |
Armin Ronacher | 8edbe49 | 2008-04-10 20:43:43 +0200 | [diff] [blame] | 138 | """Lex the given sourcecode and return a generator that yields tokens. |
Armin Ronacher | 07bc684 | 2008-03-31 14:18:49 +0200 | [diff] [blame] | 139 | The stream returned is not usable for Jinja but can be used if |
| 140 | Jinja templates should be processed by other tools (for example |
| 141 | syntax highlighting etc) |
| 142 | |
| 143 | The tuples are returned in the form ``(lineno, token, value)``. |
| 144 | """ |
Armin Ronacher | ba3757b | 2008-04-16 19:43:16 +0200 | [diff] [blame] | 145 | return self.lexer.tokeniter(source, name) |
Armin Ronacher | bcb7c53 | 2008-04-11 16:30:34 +0200 | [diff] [blame] | 146 | |
Armin Ronacher | 814f6c2 | 2008-04-17 15:52:23 +0200 | [diff] [blame] | 147 | def compile(self, source, name=None, filename=None, globals=None, |
| 148 | raw=False): |
Armin Ronacher | 68f7767 | 2008-04-17 11:50:39 +0200 | [diff] [blame] | 149 | """Compile a node or source. The name is the load name of the |
| 150 | template after it was joined using `join_path` if necessary, |
| 151 | filename is the estimated filename of the template on the file |
| 152 | system. If the template came from a database or memory this |
| 153 | can be omitted. |
| 154 | """ |
Armin Ronacher | bcb7c53 | 2008-04-11 16:30:34 +0200 | [diff] [blame] | 155 | if isinstance(source, basestring): |
Armin Ronacher | 8e8d071 | 2008-04-16 23:10:49 +0200 | [diff] [blame] | 156 | source = self.parse(source, name) |
Armin Ronacher | fed44b5 | 2008-04-13 19:42:53 +0200 | [diff] [blame] | 157 | if self.optimized: |
| 158 | node = optimize(source, self, globals or {}) |
Armin Ronacher | 8e8d071 | 2008-04-16 23:10:49 +0200 | [diff] [blame] | 159 | source = generate(node, self, name, filename) |
Armin Ronacher | bcb7c53 | 2008-04-11 16:30:34 +0200 | [diff] [blame] | 160 | if raw: |
| 161 | return source |
Armin Ronacher | 2e9396b | 2008-04-16 14:21:57 +0200 | [diff] [blame] | 162 | if filename is None: |
Armin Ronacher | 68f7767 | 2008-04-17 11:50:39 +0200 | [diff] [blame] | 163 | filename = '<template>' |
Armin Ronacher | 2e9396b | 2008-04-16 14:21:57 +0200 | [diff] [blame] | 164 | elif isinstance(filename, unicode): |
Armin Ronacher | bcb7c53 | 2008-04-11 16:30:34 +0200 | [diff] [blame] | 165 | filename = filename.encode('utf-8') |
| 166 | return compile(source, filename, 'exec') |
| 167 | |
| 168 | def join_path(self, template, parent): |
| 169 | """Join a template with the parent. By default all the lookups are |
| 170 | relative to the loader root, but if the paths should be relative this |
| 171 | function can be used to calculate the real filename.""" |
| 172 | return template |
| 173 | |
Armin Ronacher | fed44b5 | 2008-04-13 19:42:53 +0200 | [diff] [blame] | 174 | def get_template(self, name, parent=None, globals=None): |
Armin Ronacher | bcb7c53 | 2008-04-11 16:30:34 +0200 | [diff] [blame] | 175 | """Load a template.""" |
| 176 | if self.loader is None: |
| 177 | raise TypeError('no loader for this environment specified') |
| 178 | if parent is not None: |
| 179 | name = self.join_path(name, parent) |
Armin Ronacher | fed44b5 | 2008-04-13 19:42:53 +0200 | [diff] [blame] | 180 | globals = self.make_globals(globals) |
| 181 | return self.loader.load(self, name, globals) |
Armin Ronacher | bcb7c53 | 2008-04-11 16:30:34 +0200 | [diff] [blame] | 182 | |
Armin Ronacher | 8e8d071 | 2008-04-16 23:10:49 +0200 | [diff] [blame] | 183 | def from_string(self, source, globals=None): |
Armin Ronacher | 46f5f98 | 2008-04-11 16:40:09 +0200 | [diff] [blame] | 184 | """Load a template from a string.""" |
Armin Ronacher | fed44b5 | 2008-04-13 19:42:53 +0200 | [diff] [blame] | 185 | globals = self.make_globals(globals) |
Armin Ronacher | 8e8d071 | 2008-04-16 23:10:49 +0200 | [diff] [blame] | 186 | return Template(self, self.compile(source, globals=globals), |
Armin Ronacher | 2e9396b | 2008-04-16 14:21:57 +0200 | [diff] [blame] | 187 | globals) |
Armin Ronacher | fed44b5 | 2008-04-13 19:42:53 +0200 | [diff] [blame] | 188 | |
| 189 | def make_globals(self, d): |
| 190 | """Return a dict for the globals.""" |
| 191 | if d is None: |
| 192 | return self.globals |
| 193 | return dict(self.globals, **d) |
Armin Ronacher | 46f5f98 | 2008-04-11 16:40:09 +0200 | [diff] [blame] | 194 | |
Armin Ronacher | bcb7c53 | 2008-04-11 16:30:34 +0200 | [diff] [blame] | 195 | |
| 196 | class Template(object): |
| 197 | """Represents a template.""" |
| 198 | |
Armin Ronacher | 814f6c2 | 2008-04-17 15:52:23 +0200 | [diff] [blame] | 199 | def __init__(self, environment, code, globals, uptodate=None): |
Armin Ronacher | f41d139 | 2008-04-18 16:41:52 +0200 | [diff] [blame] | 200 | namespace = { |
| 201 | 'environment': environment, |
| 202 | '__jinja_template__': self |
| 203 | } |
Armin Ronacher | bcb7c53 | 2008-04-11 16:30:34 +0200 | [diff] [blame] | 204 | exec code in namespace |
| 205 | self.environment = environment |
Armin Ronacher | ba3757b | 2008-04-16 19:43:16 +0200 | [diff] [blame] | 206 | self.name = namespace['name'] |
| 207 | self.filename = code.co_filename |
Armin Ronacher | bcb7c53 | 2008-04-11 16:30:34 +0200 | [diff] [blame] | 208 | self.root_render_func = namespace['root'] |
| 209 | self.blocks = namespace['blocks'] |
Armin Ronacher | fed44b5 | 2008-04-13 19:42:53 +0200 | [diff] [blame] | 210 | self.globals = globals |
Armin Ronacher | bcb7c53 | 2008-04-11 16:30:34 +0200 | [diff] [blame] | 211 | |
Armin Ronacher | f41d139 | 2008-04-18 16:41:52 +0200 | [diff] [blame] | 212 | # debug and loader helpers |
Armin Ronacher | ba3757b | 2008-04-16 19:43:16 +0200 | [diff] [blame] | 213 | self._get_debug_info = namespace['get_debug_info'] |
Armin Ronacher | 814f6c2 | 2008-04-17 15:52:23 +0200 | [diff] [blame] | 214 | self._uptodate = uptodate |
Armin Ronacher | ba3757b | 2008-04-16 19:43:16 +0200 | [diff] [blame] | 215 | |
Armin Ronacher | bcb7c53 | 2008-04-11 16:30:34 +0200 | [diff] [blame] | 216 | def render(self, *args, **kwargs): |
Armin Ronacher | 9a82205 | 2008-04-17 18:44:07 +0200 | [diff] [blame] | 217 | """Render the template into a string.""" |
Armin Ronacher | f41d139 | 2008-04-18 16:41:52 +0200 | [diff] [blame] | 218 | try: |
| 219 | return u''.join(self.generate(*args, **kwargs)) |
| 220 | except: |
| 221 | # hide the `generate` frame |
| 222 | exc_type, exc_value, tb = sys.exc_info() |
| 223 | raise exc_type, exc_value, tb.tb_next |
Armin Ronacher | bcb7c53 | 2008-04-11 16:30:34 +0200 | [diff] [blame] | 224 | |
| 225 | def stream(self, *args, **kwargs): |
Armin Ronacher | 9a82205 | 2008-04-17 18:44:07 +0200 | [diff] [blame] | 226 | """Return a `TemplateStream` that generates the template.""" |
Armin Ronacher | f41d139 | 2008-04-18 16:41:52 +0200 | [diff] [blame] | 227 | try: |
| 228 | return TemplateStream(self.generate(*args, **kwargs)) |
| 229 | except: |
| 230 | # hide the `generate` frame |
| 231 | exc_type, exc_value, tb = sys.exc_info() |
| 232 | raise exc_type, exc_value, tb.tb_next |
Armin Ronacher | fed44b5 | 2008-04-13 19:42:53 +0200 | [diff] [blame] | 233 | |
| 234 | def generate(self, *args, **kwargs): |
Armin Ronacher | 9a82205 | 2008-04-17 18:44:07 +0200 | [diff] [blame] | 235 | """Return a generator that generates the template.""" |
Armin Ronacher | fed44b5 | 2008-04-13 19:42:53 +0200 | [diff] [blame] | 236 | # assemble the context |
Armin Ronacher | 2e9396b | 2008-04-16 14:21:57 +0200 | [diff] [blame] | 237 | context = dict(*args, **kwargs) |
Armin Ronacher | fed44b5 | 2008-04-13 19:42:53 +0200 | [diff] [blame] | 238 | |
| 239 | # if the environment is using the optimizer locals may never |
| 240 | # override globals as optimizations might have happened |
| 241 | # depending on values of certain globals. This assertion goes |
| 242 | # away if the python interpreter is started with -O |
| 243 | if __debug__ and self.environment.optimized: |
Armin Ronacher | 2e9396b | 2008-04-16 14:21:57 +0200 | [diff] [blame] | 244 | overrides = set(context) & set(self.globals) |
Armin Ronacher | fed44b5 | 2008-04-13 19:42:53 +0200 | [diff] [blame] | 245 | if overrides: |
| 246 | plural = len(overrides) != 1 and 's' or '' |
| 247 | raise AssertionError('the per template variable%s %s ' |
| 248 | 'override%s global variable%s. ' |
| 249 | 'With an enabled optimizer this ' |
| 250 | 'will lead to unexpected results.' % |
| 251 | (plural, ', '.join(overrides), plural or ' a', plural)) |
Armin Ronacher | 2e9396b | 2008-04-16 14:21:57 +0200 | [diff] [blame] | 252 | gen = self.root_render_func(dict(self.globals, **context)) |
| 253 | # skip the first item which is a reference to the context |
Armin Ronacher | f059ec1 | 2008-04-11 22:21:00 +0200 | [diff] [blame] | 254 | gen.next() |
Armin Ronacher | ba3757b | 2008-04-16 19:43:16 +0200 | [diff] [blame] | 255 | |
| 256 | try: |
| 257 | for event in gen: |
| 258 | yield event |
| 259 | except: |
| 260 | exc_info = translate_exception(sys.exc_info()) |
| 261 | raise exc_info[0], exc_info[1], exc_info[2] |
| 262 | |
| 263 | def get_corresponding_lineno(self, lineno): |
| 264 | """Return the source line number of a line number in the |
| 265 | generated bytecode as they are not in sync. |
| 266 | """ |
| 267 | for template_line, code_line in reversed(self._get_debug_info()): |
| 268 | if code_line <= lineno: |
| 269 | return template_line |
| 270 | return 1 |
Armin Ronacher | c63243e | 2008-04-14 22:53:58 +0200 | [diff] [blame] | 271 | |
Armin Ronacher | 9a82205 | 2008-04-17 18:44:07 +0200 | [diff] [blame] | 272 | @property |
Armin Ronacher | 814f6c2 | 2008-04-17 15:52:23 +0200 | [diff] [blame] | 273 | def is_up_to_date(self): |
| 274 | """Check if the template is still up to date.""" |
| 275 | if self._uptodate is None: |
| 276 | return True |
| 277 | return self._uptodate() |
| 278 | |
Armin Ronacher | c63243e | 2008-04-14 22:53:58 +0200 | [diff] [blame] | 279 | def __repr__(self): |
| 280 | return '<%s %r>' % ( |
| 281 | self.__class__.__name__, |
| 282 | self.name |
| 283 | ) |
| 284 | |
| 285 | |
| 286 | class TemplateStream(object): |
| 287 | """Wraps a genererator for outputing template streams.""" |
| 288 | |
| 289 | def __init__(self, gen): |
| 290 | self._gen = gen |
| 291 | self._next = gen.next |
| 292 | self.buffered = False |
| 293 | |
| 294 | def disable_buffering(self): |
| 295 | """Disable the output buffering.""" |
| 296 | self._next = self._gen.next |
| 297 | self.buffered = False |
| 298 | |
| 299 | def enable_buffering(self, size=5): |
| 300 | """Enable buffering. Buffer `size` items before yielding them.""" |
| 301 | if size <= 1: |
| 302 | raise ValueError('buffer size too small') |
| 303 | self.buffered = True |
| 304 | |
| 305 | def buffering_next(): |
| 306 | buf = [] |
| 307 | c_size = 0 |
| 308 | push = buf.append |
| 309 | next = self._gen.next |
| 310 | |
| 311 | try: |
| 312 | while 1: |
| 313 | item = next() |
| 314 | if item: |
| 315 | push(item) |
| 316 | c_size += 1 |
| 317 | if c_size >= size: |
| 318 | raise StopIteration() |
| 319 | except StopIteration: |
| 320 | if not c_size: |
| 321 | raise |
| 322 | return u''.join(buf) |
| 323 | |
| 324 | self._next = buffering_next |
| 325 | |
| 326 | def __iter__(self): |
| 327 | return self |
| 328 | |
| 329 | def next(self): |
| 330 | return self._next() |