views:

84

answers:

3

I can't quite figure out what's going on with string templates:

t = Template('cannot teach an ${dog.old} ${tricks.new}. ${why} is this ${not} working')
print t.safe_substitute({'dog.old': 'old dog', 'tricks.new': 'new tricks', 'why': 'OH WHY', 'not': '@#%@#% NOT'})

This prints:

cannot teach an ${dog.old} ${tricks.new}. OH WHY is this @#%@#% NOT working

I thought that the braces handled arbitrary strings... what characters are allowed in braces and is there any way I can subclass Template to do what I want?

+1  A: 
Aaron Digulla
ok, thanks! Where is this documented, and is there any way to override it?
Jason S
To override, you can derive subclasses of Template to customize the placeholder syntax.
Aaron Digulla
This is not correct, and is not mentioned in the documentation. Otherwise, `print t.safe_substitute({'dog': {'old':'old dog'}, 'tricks': {'new':'new tricks'}, 'why': 'OH WHY', 'not': '@#%@#% NOT'})` would work, and it does not.
MikeWyatt
To paraphrase Aaron: "It's on the page you linked to". That is, *you* linked to http://docs.python.org/library/string.html#string.Template . Just scroll up a couple lines.
jae
@MikeWyatt: You may be right but not for the reason you mention. The syntax `dog.old` doesn't work on a dictionary. It must be `dog['old']`. I'm talking about *instances* but I agree that the documentation doesn't say it works.
Aaron Digulla
To whoever downvoted Aaron: he fixed his mistake, please consider rescinding downvote; this was a helpful answer.
Jason S
@Aaron: Maybe I'm missing something, but dog['old'] also doesn't work. Have you tried that syntax in IDLE? Without looking at the Template source code, it seems like the default implementation only supports basic identifiers.
MikeWyatt
@MikeWyatt: I've edited my answer. Apparently, only basic identifiers work.
Aaron Digulla
@Aaron: Thanks. I removed the down vote. If you don't mind me being a little pedantic, you might want to strike out the incorrect content at the top of your answer, or reword it to mention subclassing Template.
MikeWyatt
+4  A: 

From the documentation...

$identifier names a substitution placeholder matching a mapping key of "identifier". By default, "identifier" must spell a Python identifier. The first non-identifier character after the $ character terminates this placeholder specification.

The period is a non-identifier character, and braces are simply used to separate the identifier from adjacent non-identifier text.

MikeWyatt
+1  A: 

Aha, I tried this experiment:

from string import Template
import uuid

class MyTemplate(Template):
    idpattern = r'[a-z][_a-z0-9]*(\.[a-z][_a-z0-9]*)*'

t1 = Template('cannot teach an ${dog.old} ${tricks.new}. ${why} is this ${not} working')
t2 = MyTemplate('cannot teach an ${dog.old} ${tricks.new}. ${why} is this ${not} working')
map1 = {'dog.old': 'old dog', 
    'tricks.new': 'new tricks', 'why': 'OH WHY', 'not': '@#%@#% NOT'}
map2 = {'dog': {'old': 'old dog'}, 
        'tricks': {'new': 'new tricks'}, 'why': 'OH WHY', 'not': '@#%@#% NOT'}  
print t1.safe_substitute(map1)
print t1.safe_substitute(map2)
print t2.safe_substitute(map1)
print t2.safe_substitute(map2)

which prints

cannot teach an ${dog.old} ${tricks.new}. OH WHY is this @#%@#% NOT working
cannot teach an ${dog.old} ${tricks.new}. OH WHY is this @#%@#% NOT working
cannot teach an old dog new tricks. OH WHY is this @#%@#% NOT working
cannot teach an ${dog.old} ${tricks.new}. OH WHY is this @#%@#% NOT working

so the third one (print t2.safe_substitute(map1)) works.

Jason S