Mercurial > genshi > genshi-test
annotate genshi/template/interpolation.py @ 902:09cc3627654c experimental-inline
Sync `experimental/inline` branch with [source:trunk@1126].
author | cmlenz |
---|---|
date | Fri, 23 Apr 2010 21:08:26 +0000 |
parents | 1837f39efd6f |
children |
rev | line source |
---|---|
500 | 1 # -*- coding: utf-8 -*- |
2 # | |
902
09cc3627654c
Sync `experimental/inline` branch with [source:trunk@1126].
cmlenz
parents:
820
diff
changeset
|
3 # Copyright (C) 2007-2009 Edgewall Software |
500 | 4 # All rights reserved. |
5 # | |
6 # This software is licensed as described in the file COPYING, which | |
7 # you should have received as part of this distribution. The terms | |
8 # are also available at http://genshi.edgewall.org/wiki/License. | |
9 # | |
10 # This software consists of voluntary contributions made by many | |
11 # individuals. For the exact contribution history, see the revision | |
12 # history and logs, available at http://genshi.edgewall.org/log/. | |
13 | |
14 """String interpolation routines, i.e. the splitting up a given text into some | |
15 parts that are literal strings, and others that are Python expressions. | |
16 """ | |
17 | |
18 from itertools import chain | |
19 import os | |
820
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
20 import re |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
21 from tokenize import PseudoToken |
500 | 22 |
23 from genshi.core import TEXT | |
24 from genshi.template.base import TemplateSyntaxError, EXPR | |
25 from genshi.template.eval import Expression | |
26 | |
27 __all__ = ['interpolate'] | |
28 __docformat__ = 'restructuredtext en' | |
29 | |
30 NAMESTART = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_' | |
31 NAMECHARS = NAMESTART + '.0123456789' | |
32 PREFIX = '$' | |
33 | |
820
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
34 token_re = re.compile('%s|%s(?s)' % ( |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
35 r'[uU]?[rR]?("""|\'\'\')((?<!\\)\\\1|.)*?\1', |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
36 PseudoToken |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
37 )) |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
38 |
902
09cc3627654c
Sync `experimental/inline` branch with [source:trunk@1126].
cmlenz
parents:
820
diff
changeset
|
39 |
820
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
40 def interpolate(text, filepath=None, lineno=-1, offset=0, lookup='strict'): |
500 | 41 """Parse the given string and extract expressions. |
42 | |
43 This function is a generator that yields `TEXT` events for literal strings, | |
44 and `EXPR` events for expressions, depending on the results of parsing the | |
45 string. | |
46 | |
47 >>> for kind, data, pos in interpolate("hey ${foo}bar"): | |
902
09cc3627654c
Sync `experimental/inline` branch with [source:trunk@1126].
cmlenz
parents:
820
diff
changeset
|
48 ... print('%s %r' % (kind, data)) |
09cc3627654c
Sync `experimental/inline` branch with [source:trunk@1126].
cmlenz
parents:
820
diff
changeset
|
49 TEXT 'hey ' |
500 | 50 EXPR Expression('foo') |
902
09cc3627654c
Sync `experimental/inline` branch with [source:trunk@1126].
cmlenz
parents:
820
diff
changeset
|
51 TEXT 'bar' |
500 | 52 |
53 :param text: the text to parse | |
820
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
54 :param filepath: absolute path to the file in which the text was found |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
55 (optional) |
500 | 56 :param lineno: the line number at which the text was found (optional) |
57 :param offset: the column number at which the text starts in the source | |
58 (optional) | |
59 :param lookup: the variable lookup mechanism; either "lenient" (the | |
60 default), "strict", or a custom lookup class | |
61 :return: a list of `TEXT` and `EXPR` events | |
62 :raise TemplateSyntaxError: when a syntax error in an expression is | |
63 encountered | |
64 """ | |
65 pos = [filepath, lineno, offset] | |
66 | |
67 textbuf = [] | |
68 textpos = None | |
69 for is_expr, chunk in chain(lex(text, pos, filepath), [(True, '')]): | |
70 if is_expr: | |
71 if textbuf: | |
902
09cc3627654c
Sync `experimental/inline` branch with [source:trunk@1126].
cmlenz
parents:
820
diff
changeset
|
72 yield TEXT, ''.join(textbuf), textpos |
500 | 73 del textbuf[:] |
74 textpos = None | |
75 if chunk: | |
76 try: | |
77 expr = Expression(chunk.strip(), pos[0], pos[1], | |
820
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
78 lookup=lookup) |
500 | 79 yield EXPR, expr, tuple(pos) |
80 except SyntaxError, err: | |
81 raise TemplateSyntaxError(err, filepath, pos[1], | |
82 pos[2] + (err.offset or 0)) | |
83 else: | |
84 textbuf.append(chunk) | |
85 if textpos is None: | |
86 textpos = tuple(pos) | |
87 | |
88 if '\n' in chunk: | |
89 lines = chunk.splitlines() | |
90 pos[1] += len(lines) - 1 | |
91 pos[2] += len(lines[-1]) | |
92 else: | |
93 pos[2] += len(chunk) | |
94 | |
902
09cc3627654c
Sync `experimental/inline` branch with [source:trunk@1126].
cmlenz
parents:
820
diff
changeset
|
95 |
500 | 96 def lex(text, textpos, filepath): |
97 offset = pos = 0 | |
98 end = len(text) | |
99 escaped = False | |
100 | |
101 while 1: | |
102 if escaped: | |
103 offset = text.find(PREFIX, offset + 2) | |
104 escaped = False | |
105 else: | |
106 offset = text.find(PREFIX, pos) | |
107 if offset < 0 or offset == end - 1: | |
108 break | |
109 next = text[offset + 1] | |
110 | |
111 if next == '{': | |
112 if offset > pos: | |
113 yield False, text[pos:offset] | |
114 pos = offset + 2 | |
115 level = 1 | |
116 while level: | |
820
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
117 match = token_re.match(text, pos) |
500 | 118 if match is None: |
119 raise TemplateSyntaxError('invalid syntax', filepath, | |
120 *textpos[1:]) | |
121 pos = match.end() | |
122 tstart, tend = match.regs[3] | |
123 token = text[tstart:tend] | |
124 if token == '{': | |
125 level += 1 | |
126 elif token == '}': | |
127 level -= 1 | |
128 yield True, text[offset + 2:pos - 1] | |
129 | |
130 elif next in NAMESTART: | |
131 if offset > pos: | |
132 yield False, text[pos:offset] | |
133 pos = offset | |
134 pos += 1 | |
135 while pos < end: | |
136 char = text[pos] | |
137 if char not in NAMECHARS: | |
138 break | |
139 pos += 1 | |
140 yield True, text[offset + 1:pos].strip() | |
141 | |
142 elif not escaped and next == PREFIX: | |
820
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
143 if offset > pos: |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
144 yield False, text[pos:offset] |
500 | 145 escaped = True |
146 pos = offset + 1 | |
147 | |
148 else: | |
149 yield False, text[pos:offset + 1] | |
150 pos = offset + 1 | |
151 | |
152 if pos < end: | |
153 yield False, text[pos:] |