Python Rocks! and other rants
Weblog of Kent S Johnson

#70 2005-12-28 10:49:20

Why I love Python 5

Easy introspection and dynamic loading

This example shows off several useful features of Python including introspection, dynamic loading, first-class functions and flexible except clauses.

At work I have some Java code that uses XPath support from the Xalan class org.apache.xpath.XPathAPI. In Java 1.4 this class is provided with the JRE. In Java 1.5 they moved the class to com.sun.org.apache.xpath.internal.XPathAPI. I need to run with either version of Java. I prefer not to bundle Xalan with my program, so I wrote a wrapper that dynamically locates the correct version and dispatches to it:

// The XPathAPI is in different packages in Java 1.4 and 1.5.
// Use introspection to find the right one
private static Method __selectSingleNode;

static {
    // Look for the XPathAPI class in two places
    Class XPathAPI = null;
    try {
        XPathAPI = Class.forName("org.apache.xpath.XPathAPI");
    } catch (ClassNotFoundException e) {
        try {
            XPathAPI = Class.forName("com.sun.org.apache.xpath.internal.XPathAPI");
        } catch (ClassNotFoundException e1) {
        }
    }

    // Get the methods we support
    try {
        __selectSingleNode =
          XPathAPI.getMethod("selectSingleNode",
                             new Class[] { Node.class, String.class} );
    } catch (SecurityException e) {
    } catch (NoSuchMethodException e) {
    }
}

/** XPathAPI.selectSingleNode */
public static Node selectSingleNode(Node node, String xpath) {
    try {
        return (Node)__selectSingleNode.invoke(null, new Object[] { node, xpath });
    } catch (IllegalArgumentException e) {
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    } catch (InvocationTargetException e) {
        e.printStackTrace();
    }
    return null;
}

Wow, what an ugly mess! What would it look like in Python?

The initial static block would become a conditional import:

try:
  import org.apache.xpath.XPathAPI as XPathAPI
except ImportError:
  import com.sun.org.apache.xpath.internal.XPathAPI as XPathAPI

That was easy - and wait - we're done now! The client code can call XPathAPI.selectSingleNode() and it will work!

But suppose for the sake of example we want to get a reference to selectSingleNode using introspection. That is as simple as

__selectSingleNode = getattr(XPathAPI, 'selectSingleNode')

This __selectSingleNode is itself a callable function (not a wrapper around a function) so clients can call it directly; the selectSingleNode() wrapper is not needed at all.

I have omitted the exception handling in the Python code because these exceptions are fatal and might as well terminate the program. If I wanted to catch them I could use an except clause with multiple exception types, instead of multiple except clauses, something like this:

try:
  __selectSingleNode = ...
except (SecurityException, NoSuchMethodException), e:
  e.printStackTrace()

Categories: Java, Python

 

#60 2004-10-19 15:18:24

Relief

It's such a relief to be coding in Jython again after working with Java. In Java I feel like I'm fighting the language and the APIs all the time. It's way too hard to get anything done. Python just does what I want with much less straining and fuss.

Aaahhh!

Categories: Java, Python

 

#59 2004-10-17 14:57:04

Sticky Widgets

A technique I am using quite a bit is to make sticky widgets - GUI widgets that autonomously remember some part of their state. For example, a text field that remembers its last value; a window that remembers its last size and shape; a file dialog that remembers the directory it last showed.

These widgets make it very easy to create a user interface with some persistent state. This saved state makes the GUI much easier on the users.

In my case the widgets are written in Java using Swing. Each widget takes a preferences key as a constructor argument. The given key and the class name make the full key. This allows the widget to save and restore its state without reliance on any application globals, and without writing any application code specifically to handle the stickyness. The widget registers itself as a listener to itself so it is notified when its state changes and it persists its new state.

This is work code so I won't post it but it is really pretty easy to do and very handy.

Categories: Java

 

#58 2004-10-06 19:53:36

Spring Free

For my current project, a distributed application with a database back-end, I tried Hibernate and Spring. I really wanted to like them! They have lots of cool features and make some things incredibly easy. But in the end I have gone back to tried-and-true simple tools - Jython, Velocity, Jetty and not much else.

