@@ -163,31 +163,43 @@ class PybarsError(Exception):
163
163
pass
164
164
165
165
166
- class strlist (list ):
166
+ class strlist (object ):
167
+ __slots__ = ['value' ]
168
+ def __init__ (self , default = None ):
169
+ self .value = u''
170
+ if default :
171
+ self .grow (default )
167
172
168
- """A quasi-list to let the template code avoid special casing."""
173
+ def __str__ (self ):
174
+ return self .value
169
175
170
- def __str__ (self ): # Python 3
171
- return '' . join ( self )
176
+ def __unicode__ (self ):
177
+ return self . value
172
178
173
- def __unicode__ (self ): # Python 2
174
- return u'' . join ( self )
179
+ def append (self , other ):
180
+ self . value += other
175
181
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 )
182
+ def extend (self , other ):
183
+ if type (other ) is strlist :
184
+ self .value += other .value
185
+ else :
186
+ self .value += '' .join (other )
180
187
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 ))
188
+ def __iter__ (self ):
189
+ return iter ([self ])
185
190
191
+ def __add__ (self , other ):
192
+ self .value += other .value
193
+ return self
194
+
195
+ def grow (self , other ):
196
+ if isinstance (other , basestring ):
197
+ self .value += other
198
+ elif type (other ) is strlist :
199
+ self .value += other .value
186
200
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 )
201
+ for item in other :
202
+ self .grow (item )
191
203
192
204
193
205
_map = {
@@ -212,14 +224,15 @@ def escape(something, _escape_re=_escape_re, substitute=substitute):
212
224
213
225
214
226
def pick (context , name , default = None ):
215
- if isinstance (name , str ) and hasattr (context , name ):
227
+ try :
228
+ return context [name ]
229
+ except (AttributeError , KeyError , TypeError ):
230
+ pass
231
+ if type (name ) is str and hasattr (context , name ):
216
232
return getattr (context , name )
217
233
if hasattr (context , 'get' ):
218
234
return context .get (name )
219
- try :
220
- return context [name ]
221
- except (KeyError , TypeError ):
222
- return default
235
+ return default
223
236
224
237
225
238
sentinel = object ()
@@ -270,34 +283,40 @@ def __unicode__(self):
270
283
return unicode (self .context )
271
284
272
285
286
+ ITERABLE_TYPES = (list , tuple )
287
+
273
288
def resolve (context , * segments ):
274
- carryover_data = False
275
289
290
+ context_type = type (context )
276
291
# This makes sure that bare "this" paths don't return a Scope object
277
- if segments == ('' ,) and isinstance ( context , Scope ) :
292
+ if segments == ('' ,) and context_type is Scope :
278
293
return context .get ('this' )
279
294
295
+ carryover_data = False
280
296
for segment in segments :
281
297
298
+ if context is None :
299
+ return None
300
+
282
301
# Handle @../index syntax by popping the extra @ along the segment path
283
302
if carryover_data :
303
+ segment = u'@' + segment
284
304
carryover_data = False
285
- segment = u'@%s' % segment
286
- if len ( segment ) > 1 and segment [ 0 :2 ] == '@@' :
305
+
306
+ if segment [ :2 ] == '@@' :
287
307
segment = segment [1 :]
288
308
carryover_data = True
289
309
290
- if context is None :
291
- return None
292
- if segment in (None , "" ):
310
+ if not segment :
293
311
continue
294
- if type (context ) in (list , tuple ):
295
- offset = int (segment )
296
- context = context [offset ]
297
- elif isinstance (context , Scope ):
312
+
313
+ if context_type is Scope :
298
314
context = context .get (segment )
315
+ elif context_type in ITERABLE_TYPES :
316
+ context = context [int (segment )]
299
317
else :
300
318
context = pick (context , segment )
319
+ context_type = type (context )
301
320
return context
302
321
303
322
@@ -336,7 +355,7 @@ def prepare(value, should_escape):
336
355
337
356
338
357
def ensure_scope (context , root ):
339
- return context if isinstance (context , Scope ) else Scope (context , context , root )
358
+ return context if type (context ) is Scope else Scope (context , context , root )
340
359
341
360
342
361
def _each (this , options , context ):
@@ -378,7 +397,7 @@ def _each(this, options, context):
378
397
379
398
380
399
def _if (this , options , context ):
381
- if hasattr (context , '__call__' ):
400
+ if callable (context ):
382
401
context = context (this )
383
402
if context :
384
403
return options ['fn' ](this )
@@ -403,7 +422,7 @@ def _lookup(this, context, key):
403
422
404
423
405
424
def _blockHelperMissing (this , options , context ):
406
- if hasattr (context , '__call__' ):
425
+ if callable (context ):
407
426
context = context (this )
408
427
if context != u"" and not context :
409
428
return options ['inverse' ](this )
@@ -509,8 +528,8 @@ def start(self):
509
528
])
510
529
else :
511
530
self ._result .grow (u"def %s(context, helpers, partials, root):\n " % function_name )
512
- self ._result .grow (u" result = strlist()\n " )
513
531
self ._result .grow (u" context = ensure_scope(context, root)\n " )
532
+ self ._result .grow (u" result = strlist()\n " )
514
533
515
534
def finish (self ):
516
535
lines , ns , function_name = self .stack .pop (- 1 )
@@ -521,7 +540,7 @@ def finish(self):
521
540
self ._result .grow (u" result = %s(result)\n " % str_class .__name__ )
522
541
self ._result .grow (u" return result\n " )
523
542
524
- source = str_class (u"" . join ( lines ) )
543
+ source = str_class (lines )
525
544
526
545
self ._result = self .stack and self .stack [- 1 ][0 ]
527
546
self ._locals = self .stack and self .stack [- 1 ][1 ]
@@ -574,15 +593,16 @@ def add_block(self, symbol, arguments, nested, alt_nested):
574
593
u" value = helper = helpers.get('%s')\n " % symbol ,
575
594
u" if value is None:\n "
576
595
u" value = resolve(context, '%s')\n " % symbol ,
577
- u" if helper and hasattr (helper, '__call__' ):\n "
596
+ u" if helper and callable (helper):\n "
578
597
u" value = helper(context, options%s\n " % call ,
579
598
u" else:\n "
580
599
u" value = helpers['blockHelperMissing'](context, options, value)\n "
581
- u" result.grow(value or '')\n "
600
+ u" if value:\n "
601
+ u" result.grow(value)\n "
582
602
])
583
603
584
604
def add_literal (self , value ):
585
- self ._result .grow (u" result.append(%s) \n " % repr (value ))
605
+ self ._result .grow (u" result.value += %s \n " % ( repr (value ), ))
586
606
587
607
def _lookup_arg (self , arg ):
588
608
if not arg :
@@ -611,7 +631,7 @@ def find_lookup(self, path, path_type, call):
611
631
realname = None
612
632
self ._result .grow (u" value = %s\n " % path )
613
633
self ._result .grow ([
614
- u" if hasattr (value, '__call__' ):\n "
634
+ u" if callable (value):\n "
615
635
u" value = value(context%s\n " % call ,
616
636
])
617
637
if realname :
0 commit comments