打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
Chapter 9: Scripting With Jython

Chapter 9: Scripting With Jython

In this chapter, we will look at scripting with Jython. For ourpurposes, we will define ‘scripting’ as the writing of smallprograms to help out with daily tasks. These tasks are things likedeleting and creating directories, managing files and programs, andanything else that feels repetitive that you might be able toexpress as a small program. In practice, however, scripts canbecome so large that the line between a script and a full sizedprogram can blur.

We’ll start with some very small examples to give you a feel forthe sorts of tasks you might script from Jython. Then we’ll cover amedium-sized task to show the use of a few of these techniquestogether.

Getting the Arguments Passed to a Script

All but the simplest of scripts will take some arguments from thecommand line. We’ll start our look at scripting by printing out anyargs passed to our script.

Listing 9-1.

import sysfor arg in sys.argv:    print arg

Let’s try running our script with some random arguments:

Listing 9-2.

$ jython args.py a b c 1 2 3args.pyabc123

The first value in sys.argv is the name of the script itself(args.py), the rest is the items passed in on the command line.

Searching for a File

Many scripting tasks take the form of ‘find a bunch of files and dosomething with them.’ So let’s take a look at how you might findsome files from a Jython program. We’ll start with a simple scriptthat finds any files in your current directory that match a passedin string:

Listing 9-3.

import sysimport osfor f in os.listdir(sys.argv[1]):    if f.find(sys.argv[2]) != -1:        print f

At the top of the script, we import the sys and os modules. Then werun a for loop over the results of os.listdir(sys.argv[1]). The osmodule is a good place to look if you are trying to accomplish thesorts of tasks you might do on a command prompt, such as listingthe files in a directory, deleting a file, renaming a file, and thelike. The listdir function of the os module takes one argument: astring that will be used as the path. The entries of the directoryon that path are returned as a list. In this case, if we run thisin its own directory (by passing in ‘.’ for the current directory),we see:

Listing 9-4.

$ lsargs.pysearch.py$ jython list.py . pyargs.pysearch.py$ jython list.py . sesearch.py

In the first call to list.py, we list all files that contain ‘py’,listing ‘args.py’ and ‘search.py.’ In the second call, we list allfiles that contain the string ‘se’, so only ‘search.py’ is listed.

The os module contains many useful functions and attributes thatcan be used to get information about your operating environment.Next we can open up a Jython prompt and try out a few os features:

Listing 9-5.

>>> import os>>> os.getcwd()'/Users/frank/Desktop/frank/hg/jythonbook~jython-book/src/chapter8'>>> os.chdir("/Users/frank")>>> os.getcwd()'/Users/frank'

We just printed out our current working directory with os.getcwd(),changed our current working directory to ‘/Users/frank,’ and thenprinted out the new directory with another call to os.getcwd(). Itis important to note that the JVM does not expose the ability toactually change the current working directory of the processrunning Jython. For this reason, Jython keeps track of its owncurrent working directory. As long as you stay within Jython’sstandard library, the current working directory will behave as youexpect (it will be changed with os.chdir()). However, if you importJava library functions that depend on a current working directory,it will always reflect the directory that Jython was started in.

Listing 9-6.

>>> import os>>> os.getcwd()'/Users/frank/Desktop/frank/hg/jythonbook~jython-book/src/chapter8'>>> from java.io import File>>> f = File(".")>>> for x in f.list():... print x...args.pysearch.py>>> os.chdir("/Users/frank")>>> os.getcwd()'/Users/frank'>>> os.listdir(".")['Desktop', 'Documents', 'Downloads', 'Dropbox', 'Library', 'Movies', 'Music', 'Pictures', 'Public', 'Sites']>>> g = File(".")>>> for x in g.list():...     print x...args.pysearch.py