Hibernate felt like it got in the way as much as it helped. I was constantly having to figure out the Hibernate way of things. My data is largely a tree structure flattened into a database table. Hibernate's lazy loading was sometimes great and sometimes exactly wrong.

Spring just felt too big. Every time I needed a new piece I would have to add another Spring jar to my lib directory and usually a few more jars that it depended on. It felt like using a sledgehammer to squash an ant.

Java also feels quite cumbersome now. I have been working primarily in Jython for about a year now and I hate the hoops that Java makes me jump though to get anything done.

In the end it all felt too confining. I was Mech Warrior, high in a robot programming vehicle, directing awesome power from from my command post. But I longed to put my feet on the ground, pick up a light pack and run.

So I have chucked it all, salvaging what I can, starting over for the rest. What a relief it is!

Categories: Java, Python

 

#54 2004-08-26 20:32:00

Jython and Spring Framework

I am trying out Spring Framework in a current project and I like it. I especially like the support for Hibernate transactions. The only reservation I have is that Spring won't work with Jython classes.

I have made a start at implementing Jython support. I have classes that allow a Jython bean to be instantiated from the IoC framework. Setting properties on the beans is awkward, the bean has to implement an interface that defines the setter methods. But it's a start!

One of the limitations of Jython is that it doesn't play very well with Java introspection. If you want your Jython methods to be visible to Java introspection you have two choices:

  • compile your scripts with jythonc
  • implement a Java interface containing the methods of interest

