# JythonBook / chapter8.rst

 Josh Juneau d9b2bb8 2010-02-01   1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 Chapter 8: Scripting With Jython +++++++++++++++++++++++++++++++++ In this chapter we will look at scripting with jython. For our purposes, I will define "scripting" as the writing of small programs to help out with daily tasks. These tasks are things like deleting and creating directories, mananging files and programs, or anything else that feels repetitive that you might be able to express as a small program. In practice however, scripts can become so large that the line between a script and a full sized program can blur. We'll start with an overview of some of the most helpful modules that come with jython for these tasks. These modules are os, shutil, getopt, optparse, subprocess. We will just be giving you a quick feel for these modules. For details you should look at reference documentation like http://jyton.org/currentdocs. Then we'll cover a medium sized task to show the use of a few of these modules together. Parsing Commandline Options =========================== Many scripts are simple one-offs that you write once, use, and forget. Others become part of your weekly or even daily use over time. When you find that you are using a script over and over again, you often find it helpful to pass in command line options. There are three main ways that this is done in jython. The first way is to hand parse the arguments, the second is the getopt module, and the third is the newer, more flexible optparse module. Let's say we have a script called foo.py and you want to be able to give it some parameters when you invoke it the name of the script and the arguments passed can be examined by importing the sys module and inspecting sys.argv like so: # script foo.py import sys print sys.argv If you run the above script with a, b, and c as arguments: :: $jython foo.py a b c$ ['foo.py', 'a', 'b', 'c'] The name of the script ended up in sys.argv[0], and the rest in sys.argv[1:]. Often you will see this instead in jython programs: # script foo2.py import sys args = sys.argv[1:] print args which will result in: :: $jython foo2.py a b c$ ['a', 'b', 'c'] If you are going to do more than just feed the arguments to your script directly, than parsing these arguments by hand can get pretty tedious. The jython libraries include two modules that you can use to avoid tedius hand parsing. Those modules are getopt and optparse. The optparse module is the newer, more flexible option, so I'll cover that one. The getopt module is still useful since it requires a little less code for simpler expected arguments. Here is a basic optparse script: :: # script foo3.py from optparse import optionparser parser = optionparser() parser.add_option("-f", "--foo" help="set foo option") parser.add_option("-b", "--bar" help="set bar option") (options, args) = parser.parse_args() print "options: %s" % options print "args: %s" % args running the above: :: $jython foo3.py -b a --foo b c d$ options: {'foo': 'b', 'bar': 'a'} $args: ['c', 'd'] I'll come back to the optparse module with a more concrete example later in this chapter. Scripting The Filesystem ======================== We'll start with what is probably the simplest thing that you can do to a filesystem, and that is listing the file contents of a directory. :: >>> import os >>> os.listdir('.') ['ast', 'doc', 'grammar', 'lib', 'license.txt', 'news', 'notice.txt', 'src'] First we imported the os module, and then we executed listdir on the current directory, indicated by the '.'. Of course your output will reflect the contents of your local directory. The os module contains many of the sorts of functions that you would expect to see for working with your operating system. The os.path module contains functions that help in working with filesystem paths. Compiling Java Source ===================== While compiling java source is not strictly a typical scripting task, it is a task that I'd like to show off in my bigger example starting in the next section. The api I am about to cover was introduced in jdk 6, and is optional for jvm vendors to implement. I know that it works on the jdk 6 from sun and on the jdk 6 that ships with mac os x. For more details of the javacompiler api, a good starting point is here: http://java.sun.com/javase/6/docs/api/javax/tools/javacompiler.html. The following is a simple example of the use of this api from jython :: compiler = toolprovider.getsystemjavacompiler() diagnostics = diagnosticcollector() manager = compiler.getstandardfilemanager(diagnostics, none, none) units = manager.getjavafileobjectsfromstrings(names) comp_task = compiler.gettask(none, manager, diagnostics, none, none, units) success = comp_task.call() manager.close() Example Script: builder.py ========================== So I've discussed a few of the modules that tend to come in handy when writing scripts for jython. Now I'll put together a simple script to show off what can be done. I've chosen to write a script that will help handle the compilation of java files to .class files in a directory, and clean the directory of .class files as a separate task. I will want to be able to create a directory structure, delete the directory structure for a clean build, and of course compile my java source files. :: import os import sys import glob from javax.tools import (forwardingjavafilemanager, toolprovider, diagnosticcollector,) tasks = {} def task(func): tasks[func.func_name] = func @task def clean(): files = glob.glob("*.class") for file in files: os.unlink(file) @task def compile(): files = glob.glob("*.java") _log("compiling %s" % files) if not _compile(files): quit() _log("compiled") def _log(message): if options.verbose: print message def _compile(names): compiler = toolprovider.getsystemjavacompiler() diagnostics = diagnosticcollector() manager = compiler.getstandardfilemanager(diagnostics, none, none) units = manager.getjavafileobjectsfromstrings(names) comp_task = compiler.gettask(none, manager, diagnostics, none, none, units) success = comp_task.call() manager.close() return success if __name__ == '__main__': from optparse import optionparser parser = optionparser() parser.add_option("-q", "--quiet", action="store_false", dest="verbose", default=true, help="don't print out task messages.") parser.add_option("-p", "--projecthelp", action="store_true", dest="projecthelp", help="print out list of tasks.") (options, args) = parser.parse_args() if options.projecthelp: for task in tasks: print task sys.exit(0) if len(args) < 1: print "usage: jython builder.py [options] task" sys.exit(1) try: current = tasks[args[0]] except keyerror: print "task %s not defined." % args[0] sys.exit(1) current() The script defines a "task" decorator that gathers the names of the functions and puts them in a dictionary. We have an optionparser class that defines two options --projecthelp and --quiet. By default the script logs its actions to standard out. --quiet turns this logging off. --projecthelp lists the available tasks. We have defined two tasks, "compile" and "clean". The "compile" task globs for all of the .java files in your directory and compiles them. The "clean" task globs for all of the .class files in your directory and deletes them. Do be careful! The .class files are deleted without prompting! So lets give it a try. If you create a Java class in the same directory as builer.py, say the classic "Hello World" program: HelloWorld.java =============== :: public class HelloWorld { public static void main(String[] args) { System.out.println("Hello, World"); } } You could then issue these commands to builder.py with these results: :: [frank@pacman chapter8]$ jython builder.py --help Usage: builder.py [options] Options: -h, --help show this help message and exit -q, --quiet Don't print out task messages. -p, --projecthelp Print out list of tasks. [frank@pacman chapter8]$jython builder.py --projecthelp compile clean [frank@pacman chapter8]$ jython builder.py compile compiling ['HelloWorld.java'] compiled [frank@pacman chapter8]$ls DEBUG.classicHelloWorld.java HelloWorld.classicHelloWorldbuilder.py [frank@pacman chapter8]$ jython builder.py clean [frank@pacman chapter8]$ls HelloWorld.javabuilder.py [frank@pacman chapter8]$ jython builder.py --quiet compile [frank@pacman chapter8]$ls DEBUG.classicHelloWorldHelloWorld.java HelloWorld.classicHelloWorldHelloWorldbuilder.py [frank@pacman chapter8]$ 
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.