{"id":908,"date":"2009-06-30T09:28:17","date_gmt":"2009-06-30T08:28:17","guid":{"rendered":"http:\/\/fiber-space.de\/wordpress\/?p=908"},"modified":"2009-06-30T09:28:17","modified_gmt":"2009-06-30T08:28:17","slug":"stitches-of-a-flea-language-defining-java-annotations-in-jython","status":"publish","type":"post","link":"http:\/\/fiber-space.de\/wordpress\/2009\/06\/30\/stitches-of-a-flea-language-defining-java-annotations-in-jython\/","title":{"rendered":"Stitches of a flea language &#8211; defining Java annotations in Jython"},"content":{"rendered":"<h3>Jython annotations &#8211; anyone?<\/h3>\n<p>The last few days I tried to figure out how to create Jython annotations. A Jython annotation<em> <\/em>is defined here as a <em>Java annotation lifted from Jython to Java. <\/em>So one essentially defines a Java annotation in a Jython class. A Jython annotation shall not be confused with a decorator. A Python ( or Jython ) decorator is just a higher order function ( or callable ). It might create attributes in Jython objects but those are not the same as Java annotations reflected by the Java runtime. Without Jython annotations Jython is right now essentially stuck in a pre version 1.5 Javaverse and Jython classes are disconnected from modern Java frameworks and cannot be plugged.<\/p>\n<p>Jython annotations in Jython 2.5 don&#8217;t work out of the box. It is not much known yet about how or when Jython annotations will be supported by core Jython. The lead Jython developer Frank Wierzbicki announced something along the lines in his 2008 PyCon <a href=\"http:\/\/us.pycon.org\/2008\/conference\/talks\/?filter=jython\">conference talk<\/a> but this is now about 16 months ago. I could temper my impatience if Jython annotations were just around the corner but what can we expect after those 16 months?<\/p>\n<p>In this article I introduce a library that enables lifting of meta-data from Jython to Java and loading Java back into Jython. One key element is Java code generation and dynamic compilation using the Java 6 <a href=\"http:\/\/fiber-space.de\/wordpress\/?p=858\">Compilation API<\/a>. Another one is interface extraction of Jython classes using the rich reflection API provided by core Jython.<\/p>\n<h3>Lifting up Jython classes<\/h3>\n<p>For every Jython class `JyClass` one can generate a Java class `JaFromJyClass` by means of interface extraction. We assume `JyClass` to be a subclass of a Java class, e.g. `Object`, and translate the Jython class<\/p>\n<pre lang=\"python\">class JyClass(Object):\r\n    def foo(self, *args):\r\n        print \"foo\", args<\/pre>\n<p>into a corresponding Java class<\/p>\n<pre lang=\"python\">public class JaFromJyClass extends Object{\r\n    PyObject jyobject;\r\n\r\n    public PyObject foo(PyObject[] args)\r\n    {\r\n        return jyobject.invoke(\"foo\", args);\r\n    }\r\n}<\/pre>\n<p>This class is basically a proxy for the `jyobject` member variable of type PyObject which is a Jython API type. Once we have generated the Java code from Jython we can dynamically compile and load the Java code into Jython:<\/p>\n<pre lang=\"python\">JaFromJyClass = createJavaClass(\"JaFromJyClass\", source)\r\njainstance = JaFromJyClass()\r\njainstance.jyobject = JyClass()\r\njainstance.foo(9)  # prints 'foo 9'<\/pre>\n<p>This was straightforward and hints on our translation strategy. Next we review the Jython to Java translations in more detail.<\/p>\n<h3>Jython to Java translations<\/h3>\n<h4>PyObject Injections<\/h4>\n<p>We cannot be glad with the way the `jyobject` was assigned to the `jainstance` in the previous example. The particular  assignment protocol implies that the Jython script has always control over the instantiation of Jython classes. But once we plug the class into a framework the framework takes over. A better solution is to inject the PyObject using a factory mechanism.<\/p>\n<pre lang=\"python\">public JaFromJyClass() {\r\n    super();\r\n    jyobject = JyGateway.newInstance(\"JyClass\", this, null);\r\n    jaobject = (Object)jyobject.__tojava__(Object.class);\r\n}<\/pre>\n<p>The factory is called `JyGateway`. The `JyGateway` is a Java class which defines HashMap called registry<\/p>\n<pre lang=\"python\">public static HashMap&lt;String, PyDictionary&gt; registry = new HashMap&lt;String, PyDictionary&gt;();<\/pre>\n<p>The keys of the Java HashMap are Strings that represent class names. The `PyDictionary` is a dictionary of Jython functions. Right now two functions are defined: `newInstance` and `callStatic`. Both of them correspond to static methods of `JyGateway`. If `JyGateway.newInstance(&#8220;JyClass&#8221;, this, null)` is called the `newInstance` Jython function is fetched from the registry using &#8220;JyClass&#8221; as a key. The third argument of `JyGateway.newInstance` contains an array of Java objects passed as arguments to the `newInstance` function which returns a new PyObject. If the constructor doesn&#8217;t take an argument `null` is passed as in the example. The particular `JyClass` will never be exposed to Java code.<\/p>\n<h4>Overrdiding superclass methods<\/h4>\n<p>Aside from `jyobject` we have also defined `jaobject` in the `JaFromJyClass` constructor which has the type `Object`. Here `Object` is just the superclass of both `JaFromJyClass` and `JyClass`. The `jaobject`is defined for the purpose of overriding superclass methods: we cannot simply change the signature of superclass methods in particular not the return value.<\/p>\n<p>If `public void foo(int x)` is a method defined in the superclass of `JyClass`, the Java method generated from `JyClass` is<\/p>\n<pre lang=\"python\">public void foo(int arg) { jaobject.foo(arg) }<\/pre>\n<p>The method `foo` called from `jaobject`  is still the method implemented in Jython. The Jython method is just called with Java arguments and returns a Java value ( if any ) that gets converted back to Jython.<\/p>\n<h4>Calling static methods<\/h4>\n<p>Calling static or classmethods of Jython objects from Java is similar to calling `JyGateway.newInstance`:<\/p>\n<pre lang=\"python\">public static PyObject bar(PyObject args[])\r\n{\r\n     return JyGateway.callStatic(\"JyClass\", \"bar\", args);\r\n}<\/pre>\n<h3>Defining Jython metadata<\/h3>\n<p>There are three kinds of meta-data which can be added to Jython classes which are extracted for dynamic Java generation. Those are called `jproperty`, `annotation` and `signature`. They serve different purposes.<\/p>\n<h4>signature<\/h4>\n<p>A `signature` decorator is defined to assign Java argument and the return types to a Jython method. Without the signature decorator a default translation is applied:<\/p>\n<pre lang=\"python\">def foo(self, arg):\r\n    ...<\/pre>\n<p>&#8212;&#8211;&gt;<\/p>\n<pre lang=\"python\">public PyObject foo(PyObject[] args) {\r\n    return jyobject.invoke(\"foo\", args);\r\n}<\/pre>\n<p>If we decorate `foo` with the following `signature` decorator<\/p>\n<pre lang=\"python\">@signature(\"public int _(char)\")\r\ndef foo(self, arg):\r\n    ...<\/pre>\n<p>we get the translation<\/p>\n<pre lang=\"python\">public int foo(char arg0){\r\n    PyObject args[] = new PyObject[1];\r\n    for(int i=0;i&lt;1;i++) {\r\n        args[0] = Py.java2py(arg0);\r\n    }\r\n    return (Integer)jyobject.invoke(\"foo\", args).__tojava__(int.class);\r\n}<\/pre>\n<p>The name of the function in the signature declaration string is of no relevance. That&#8217;s why we have used a single underscore.<\/p>\n<h4>annotation<\/h4>\n<p>The `annotation` decorator applies to methods and classes. We have to wait for Jython 2.6 for proper class decorator syntax but the semantics is the same when we write<\/p>\n<pre lang=\"python\">cls = annotation(value)(cls)<\/pre>\n<p>The value passed to `annotation` is a string which must conform Java annotation syntax with the leading `@` character being stripped.<\/p>\n<pre lang=\"python\">@annotation(\"Override\")\r\ndef foo(self, arg):\r\n    ...<\/pre>\n<p>is a valid annotation which corresponds to the Java method<\/p>\n<pre lang=\"python\">@Override\r\npublic PyObject foo(PyObject[] args) {\r\n    return jyobject.invoke(\"foo\", args);\r\n}<\/pre>\n<p>Annotations can be stacked and also combined with the signature decorator. So we can define three new decorators<\/p>\n<pre lang=\"python\">test  = annotation(\"Test\")(signature(\"public void _()\"))\r\nsetUp = annotation(\"Before\")(signature(\"public void _()\"))\r\nbeforeClass = annotation(\"BeforeClass\")(signature(\"public static void _()\"))<\/pre>\n<p>and use them within e.g. JUnit 4<\/p>\n<pre lang=\"python\">from org.junit import*\r\nfrom org.junit.Assert import*\r\n\r\nclass TestClass(Object):\r\n    @beforeClass\r\n    def start(self):\r\n        print \"Run 'TestClass' tests ...\"\r\n\r\n    @test\r\n    def test_epoweripi(self):\r\n        from math import e, pi\r\n        assertTrue( abs( e**(pi*1j) + 1 ) &lt; 10**-10 )<\/pre>\n<h4>jproperty<\/h4>\n<p>The `jproperty` object is a descriptor which assigns a Java type and zero or more annotations to a Jython attribute.<\/p>\n<pre lang=\"python\">class JyClass(Object):\r\n    x = jproperty(\"private int\", \"X\", \"Y\")<\/pre>\n<p>This is how it translates<\/p>\n<pre lang=\"python\">pubic class JyClassBase(Object)\r\n{\r\n    @Y @X\r\n    private int x;\r\n}<\/pre>\n<p>A `JyClass` instance reads ( and sets ) `jproperty` values from the corresponding Java class instances it was assigned to. Remember that the `jyobject` instance construction looked like this<\/p>\n<pre lang=\"python\">jyobject = JyGateway.newInstance(\"JyClass\", this, null);<\/pre>\n<p>With `this` an instance of the Java class was passed to the `newInstance` factory function. Not only holds a `javaobject` a `jyobject` but also a `jyobject` holds a `javaobject`. Reading \/ writing `jproperty` values is the primary reason for this cyclic dependence.<\/p>\n<h3>Class import heuristics<\/h3>\n<p>Whenever a Java class gets compiled, names have to be imported from classes\/packages using the import-statement. Jython applies some heuristics to extract class and package names from annotations\/Jython code and creates Java import statements accordingly. An important precondition for a class\/package to be found is that it has been imported in Jython code. This isn&#8217;t particularly cumbersome. When you define an annotation<\/p>\n<pre lang=\"python\">annotation('SupportedAnnotationTypes(\"*\")')<\/pre>\n<p>the name `SupportedAnnotationTypes` has to be made public using a normal Jython import:<\/p>\n<pre lang=\"python\">from javax.annotation.processing import SupportedAnnotationTypes<\/pre>\n<p>This is not much different from considering an evaluation of the parameter string using Pythons `eval`.<\/p>\n<p>The `annotation` class has a class attribute `namespace` which is used for Jython class and annotation extraction. If the heuristics fails to extract a class the `namespace` can be manually updated:<\/p>\n<pre lang=\"python\">annotation.namespace.update(locals())<\/pre>\n<h3>Jynx<\/h3>\n<p>Within the next few days I&#8217;ll launch a new project on `code.google.com` called <strong>jynx<\/strong> which will contain tools particularly suited for Jython utilization of modern Java frameworks and APIs.  The relevant source code for this article can be found <a href=\"http:\/\/www.fiber-space.de\/misc\/jynx.zip\">here<\/a> and you can examine and play with it.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Jython annotations &#8211; anyone? The last few days I tried to figure out how to create Jython annotations. A Jython annotation is defined here as a Java annotation lifted from Jython to Java. So one essentially defines a Java annotation &hellip; <a href=\"http:\/\/fiber-space.de\/wordpress\/2009\/06\/30\/stitches-of-a-flea-language-defining-java-annotations-in-jython\/\">Continue reading <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[24,25],"tags":[],"_links":{"self":[{"href":"http:\/\/fiber-space.de\/wordpress\/wp-json\/wp\/v2\/posts\/908"}],"collection":[{"href":"http:\/\/fiber-space.de\/wordpress\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/fiber-space.de\/wordpress\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/fiber-space.de\/wordpress\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/fiber-space.de\/wordpress\/wp-json\/wp\/v2\/comments?post=908"}],"version-history":[{"count":41,"href":"http:\/\/fiber-space.de\/wordpress\/wp-json\/wp\/v2\/posts\/908\/revisions"}],"predecessor-version":[{"id":965,"href":"http:\/\/fiber-space.de\/wordpress\/wp-json\/wp\/v2\/posts\/908\/revisions\/965"}],"wp:attachment":[{"href":"http:\/\/fiber-space.de\/wordpress\/wp-json\/wp\/v2\/media?parent=908"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/fiber-space.de\/wordpress\/wp-json\/wp\/v2\/categories?post=908"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/fiber-space.de\/wordpress\/wp-json\/wp\/v2\/tags?post=908"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}