The first option is problematic (I have had too much trouble with jythonc). To work with Spring setter injection, the second requires that every setter is declared in an interface; not really practical. Since the Spring Inversion of Control framework is built on top of an introspection engine, this is a problem for using it with Jython :-( Rod Johnson (Spring author) says this may be fixable in the framework.

You can learn more from this thread on the Spring support forum: http://forum.springframework.org/viewtopic.php?p=1643#1643

Categories: Java, Python

 

#51 2004-08-07 14:29:20

Hibernate

I have been trying out the Hibernate persistence framework for my current work project, Curriculum Builder. So far I am very impressed with it.

Hibernate is a transparent object/relational mapping framework. It makes it very easy to map your objects to a relational database.

"Transparent" means that your business objects need little or no change to work with Hibernate. Persistent objects are POJOs - they don't have to extend a Hibernate base class or implement a Hibernate interface.

The mapping between your objects and the database is defined in an xml configuration file. The mapping is very flexible. For example your business object can have a collection of associated objects that map through a foreign key in the database.

Several features of Hibernate made me think it was worth trying it out for Curriculum Builder.

Hibernate transparently supports the unit-of-work pattern. The way this works is, you access your persistent objects in the scope of a Hibernate Session. Any changes you make to the persistent objects will be detected automatically by Hibernate and persisted when the session is committed.

Hibernate supports optimistic locking with object versioning. Some form of optimistic locking will be essential in CB and I'm very happy not to write it myself!

Hibernate supports lazy loading, so you can control when dependent objects are loaded. For example a Learning Path object might contain a list of courses. With lazy loading, the actual course objects are not loaded unless the collection is accessed.

For example, to create a new Course object and persist it to the database, I use this code. The session has to be told to save the course:

Session s = openSession();
Transaction tx = s.beginTransaction();
Course course = new Course("ABC0101", "Test course", "enUS", "_ss_bs", false);
s.save(course);
tx.commit();
s.close();

To load a course and change it, I do this. Note that I don't have to tell Hibernate that the course has changed, it figures that out by itself:

s = openSession();
tx = s.beginTransaction();
course = findOneCourse(s, "ABC0101");
course.setTitle("Test course new title");
tx.commit();
s.close();

findOneCourse() uses Hibernate's query mechanism:

private Course findOneCourse(Session s, String code) throws HibernateException {
    Query query = s.createQuery("from Course where code= :code");
    query.setString("code", code);
    Course course = (Course)query.uniqueResult();
    return course;
}

Here is a test case that checks optimistic locking. It creates an object, then modifies it in two concurrent sessions. When the second session commits it gets a StaleObjectStateException:

public void testLocking() throws Exception {
    Session s = openSession();
    Transaction tx = s.beginTransaction();
    Course course = new Course("ABC0103", "Test course 3", "enUS", "_ss_bs", false);
    s.save(course);
    tx.commit();
    s.close();

    Session s1=null, s2=null;
    try {
        // Change the course in two different sessions
        s1 = openSession();
        Transaction tx1 = s1.beginTransaction();
        Course course1 = findOneCourse(s1, "ABC0103");
        assertEquals("Test course 3", course1.getTitle());

        s2 = openSession();
        Transaction tx2 = s2.beginTransaction();
        Course course2 = findOneCourse(s2, "ABC0103");
        assertEquals("Test course 3", course2.getTitle());

        course1.setTitle("Test course three");
        tx1.commit();
        s1.close();

        try {
            course2.setTitle("Conflict!");
            tx2.commit();
            s2.close();
            fail("Should throw StaleObjectStateException");
        } catch (StaleObjectStateException e) { }

    } finally {
        if (s1 != null) s1.close();
        if (s2 != null) s2.close();
    }
}

For more information, visit the Hibernate web site: http://www.hibernate.org/

Take a look at the road map: http://www.hibernate.org/152.html

Hibernate: A Developers Notebook is a good place to get started.

Categories: Java

 

#49 2004-06-24 07:35:28

Eclipse Preannouncement

You have probably seen the announcement that Eclipse 3.0 Final has been released. What you may have missed is the last line of the announcement: "Distributions of Eclipse 3.0 will be available by June 30 for download from http://www.eclipse.org."

In other words, they pre-announced the release! I don't think I've seen anything like this from an open-source project before - usually the release announcement accompanies the actual release.

Categories: Java

 

#41 2004-05-18 07:52:32

Velocity and Velocity Tools releases

The Jakarta Velocity project has recently released new versions of both the main Velocity package and the Velocity Tools sub-package. Velocity is a flexible, powerful and easy-to-use template engine that can be used for any kind of text generation including web pages, XML files and code generation. Velocity Tools includes modules that integrate Velocity with Struts and allow Velocity to be used as an alternative to JSP for Struts views. Velocity's simple, uncluttered syntax makes it a very readable alternative to JSP.

Velocity has a built-in introspection engine that makes it suitable for generating text output from any kind of domain data. In particular it is a great way to generate XML from a Java object model. All the XML markup goes in the template file instead of cluttering up your code. [*]

I have used Velocity on many projects for report generation, web page generation, and output file formatting. I highly recommend it.

With the new releases, this is a great time to take a look at these packages.

[*]: Of all the ways to generate XML from a Java object model, I think Velocity is one of the best. Just like with web page generation, it separates content and presentation and keeps the markup out of your code. Here are some alternatives:
  • If your data is in a DOM model already, you can serialize it with DOM tools. This works as a way to persist your data. For custom formatting you need to transform the data, for example using XSL.
  • You can build a DOM model and serialize it. That is a lot of work just to generate a file.
  • To serialize a custom model you can use one of the many Java-to-XML mapping products such as Castor XML Mapping or JAXB. Again, this works for persistence but it is hard to generate a custom format from it.
  • You can write code to generate the XML directly or using a helper library such as XMLWriter. This is painful in several ways. It puts the markup in the code, which is not such a good idea. You have to manually match up begin and end tags, which is difficult and error-prone. There is no good place for the code - it doesn't feel right either in the data classes or as a separate module.
  • Or (drum-roll, please) you can write a Velocity template to generate the XML. In code, you just need a little boilerplate to set up the template engine and run the template. All the XML structure is in one place, in the template file. You have full access to the data using Velocity introspection. You can add more output formats just by creating new templates and hooking them in to the code. Life is good!

Categories: Java

 

#35 2004-04-30 09:54:08

Preaching to the Choir

My two "Why I love Python" articles have been wildly popular. Much of their popularity is due to being mentioned in the Daily Python-URL. But I have to wonder, why do Pythonistas so enjoy reading about why Python is great? And how can I reach the Java programmers where I work and convince them to try Python?

Categories: Java, Python

 

#34 2004-04-29 20:10:40

Why I love Python 2

Python makes it very easy to build complex data structures. One place this is handy is with data-driven programming.

For Meccano I wrote a simple walk-by-rule engine. It walks the tree of domain data and applies callbacks at indicated points. The walk is driven from a tree structure that can be quite large and deeply nested. (I have written about the rule engine before.)

Here is a simple example using some of the same techniques. As you read the example, imagine that the list of rules might be hundreds of lines long and deeply nested. Later on I will indicate some other ways the example might be extended.

Assume we are given a dictionary and we are to print its contents in a nested form where the nesting and order of keys in the output is given by the location of dictionary keys in a structure built from nested lists.

The essential idea of the problem is to use a staticly defined nested list to drive the formatting.

Python version

The Python version is short and sweet (14 lines, 349 chars). The data structures are defined easily and the output generation is simple:

data = { 'a':1, 'b':2, 'c':3, 'd':4, 'e':5 }

formatData = [ 'a', [ 'b', [ 'd', 'e' ], 'c' ] ]


def output(format, indent):
  for item in format:
      if type(item) == type([]):
          output(item, indent+2)
      else:
          val = data[item]
          print '%*s%s: %s' % (indent, ' ', item, val )

output(formatData, 0)

The output is:

a: 1
 b: 2
   d: 4
   e: 5
 c: 3

Java version

The Java version is long and ugly. It is 44 lines and 1127 chars - over three times the size of the Python version! The Map is defined in code. The nested list needs extra (Object[]) casts that greatly reduce readability. The code is much more verbose; this is always the case with Java collection code vs Python:

import java.util.HashMap;
import java.util.Map;

public class Structure {

   static Map data = new HashMap();

   static {
       data.put("a", new Integer(1));
       data.put("b", new Integer(2));
       data.put("c", new Integer(3));
       data.put("d", new Integer(4));
       data.put("e", new Integer(5));
   }

   static Object[] struct = {
       "a",
       new Object[]{
           "b", new Object[]{
               "d", "e"
           },
           "c"
       }
   };

   public static void output(Object[] format, int indent) {
       for (int i = 0; i < format.length; i++) {
           Object item = format[i];
           if (item instanceof Object[]) {
               output((Object[])item, indent+2);
           }
           else {
               Integer val = (Integer)data.get(item);
               for (int j=0; j<indent; j++)
                   System.out.print(' ');
               System.out.println(item + ": " + val);
           }
       }
   }

   public static void main(String[] argv) {
       output(struct, 0);
   }
}

Reading data from a file

Now suppose you want to put the configuration data in a file that can be changed at runtime and reloaded as needed.

In Python, all you have to do is move the data structure definitions into a separate Python module. Client code imports the data module and reloads it before each use to re-read the source if it has changed.

In Java, typically the configuration data will be put into a text (non-code) format. XML works well for storing hierarchical data so it would be an obvious choice. Now, you have to define an XML format to hold the data and write code to load and parse the data.

So a hidden benefit of Python is that it includes a parser with the runtime. The parser can read text files and create native collections. This is a huge plus for Python!

More Python benefits

Imagine that part of the nested structure is a class or function name. In Python, the configuration module is code so it can define classes and functions that are referenced directly in the data. Or you can import the module that defines the class or function, then put a reference to it in the data.

In Java, you would typically use separate compiled modules to define the classes and reflection to reference them. The use of reflection further complicates the configuration parser or the client code.

What if parts of the data are repeated? In Python, it's no problem! A repeated section of the configuration can be defined separately and included into the main structure by reference. With an XML representation, the data would likely be repeated in multiple locations in the file.

This all works

I'm not just making this up for the sake of argument - these are all techniques I have used in production code. Python data structures are wonderfully flexible and easy to use!

Categories: Java, Python

 

#30 2004-04-23 08:30:56

Why I love Python

The code I wrote last night to build a Map of Maps shows one reason why I like Python so much - it is so easy to work with collections!

I wrote a sample app that shows the same thing in Java and Python. The requirement is to take a list of triples of strings in the form

[ language code, item id, localized string ]

and build a two level Map from language code => item id => data triple. Both examples include a simple test driver which prints:

This is a test
C'est un essai
no data
Another test
no data

Java version

Here is the Java version abstracted from the code I wrote last night. It is 56 lines and 1797 characters. The functional part of the code (excluding main()) is 38 lines.

import java.util.*;

public class Test {

  private Map _map = new HashMap();

  public Test(String[][] data) {
      // Convert the input data to a two-level Map from language code => course ID => locale data
      for (int i = 0; i < data.length; i++) {
              String[] itemData = data[i];

          String lang = itemData[0];

          Map langMap = (Map)_map.get(lang);
          if (langMap == null) {
              langMap = new HashMap();
              _map.put(lang, langMap);
          }

          String id = itemData[1];
          langMap.put(id, itemData);
      }
  }

  public String lookup(String lang, String id, String defaultData) {
      Map langMap = (Map)_map.get(lang);
      if (langMap == null) return defaultData;

      String[] itemData = (String[])langMap.get(id);
      if (itemData == null) return defaultData;

      String title = itemData[2];
      if (title == null || title.length() == 0)
          return defaultData;

      return title;
  }


  public static void main(String[] args) {
      String[][] data = {
          { "en", "123", "This is a test" },
          { "fr", "123", "C'est un essai" },
          { "es", "123", "" },
          { "en", "345", "Another test" }
      };

      Test test = new Test(data);

      System.out.println(test.lookup("en", "123", "no data"));
      System.out.println(test.lookup("fr", "123", "no data"));
      System.out.println(test.lookup("es", "123", "no data"));
      System.out.println(test.lookup("en", "345", "no data"));
      System.out.println(test.lookup("fr", "345", "no data"));
  }
}

Python version

And here is the Python version. It is 34 lines and 1036 characters. The functional part of the code (excluding main) is 17 lines. That is roughly 40% shorter than the Java version.

class Test:

  def __init__(self, data):
      # Convert the input data to a two-level Map from language code => course ID => locale data
      self._map = {}

      for itemData in data:
          lang, id = itemData[:2]
          self._map.setdefault(lang, {})[id] = itemData


  def lookup(self, lang, id, defaultData):
      itemData = self._map.get(lang, {}).get(id)
      if  not itemData:
          return defaultData

      return itemData[2] or defaultData


if __name__ == '__main__':
  data = [
          [ "en", "123", "This is a test" ],
          [ "fr", "123", "C'est un essai" ],
          [ "es", "123", "" ],
          [ "en", "345", "Another test" ]
      ]

  test = Test(data);

  print test.lookup("en", "123", "no data")
  print test.lookup("fr", "123", "no data")
  print test.lookup("es", "123", "no data")
  print test.lookup("en", "345", "no data")
  print test.lookup("fr", "345", "no data")

I know which version I prefer!

So how come I'm using Java?

Sigh. I chickened out.

I work in a predominantly Java shop. The project I am working on could grow to a GUI app or a web app or both. I'm familiar with Java Swing, Jetty web server and servlets. I know that there is a great variety of mature tools available for writing Java web apps. I have support available at work if I need help.

On the Python side, I would have to learn wxPython and/or one of the Python web app frameworks like WebWork or Quixote. I don't get such warm fuzzy feelings about the completeness of these frameworks, both in features and release quality. I would be working on my own and out on a limb if I had any problems.

In the end, I decided it was too great a risk so I went with the safer solution.

Sigh.

Categories: Java, Python

 
© Kent S Johnson Creative Commons License

Comments about life, the universe and Python, from the imagination of Kent S Johnson.

kentsjohnson.com

Weblog home

All By Date

All By Category

Agile

Java

Python

Essays

XML-Image

BlogRoll

Powered by Firedrop2