Ant Jython Tasks (PAnt Tasks)

Posted on 08/20/2008 , 12 Comments ( Add )

PAnt build tool comes with several Ant tasks to facilitate the use of Jython/Python from Ant.

PAnt tasks have a number of advantages over built-in <script language="jython"> way of invoking Jython from Ant:

  • More graceful exception handling. Jython code invoked using "script" generates long error stack that contains full stack trace of the "script" task itself. Sifting through the traces and trying to distinguish Java trace from Python trace is quite painful. PAnt "jython" task produces brief readable python-only error stack.
  • You can use Ant properties as parameters ("jython" task makes them available in the local namespace of the calling script).
  • Convenience "import" attribute.
  • "jythonInit" task allows for setting python.path using Ant path structure.
  • Jython interpreter is initialized once per Ant project. All scripts invoked from the same Ant project reuse the same built-in namespace. So you can define variables and imports in one call and use them in a subsequent call.
  • Task name ( the name that prefixes all console output from Ant for a given task) is generated automatically based on the supplied Python code.
  • "verbose.jython" property triggers verbose output for jython-related tasks only. This is much easier than trying to scan through hundreds of lines of general "ant -v" verbose log.

Example:

Ant code:


<jythonInit pythonPathRef="python.path" />
<property name="testProp" value="testVal" />

<jython>
print "Property from ant:", testProp
# define a var that we can use in other scripts
s="test"
</jython>

<jython>
print "Var created earlier: ",s
</jython>

<jython  import="from testmodule import *" exec="test(testProp)"  />

"testmodule" python code:


from pant.pant import project 
def test (prop):
    print "Passed parameter: ",prop
    print "Test property: ", project.properties["testProp"]

Please refer to this build.xml file for more examples.

The tasks can be used independently of PAnt python code.

PAnt Ant Tasks Reference

Getting Started

Download PAnt, extract pant.jar and create "taskdef" as described here

"jythonInit" Task

The tasks initializes jython interpreter. Because of the overhead, the interpreter is initialized only once even if jythonInit is invoked multiple times. The repeating calls are simply ignored.
jythonInit automatically adds pant.pant module to PYTHONPATH.

Attributes:

  • pythonPathRef - PYTHONPATH (python.path) to use, given as reference to a PATH defined elsewhere. Required if "pythonPath" nested element was not provided.
  • pythonHome - location of python distribution (optional). If provided,jythonInit will set python.home system property and will automatically add ${python.home}/Lib to the python path if ${python.home}/Lib exists.
  • cacheDir - location of jython cachedir used for caching packages (optional). Defaults to ${java.io.tmpdir}/jython_cache (note-- this is different from default jython behavior).

Nested elements:

pythonPath - python.path to use defined using Ant path-like structure. Required if "pythonPathRef" attribute was not provided.

Special properties:

log.python.path - if set to "true", jythonInit will print python path to Ant log. Default: false.

"jython" Task

Invokes python code.
Note: by default, jython does not print python stack trace in case of an exception. To see the trace, run Ant in verbose mode using "-v" or use "-Dverbose.jython=true" property.

Attributes:

  • exec - Python code snippet to execute. Typically, this is a function from a module available from python.path. This has to be a single line, e.g., mod.fun() although you could combine multiple statements separated by ";". Required if "execfile" was not provided.
  • import - a convenience attribute for providing "import" statement. Its only purpose is to make the task invocation more readable. Alternatively, you can have "import" as part of the"exec",e.g., exec="import mod;mod.fun()". Optional.
  • execfile - path to a python script file. Required if "exec" was not provided.

Nested elements:

Inline text with python code.

Special properties:

verbose.jython - if set to "true", jython will print additional information about executing python code to Ant log. Default: false.

pimport Task

Creates Ant targets from a python module. Functions that will be used as targets have to be marked using "@target" decorator as described here.
Python module name is used as Ant project name. Target overriding works the same way with Ant import task. In other words, targets defined using pimport will override targets previously defined using "import" or "pimport" tasks.

Attributes:
module - python module to create targets from. The module has to be available from python.path specified using jythonInit.