Quite a bit went on in that last example, we’ll take it step bystep. We imported os and printed the current working directory,which is chapter8. We imported the File class from java.io. We thenprinted the contents of ‘.’ from the Java side of the world. Wethen changed directories with os.chdir() to the home directory, andlisted the contents of ‘.’ from Jython’s perspective, and listed‘.’ from the Java perspective. The important thing to note is that‘.’ from Java will always see the chapter8 directory because wecannot change the real working directory of the Java process—we canonly keep track of a working directory so that Jython’s workingdirectory behaves the way Python programmers expect. Too manyPython tools (like distutils and setuptools) depend on the abilityto change working directories to ignore.

Manipulating Files

Listing files is great, but a more interesting scripting problem isactually doing something to the files you are working with. One ofthese problems that comes up for me from time to time is that ofchanging the extensions of a bunch of files. If you need to changeone or two extensions, it isn’t a big deal to do it manually. Ifyou want to change hundreds of extensions, things get very tedious.Splitting extensions can be handled with the splitext function fromthe os.path module. The splitext function takes a file name andreturns a tuple of the base name of the file and the extension.

Listing 9-7.

>>> import os>>> for f in os.listdir("."):... print os.path.splitext(f)...('args', '.py')('builder', '.py')('HelloWorld', '.java')('search', '.py')

Now that we can get the extensions, we just need to be able torename the files. Luckily, the os module has exactly the functionwe need, rename:

Listing 9-8.

>>> import os>>> os.rename('HelloWorld.java', 'HelloWorld.foo')>>> os.listdir('.')['args.py', 'builder.py', 'HelloWorld.foo', 'search.py']

If you are manipulating any important files, be sure to put thenames back!

>>> os.rename('HelloWorld.foo', 'HelloWorld.java')>>> os.listdir('.')['args.py', 'builder.py', 'HelloWorld.java', 'search.py']

Now that you know how to get extensions and how to rename files, wecan put them together into a script (chext.py) that changesextensions:

Listing 9-9.

import sysimport osfor f in os.listdir(sys.argv[1]):    base, ext = os.path.splitext(f)    if ext[1:] == sys.argv[2]:        os.rename(f, "%s.%s" % (base, sys.argv[3]))

Making a Script a Module

If you wanted to turn chext.py into a module that could also beused from other modules, you could put this code into a functionand separate its use as a script like this:

Listing-9-10.

import sysimport osdef change_ext(directory, old_ext, new_ext):    for f in os.listdir(sys.argv[1]):        base, ext = os.path.splitext(f)        if ext[1:] == sys.argv[2]:            os.rename(f, "%s.%s" % (base, sys.argv[3]))if __name__ == '__main__':    if len(sys.argv) < 4:        print "usage: %s directory old_ext new_ext" % sys.argv[0]        sys.exit(1)    change_ext(sys.argv[1], sys.argv[2], sys.argv[3])

This new version can be used from an external module like this:

Listing 9-11.

import chextchext.change_ext(".", "foo", "java")

We have also used this change to introduce a little error checking,if we haven’t supplied enough arguments, the script prints out ausage message.

Parsing Commandline Options

Many scripts are simple one-offs that you write once, use, andforget. Others become part of your weekly or even daily use overtime. When you find that you are using a script over and overagain, you often find it helpful to pass in command line options.There are three main ways that this is done in Jython. The firstway is to hand parse the arguments that can be found from sys.argvas we did above in chext.py, the second is the getopt module, andthe third is the newer, more flexible optparse module.

If you are going to do more than just feed the arguments to yourscript directly, then parsing these arguments by hand can getpretty tedious, and you’ll be much better off using getopt oroptparse. The optparse module is the newer, more flexible option,so we’ll cover that one. The getopt module is still useful since itrequires a little less code for simpler expected arguments. Here isa basic optparse script:

Listing 9-12.

# script foo3.pyfrom optparse import optionparserparser = 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" % optionsprint "args: %s" % args

running the above:

Listing 9-13.

$ jython foo3.py -b a --foo b c d$ options: {'foo': 'b', 'bar': 'a'}$ args: ['c', 'd']

