42
42
except NameError :
43
43
# Python 3 support
44
44
str_class = str
45
+ basestring = str
45
46
46
47
47
48
# Flag for testing
@@ -163,31 +164,43 @@ class PybarsError(Exception):
163
164
pass
164
165
165
166
166
- class strlist (list ):
167
+ class strlist (object ):
168
+ __slots__ = ['value' ]
169
+ def __init__ (self , default = None ):
170
+ self .value = u''
171
+ if default :
172
+ self .grow (default )
167
173
168
- """A quasi-list to let the template code avoid special casing."""
174
+ def __str__ (self ):
175
+ return self .value
176
+
177
+ def __unicode__ (self ):
178
+ return self .value
169
179
170
- def __str__ (self ): # Python 3
171
- return '' . join ( self )
180
+ def append (self , other ):
181
+ self . value += other
172
182
173
- def __unicode__ (self ): # Python 2
174
- return u'' .join (self )
183
+ def extend (self , other ):
184
+ if type (other ) is strlist :
185
+ self .value += other .value
186
+ else :
187
+ self .value += '' .join (other )
175
188
176
- def grow (self , thing ):
177
- """Make the list longer, appending for unicode, extending otherwise."""
178
- if type (thing ) == str_class :
179
- self .append (thing )
189
+ def __iter__ (self ):
190
+ return iter ([self ])
180
191
181
- # This will only ever match in Python 2 since str_class is str in
182
- # Python 3.
183
- elif type (thing ) == str :
184
- self .append (unicode (thing ))
192
+ def __add__ (self , other ):
193
+ self .value += other .value
194
+ return self
185
195
196
+ def grow (self , other ):
197
+ if isinstance (other , basestring ):
198
+ self .value += other
199
+ elif type (other ) is strlist :
200
+ self .value += other .value
186
201
else :
187
- # Recursively expand to a flat list; may deserve a C accelerator at
188
- # some point.
189
- for element in thing :
190
- self .grow (element )
202
+ for item in other :
203
+ self .grow (item )
191
204
192
205
193
206
_map = {
@@ -212,14 +225,15 @@ def escape(something, _escape_re=_escape_re, substitute=substitute):
212
225
213
226
214
227
def pick (context , name , default = None ):
215
- if isinstance (name , str ) and hasattr (context , name ):
228
+ if type (name ) is str and hasattr (context , name ):
216
229
return getattr (context , name )
217
230
if hasattr (context , 'get' ):
218
231
return context .get (name )
219
232
try :
220
233
return context [name ]
221
234
except (KeyError , TypeError ):
222
- return default
235
+ pass
236
+ return default
223
237
224
238
225
239
sentinel = object ()
@@ -270,34 +284,40 @@ def __unicode__(self):
270
284
return unicode (self .context )
271
285
272
286
287
+ ITERABLE_TYPES = (list , tuple )
288
+
273
289
def resolve (context , * segments ):
274
- carryover_data = False
275
290
291
+ context_type = type (context )
276
292
# This makes sure that bare "this" paths don't return a Scope object
277
- if segments == ('' ,) and isinstance ( context , Scope ) :
293
+ if segments == ('' ,) and context_type is Scope :
278
294
return context .get ('this' )
279
295
296
+ carryover_data = False
280
297
for segment in segments :
281
298
299
+ if context is None :
300
+ return None
301
+
282
302
# Handle @../index syntax by popping the extra @ along the segment path
283
303
if carryover_data :
304
+ segment = u'@' + segment
284
305
carryover_data = False
285
- segment = u'@%s' % segment
286
- if len ( segment ) > 1 and segment [ 0 :2 ] == '@@' :
306
+
307
+ if segment [ :2 ] == '@@' :
287
308
segment = segment [1 :]
288
309
carryover_data = True
289
310
290
- if context is None :
291
- return None
292
- if segment in (None , "" ):
311
+ if not segment :
293
312
continue
294
- if type (context ) in (list , tuple ):
295
- offset = int (segment )
296
- context = context [offset ]
297
- elif isinstance (context , Scope ):
313
+
314
+ if context_type is Scope :
298
315
context = context .get (segment )
316
+ elif context_type in ITERABLE_TYPES :
317
+ context = context [int (segment )]
299
318
else :
300
319
context = pick (context , segment )
320
+ context_type = type (context )
301
321
return context
302
322
303
323
@@ -336,7 +356,7 @@ def prepare(value, should_escape):
336
356
337
357
338
358
def ensure_scope (context , root ):
339
- return context if isinstance (context , Scope ) else Scope (context , context , root )
359
+ return context if type (context ) is Scope else Scope (context , context , root )
340
360
341
361
342
362
def _each (this , options , context ):
@@ -509,8 +529,8 @@ def start(self):
509
529
])
510
530
else :
511
531
self ._result .grow (u"def %s(context, helpers, partials, root):\n " % function_name )
512
- self ._result .grow (u" result = strlist()\n " )
513
532
self ._result .grow (u" context = ensure_scope(context, root)\n " )
533
+ self ._result .grow (u" result = strlist()\n " )
514
534
515
535
def finish (self ):
516
536
lines , ns , function_name = self .stack .pop (- 1 )
@@ -521,7 +541,7 @@ def finish(self):
521
541
self ._result .grow (u" result = %s(result)\n " % str_class .__name__ )
522
542
self ._result .grow (u" return result\n " )
523
543
524
- source = str_class (u"" . join ( lines ) )
544
+ source = str_class (lines )
525
545
526
546
self ._result = self .stack and self .stack [- 1 ][0 ]
527
547
self ._locals = self .stack and self .stack [- 1 ][1 ]
@@ -578,11 +598,12 @@ def add_block(self, symbol, arguments, nested, alt_nested):
578
598
u" value = helper(context, options%s\n " % call ,
579
599
u" else:\n "
580
600
u" value = helpers['blockHelperMissing'](context, options, value)\n "
581
- u" result.grow(value or '')\n "
601
+ u" if value:\n "
602
+ u" result.grow(value)\n "
582
603
])
583
604
584
605
def add_literal (self , value ):
585
- self ._result .grow (u" result.append(%s) \n " % repr (value ))
606
+ self ._result .grow (u" result.value += %s \n " % ( repr (value ), ))
586
607
587
608
def _lookup_arg (self , arg ):
588
609
if not arg :
0 commit comments