12 Responses to “Ant Jython Tasks (PAnt Tasks)”

#34753 | September 18th, 2008 | Mark Melvin

I am very interested in both your Jython task and PAnt, but both download links are broken. Do you have these available elsewhere?

#34754 | September 18th, 2008 | Alexander Ananiev

The links are now fixed. Also, I’ll be uploading an updated version of jythonTask in the next few days.

#35120 | January 13th, 2010 | Michel

Interesting work, but configured elements within tasks, like manifest in jar or file in filelist are not supported. Do you plan to fix it?

#35121 | January 13th, 2010 | Alexander Ananiev

Hi Michel. It is certainly supported. A nested element is represented by a nested dictionary. E.g.,
ant.copy( todir=”${target.dir}”, overwrite=”true”,
fileset=dict( dir=”.”, includes=”*.xml” ),
filterchain=dict( expandproperties={} ) )
Repeating elements are represented as a list of dictionaries:
filelist=dict(dir=my_dir, file=[{“name”:”foo.xml”},{“name”:bar.xml”}] )
Or, you can also use suffixes with underscores:
filelist=dict(dir=my_dir, file_foo={“name”:”foo.xml”},file_bar={“name”:bar.xml”} )
Let me know if it does not work for you.

#35135 | February 2nd, 2010 | Michel

Thank you for your answer. What i meant was that for particular nested elements called ‘configured element’, like for example the element ‘manifest’ nested in ‘jar’ instead of being specified in an attribute of ‘jar’, pant does not configure those element properly . Just try the pant equivalent of:

and check the jar, you’ll see that the manifest is not populated as wanted.
the same for etc…

#35136 | February 2nd, 2010 | Michel

Sorry the example did not appear in my previous post because of xml the pant example is:
@target(description=”JARs the Task”)
def jar():
“JARs the Task”
zipfileset = dict(src=”dist/libs/lucene-core-2.4.1.jar”)
filelist = dict(dir=”dist/libs”, file=dict(name=”htmlgen.jar”))
manifest = dict(attribute={“name”:”Main-Class”, “value”:”search.Main”})
ant.jar(destfile=”debug/debugpant.jar”, basedir=”bin”,
zipfileset=zipfileset, filelist=filelist, manifest=manifest)

pant works formally on it except that the jar manifest does not contain the Main-Class attribute and the specified libs…..

#35142 | February 5th, 2010 | Alexander Ananiev

Michel, I was able to reproduce the issue, I think I know what the problem is. I’ll try to release a fix shortly. Thanks.

#35341 | March 27th, 2010 | Alexander Ananiev

It took me a little longer than anticipated, but the bug affecting manifest and several other tasks is now fixed: http://myarch.com/pant-2-0-1-is-released.

#35355 | July 31st, 2010 | Guancio

Nice work. I found this project after investigating the adoption of gant.
I’will start to usa Pant as build system for my current J2EE project.

I’m using a new decorator to ensure that target functions (if directly invoked by python) runs only once:

def once(f):
to_run = False
def wrap(*args):
if not to_run:
return
return f(*args)
return wrap

The I define my tasks as:

@once
@target(depends=”clean1″)
def compile():

I think that this could be the behavior of the @target decorator

#35356 | August 1st, 2010 | Alexander Ananiev

Hi Guancio,
I’m trying to understand why it would be desirable for a target function to only run once. Can you explain?
Cheers,

#35365 | October 15th, 2010 | Guancio

Dear Alexander,
the main reason to execute target function once is because they represent ant target :D.
Using pant I have two way (that can be mixed) to implements dependencies.
The first one is to declare ant target dependencies. If f1 depends on f2 and f3, and both f2 and f3 depends on f4, If I require the execution of the task f1(formally the dask implemented by f1) f4 will be executed only once.
The former approach is to manually call the function f2 and f3 inside the body of f1. In this case the function f4 will be executed twice.

#35784 | September 7th, 2012 | Samuel

Hi there,

I noticed an unusual problem with the tag when accessing ant properties. I often use ant properties like this:

web.server.name

It seems that jython doesn’t like accessing properties that contain a period in the name.

Is this somthing that others have noticed?