In this example, we have created an optionparser and added twooptions with the add_option method. The add_option method takesat least one string as an option argument (‘-f’ in the first case)and an optional long version (‘–foo’ in the previous case). Youcan then pass in optional keyword options like the ‘help’ optionthat sets the help string that will be associated with the script.We’ll come back to the optparse module with a more concrete examplelater in this chapter.

Compiling Java Source

While compiling Java source is not strictly a typical scriptingtask, it is a task that we’d like to show off in a bigger examplestarting in the next section. The API we are about to cover wasintroduced in jdk 6, and is optional for jvm vendors to implement.We know that it works on the jdk 6 from Sun (the most common JDK inuse) and on the jdk 6 that ships with mac os x. For more details ofthe 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 fromJython:

Listing 9-14.

from javax.tools import (ForwardingJavaFileManager, ToolProvider, DiagnosticCollector,)names = ["HelloWorld.java"]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()

First we import some Java classes from the javax.tools package.Then we create a list containing just one string,‘HelloWorld.java.’ Then we get a handle on the system Java compilerand call it ‘compiler.’ A couple of objects that need to get passedto our compiler task, ‘diagnostics’ and ‘manager’ are created. Weturn our list of strings into ‘units’ and finally we create acompiler task and execute its call method. If we wanted to do thisoften, we’d probably want to roll up all of this into a simplemethod.

Example Script: Builder.py

So we’ve discussed a few of the modules that tend to come in handywhen writing scripts for Jython. Now we’ll put together a simplescript to show off what can be done. We’ve chosen to write a scriptthat will help handle the compilation of java files to .class filesin a directory, and clean the directory of .class files as aseparate task. We will want to be able to create a directorystructure, delete the directory structure for a clean build, and ofcourse compile our java source files.

Listing 9-15.

import osimport sysimport globfrom javax.tools import (forwardingjavafilemanager, toolprovider,         diagnosticcollector,)tasks = {}def task(func):    tasks[func.func_name] = func@taskdef clean():    files = glob.glob("\*.class")    for file in files:        os.unlink(file)@taskdef compile():    files = glob.glob("\*.java")    _log("compiling %s" % files)    if not _compile(files):        quit()    _log("compiled")def _log(message):    if options.verbose:        print messagedef _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 successif __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 thefunctions and puts them in a dictionary. We have an optionparserclass that defines two options –projecthelp and –quiet. Bydefault the script logs its actions to standard out. The option–quiet turns this logging off, and –projecthelp lists theavailable tasks. We have defined two tasks, ‘compile’ and ‘clean.’The ‘compile’ task globs for all of the .java files in yourdirectory 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 let’s give it a try. If you create a Java class in the samedirectory as builder.py, say the classic ‘Hello World’ program:

HelloWorld.java

Listing 9-16.

public class HelloWorld {    public static void main(String[] args) {        System.out.println("Hello, World");    }}

You could then issue these commands to builder.py with theseresults:

Listing 9-17.

[frank@pacman chapter8]$ jython builder.py --helpUsage: 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 --projecthelpcompileclean[frank@pacman chapter8]$ jython builder.py compilecompiling ['HelloWorld.java']compiled[frank@pacman chapter8]$ lsHelloWorld.java HelloWorld.class builder.py[frank@pacman chapter8]$ jython builder.py clean[frank@pacman chapter8]$ lsHelloWorld.java builder.py[frank@pacman chapter8]$ jython builder.py --quiet compile[frank@pacman chapter8]$ lsHelloWorld.class HelloWorld.java builder.py[frank@pacman chapter8]$

Summary

This chapter has shown how to create scripts with Jython. We havegone from the most simple one- and two-line scripts to largescripts with lots of optional inputs. We hope this will help youcreate your own tools to help automate some of the repetition outof your days.

本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
Robot Framework简介及安装
Java Test Tools
Java技术版图俯瞰——awesome
How to Install Eclipse for Java Programming on Windows, Mac and Linux
What is the JDK? Introduction to the Java Developm...
Java in Flames – Netflix TechBlog – Medium
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服