Beginning Python - From Novice to Professional

Beginning Python - From Novice to Professional Beginning Python - From Novice to Professional

16.01.2014 Views

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

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'\[(.+?)\]')

Hooray! Your file is uploaded and ready to be published.

Saved successfully!

Ooh no, something went wrong!