In this section, we will look at two examples of Python scripts that take Python code as strings and execute them at runtime. The first example is more dynamic, but the second shows off function attributes at the same time.
Creating Code at Runtime and Executing It
The first example is loopmake.py script, which is a simple computer-aided software engineering (CASE) that generates and executes loops on-the-fly. It prompts the user for the various parameters (i.e., loop type (while or for), type of data to iterate over [numbers or sequences]), generates the code string, and executes it.
ptg 14.3 Executable Object Statements and Built-in Functions 643
Example 14.1 Dynamically Generating and Executing Python Code (loopmake.py)
1 #!/usr/bin/env python
23 dashes = '\n' + '-' * 50 # dashed line 4 exec_dict = {
56 'f': """ # for loop 7 for %s in %s:
8 print %s 9 """,
1011 's': """ # sequence while loop 12 %s = 0
13 %s = %s
14 while %s < len(%s):
15 print %s[%s]
16 %s = %s + 1 17 """,
1819 'n': """ # counting while loop 20 %s = %d
21 while %s < %d:
22 print %s 23 %s = %s + %d 24 """
25 }
2627 def main():
2829 ltype = raw_input('Loop type? (For/While) ') 30 dtype = raw_input('Data type? (Number/Seq) ') 3132 if dtype == 'n':
33 start = input('Starting value? ')
34 stop = input('Ending value (non-inclusive)? ') 35 step = input('Stepping value? ')
36 seq = str(range(start, stop, step)) 37
38 else:
39 seq = raw_input('Enter sequence: ') 4041 var = raw_input('Iterative variable name? ') 4243 if ltype == 'f':
44 exec_str = exec_dict['f'] % (var, seq, var) 45
46 elif ltype == 'w':
47 if dtype == 's':
48 svar = raw_input('Enter sequence name? ') 49 exec_str = exec_dict['s'] % \
50 (var, svar, seq, var, svar, svar, var, var, var) 51
(continued)
ptg 644 Chapter 14 Execution Environment
Here are a few example executions of this script:
% loopmake.py
Loop type? (For/While) f Data type? (Number/Sequence) n Starting value? 0
Ending value (non-inclusive)? 4 Stepping value? 1
Iterative variable name? counter
--- The custom-generated code for you is:
--- for counter in [0, 1, 2, 3]:
print counter
--- Test execution of the code:
--- 0
1 2 3
---
% loopmake.py
Loop type? (For/While) w Data type? (Number/Sequence) n Starting value? 0
Example 14.1 Dynamically Generating and Executing Python Code (loopmake.py) (continued)
52 elif dtype == 'n':
53 exec_str = exec_dict['n'] % \
54 (var, start, var, stop, var, var, var, step) 55
56 print dashes
57 print 'Your custom-generated code:' + dashes 58 print exec_str + dashes
59 print 'Test execution of the code:' + dashes 60 exec exec_str
61 print dashes
6263 if __name__ == '__main__':
64 main()
ptg 14.3 Executable Object Statements and Built-in Functions 645
Ending value (non-inclusive)? 4 Stepping value? 1
Iterative variable name? counter
--- Your custom-generated code:
--- counter = 0
while counter < 4:
print counter
counter = counter + 1
--- Test execution of the code:
--- 0
1 2 3
---
% loopmake.py
Loop type? (For/While) f Data type? (Number/Sequence) s
Enter sequence: [932, 'grail', 3.0, 'arrrghhh']
Iterative variable name? eachItem
--- Your custom-generated code:
--- for eachItem in [932, 'grail', 3.0, 'arrrghhh']:
print eachItem
--- Test execution of the code:
--- 932
grail 3.0 arrrghhh
---
% loopmake.py
Loop type? (For/While) w Data type? (Number/Sequence) s
ptg 646 Chapter 14 Execution Environment
Enter sequence: [932, 'grail', 3.0, 'arrrghhh']
Iterative variable name? eachIndex Enter sequence name? myList
--- Your custom-generated code:
--- eachIndex = 0
myList = [932, 'grail', 3.0, 'arrrghhh']
while eachIndex < len(myList):
print myList[eachIndex]
eachIndex = eachIndex + 1
--- Test execution of the code:
--- 932
grail 3.0 arrrghhh
---
Line-by-Line Explanation
Lines 1–25
In this first part of the script, we are setting up two global variables. The first is a static string consisting of a line of dashes (hence the name) and the sec- ond is a dictionary of the skeleton code we will need to use for the loops we are going to generate. The keys are “f” for a for loop, “s” for a while loop iterating through a sequence, and “n” for a counting while loop.
Lines 27–30
Here we prompt the user for the type of loop he or she wants and what data types to use.
Lines 32–36
Numbers have been chosen; they provide the starting, stopping, and incre- mental values. In this section of code, we are introduced to the input() BIF for the first time. As we shall see in Section 14.3.5, input() is similar to raw_input() in that it prompts the user for string input, but unlike raw_input(), input() also evaluates the input as a Python expression, rendering a Python object even if the user typed it in as a string.
ptg 14.3 Executable Object Statements and Built-in Functions 647
Lines 38–39
A sequence was chosen; enter the sequence here as a string.
Line 41
Get the name of the iterative loop variable that the user wants to use.
Lines 43–44
Generate the for loop, filling in all the customized details.
Lines 46–50
Generate a while loop which iterates through a sequence.
Lines 52–54
Generate a counting while loop.
Lines 56–61
Output the generated source code as well as the resulting output from execu- tion of the aforementioned generated code.
Lines 63–64
Executemain() only if this module was invoked directly.
To keep the size of this script to a manageable size, we had to trim all the comments and error checking from the original script. You can find both the original as well as an alternate version of this script on the book’s Web site.
The extended version includes extra features such as not requiring enclos- ing quotation marks for string input, default values for input data, and detec- tion of invalid ranges and identifiers; it also does not permit built-in names or keywords as variable names.
Conditionally Executing Code
Our second example highlights the usefulness of function attributes intro- duced back in Chapter 11, “Functions”, inspired by the example in PEP 232.
Let us assume that you are a software QA developer encouraging your engi- neers to install either regression testers or regression instruction code into the main source but do not want the testing code mixed with the production code. You can tell your engineers to create a string representing the testing code. When your test framework executes, it checks to see if that function has defined a test body, and if so, (evaluates and) executes it. If not, it will skip and continue as normal.
ptg 648 Chapter 14 Execution Environment
Lines 1–8
We define foo() and bar() in the first part of this script. Neither function does anything other than return True. The one difference between the two is thatfoo() has no attributes while bar() gets a documentation string.
Lines 10–16
Using function attributes, we add a doc string and a regression or unit tester string to foo(). Note that the tester string is actually comprised of real lines of Python code.
Example 14.2 Function Attributes (funcAttrs.py)
Callingsys.exit() causes the Python interpreter to quit. Any integer argument toexit() will be returned to the caller as the exit status,
which has a default value of 0.
1 #!/usr/bin/env python 23 def foo():
4 return True 56 def bar():
7 'bar() does not do much' 8 return True
910 foo.__doc__ = 'foo() does not do much' 11 foo.tester = '''
12 if foo():
13 print 'PASSED' 14 else:
15 print 'FAILED' 16 '''
1718 for eachAttr in dir():
19 obj = eval(eachAttr)
20 if isinstance(obj, type(foo)):
21 if hasattr(obj, '__doc__'):
22 print '\nFunction "%s" has a doc
string:\n\t%s' % (eachAttr, obj.__doc__) 23 if hasattr(obj, 'tester'):
24 print 'Function "%s" has a tester...
executing' % eachAttr
25 exec obj.tester
26 else:
27 print 'Function "%s" has no tester...
skipping' % eachAttr 28 else:
29 print '"%s" is not a function' % eachAttr