Beginning Python - From Novice to Professional
Beginning Python - From Novice to Professional Beginning Python - From Novice to Professional
CHAPTER 10 ■ BATTERIES INCLUDED 247 Making a template system. A template is a file you can put specific values into to get a finished text of some kind. For example, you may have a mail template requiring only the insertion of a recipient name. Python already has an advanced template mechanism: string formatting. However, with regular expressions you can make the system even more advanced. Let’s say you want to replace all occurrences of '[something]' (the “fields”) with the result of evaluating something as an expression in Python. Thus, the string 'The sum of 7 and 9 is [7 + 9].' should be translated to 'The sum of 7 and 9 is 16.' Also, you want to be able to perform assignments in these fields, so that the string '[name="Mr. Gumby"]Hello, [name]' should be translated to 'Hello, Mr. Gumby' This may sound like a complex task, but let’s review the available tools: • You can use a regular expression to match the fields and extract their contents. • You can evaluate the expression strings with eval, supplying the dictionary containing the scope. You do this in a try/except statement; if a SyntaxError is raised, you probably have a statement (such as an assignment) on your hands and should use exec instead. • You can execute the assignment strings (and other statements) with exec, storing the template’s scope in a dictionary. • You can use re.sub to substitute the result of the evaluation into the string being processed. Suddenly it doesn’t look so intimidating, does it? ■Tip If a task seems daunting, it almost always helps to break it down into smaller pieces. Also, take stock of the tools at your disposal for ideas on how to solve your problem. See Listing 10-11 for a sample implementation. Listing 10-11. A Template System # templates.py import fileinput, re # Matches fields enclosed in square brackets: field_pat = re.compile(r'\[(.+?)\]')
248 CHAPTER 10 ■ BATTERIES INCLUDED # We'll collect variables in this: scope = {} # This is used in re.sub: def replacement(match): code = match.group(1) try: # If the field can be evaluated, return it: return str(eval(code, scope)) except SyntaxError: # Otherwise, execute the assignment in the same scope... exec code in scope # ...and return an empty string: return '' # Get all the text as a single string: # (There are other ways of doing this; see Chapter 11) lines = [] for line in fileinput.input(): lines.append(line) text = ''.join(lines) # Substitute all the occurrences of the field pattern: print field_pat.sub(replacement, text) Simply put, this program does the following: 1. Defines a pattern for matching fields 2. Creates a dictionary to act as a scope for the template 3. Defines a replacement function that does the following: a. Grabs group 1 from the match and puts it in code. b. Tries to evaluate code with the scope dictionary as namespace, converts the result to a string, and returns it. If this succeeds, the field was an expression and everything is fine. Otherwise (i.e., a SyntaxError is raised), go to Step 3c. c. Executes the field in the same namespace (the scope dictionary) used for evaluating expressions, and then returns an empty string (because the assignment doesn’t evaluate to anything). 4. Uses fileinput to read in all available lines, puts them in a list, and joins them into one big string 5. Replaces all occurrences of field_pat using the replacement function in re.sub, and prints the result
- Page 228 and 229: CHAPTER 9 ■ MAGIC METHODS, PROPER
- Page 230 and 231: CHAPTER 9 ■ MAGIC METHODS, PROPER
- Page 232 and 233: CHAPTER 9 ■ MAGIC METHODS, PROPER
- Page 234 and 235: CHAPTER 10 ■ ■ ■ Batteries In
- Page 236 and 237: CHAPTER 10 ■ BATTERIES INCLUDED 2
- Page 238 and 239: CHAPTER 10 ■ BATTERIES INCLUDED 2
- Page 240 and 241: CHAPTER 10 ■ BATTERIES INCLUDED 2
- Page 242 and 243: CHAPTER 10 ■ BATTERIES INCLUDED 2
- Page 244 and 245: CHAPTER 10 ■ BATTERIES INCLUDED 2
- Page 246 and 247: CHAPTER 10 ■ BATTERIES INCLUDED 2
- Page 248 and 249: CHAPTER 10 ■ BATTERIES INCLUDED 2
- Page 250 and 251: CHAPTER 10 ■ BATTERIES INCLUDED 2
- Page 252 and 253: CHAPTER 10 ■ BATTERIES INCLUDED 2
- Page 254 and 255: CHAPTER 10 ■ BATTERIES INCLUDED 2
- Page 256 and 257: CHAPTER 10 ■ BATTERIES INCLUDED 2
- Page 258 and 259: CHAPTER 10 ■ BATTERIES INCLUDED 2
- Page 260 and 261: CHAPTER 10 ■ BATTERIES INCLUDED 2
- Page 262 and 263: CHAPTER 10 ■ BATTERIES INCLUDED 2
- Page 264 and 265: CHAPTER 10 ■ BATTERIES INCLUDED 2
- Page 266 and 267: CHAPTER 10 ■ BATTERIES INCLUDED 2
- Page 268 and 269: CHAPTER 10 ■ BATTERIES INCLUDED 2
- Page 270 and 271: CHAPTER 10 ■ BATTERIES INCLUDED 2
- Page 272 and 273: CHAPTER 10 ■ BATTERIES INCLUDED 2
- Page 274 and 275: CHAPTER 10 ■ BATTERIES INCLUDED 2
- Page 276 and 277: CHAPTER 10 ■ BATTERIES INCLUDED 2
- Page 280 and 281: CHAPTER 10 ■ BATTERIES INCLUDED 2
- Page 282 and 283: CHAPTER 10 ■ BATTERIES INCLUDED 2
- Page 284: CHAPTER 10 ■ BATTERIES INCLUDED 2
- Page 287 and 288: 256 CHAPTER 11 ■ FILES AND STUFF
- Page 289 and 290: 258 CHAPTER 11 ■ FILES AND STUFF
- Page 291 and 292: 260 CHAPTER 11 ■ FILES AND STUFF
- Page 293 and 294: 262 CHAPTER 11 ■ FILES AND STUFF
- Page 295 and 296: 264 CHAPTER 11 ■ FILES AND STUFF
- Page 297 and 298: 266 CHAPTER 11 ■ FILES AND STUFF
- Page 299 and 300: 268 CHAPTER 11 ■ FILES AND STUFF
- Page 301 and 302: 270 CHAPTER 12 ■ GRAPHICAL USER I
- Page 303 and 304: 272 CHAPTER 12 ■ GRAPHICAL USER I
- Page 305 and 306: 274 CHAPTER 12 ■ GRAPHICAL USER I
- Page 307 and 308: 276 CHAPTER 12 ■ GRAPHICAL USER I
- Page 309 and 310: 278 CHAPTER 12 ■ GRAPHICAL USER I
- Page 311 and 312: 280 CHAPTER 12 ■ GRAPHICAL USER I
- Page 313 and 314: 282 CHAPTER 12 ■ GRAPHICAL USER I
- Page 316 and 317: CHAPTER 13 ■ ■ ■ Database Sup
- Page 318 and 319: CHAPTER 13 ■ DATABASE SUPPORT 287
- Page 320 and 321: CHAPTER 13 ■ DATABASE SUPPORT 289
- Page 322 and 323: CHAPTER 13 ■ DATABASE SUPPORT 291
- Page 324 and 325: CHAPTER 13 ■ DATABASE SUPPORT 293
- Page 326: CHAPTER 13 ■ DATABASE SUPPORT 295
CHAPTER 10 ■ BATTERIES INCLUDED 247<br />
Making a template system. A template is a file you can put specific values in<strong>to</strong> <strong>to</strong> get a finished text of some kind.<br />
For example, you may have a mail template requiring only the insertion of a recipient name. <strong>Python</strong> already has an<br />
advanced template mechanism: string formatting. However, with regular expressions you can make the system<br />
even more advanced. Let’s say you want <strong>to</strong> replace all occurrences of '[something]' (the “fields”) with the result<br />
of evaluating something as an expression in <strong>Python</strong>. Thus, the string<br />
'The sum of 7 and 9 is [7 + 9].'<br />
should be translated <strong>to</strong><br />
'The sum of 7 and 9 is 16.'<br />
Also, you want <strong>to</strong> be able <strong>to</strong> perform assignments in these fields, so that the string<br />
'[name="Mr. Gumby"]Hello, [name]'<br />
should be translated <strong>to</strong><br />
'Hello, Mr. Gumby'<br />
This may sound like a complex task, but let’s review the available <strong>to</strong>ols:<br />
• You can use a regular expression <strong>to</strong> match the fields and extract their contents.<br />
• You can evaluate the expression strings with eval, supplying the dictionary containing the scope. You<br />
do this in a try/except statement; if a SyntaxError is raised, you probably have a statement (such<br />
as an assignment) on your hands and should use exec instead.<br />
• You can execute the assignment strings (and other statements) with exec, s<strong>to</strong>ring the template’s scope<br />
in a dictionary.<br />
• You can use re.sub <strong>to</strong> substitute the result of the evaluation in<strong>to</strong> the string being processed.<br />
Suddenly it doesn’t look so intimidating, does it?<br />
■Tip If a task seems daunting, it almost always helps <strong>to</strong> break it down in<strong>to</strong> smaller pieces. Also, take s<strong>to</strong>ck<br />
of the <strong>to</strong>ols at your disposal for ideas on how <strong>to</strong> solve your problem.<br />
See Listing 10-11 for a sample implementation.<br />
Listing 10-11. A Template System<br />
# templates.py<br />
import fileinput, re<br />
# Matches fields enclosed in square brackets:<br />
field_pat = re.compile(r'\[(.+?)\]')