Search code examples
javaswingjython

Access protected Java attribute from Jython


I am building a text editor with Jython using Java Swing. I have come across CompoundEdit, a Swing class that has a list of edit actions from a text editor. This attribute is protected, which means that I can't access it directly from another class, but I can from other classes that extend it. So, if I create a MyEdit class that extends CompoundEdit, MyEdit should have access to the list of edits.

This is what I'm trying:

class MyEdit(CompoundEdit):
    def __init__(self):
        super(CompoundEdit, self).__init__()
        print(dir(self)) # Doesn't show the edits
        self.nammu_edits = super(CompoundEdit, self).edits 

Running this gives me this error:

AttributeError: 'super' object has no attribute 'edits'

For reference, this is what dir comes back with:

['__class__', '__copy__', '__deepcopy__', '__delattr__', '__dict__', '__doc__', '__ensure_finalizer__', '__eq__', '__format__', '__getattribute__', '__hash__', '__initProxy__', '__init__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__str__', '__subclasshook__', '__supernames__', '__unicode__', '__weakref__', '_getPyInstance', '_getPySystemState', '_setPyInstance', '_setPySystemState', 'addEdit', 'canRedo', 'canUndo', 'class', 'classDictInit', 'clone', 'die', 'doPrint', 'end', 'equals', 'finalize', 'getClass', 'getPresentationName', 'getRedoPresentationName', 'getUndoPresentationName', 'hashCode', 'inProgress', 'isInProgress', 'isSignificant', 'lastEdit', 'notify', 'notifyAll', 'presentationName', 'redo', 'redoPresentationName', 'replaceEdit', 'significant', 'toString', 'undo', 'undoPresentationName', 'wait']

And this is an extract of the CompoundEdit.java code:

public class CompoundEdit extends AbstractUndoableEdit {
    /**
     * True if this edit has never received <code>end.
     */
    boolean inProgress;

    /**
     * The collection of <code>UndoableEdits
     * undone/redone en masse by this <code>CompoundEdit.
     */
    protected Vector<UndoableEdit> edits;

    public CompoundEdit() {
        super();
        inProgress = true;
        edits = new Vector<UndoableEdit>();
    }

I have tried the exact same from Java, and it lets me access edits. Is there something I'm doing wrong in the Jython version? Is there a special way in Jython to access protected variables? In the documentation it mentions something about calling super__<method>(), but I tried it for this case and it also doesn't work.


Solution

  • @mzjn is correct. Setting python.security.respectJavaAccessibility = false is the only way to access a protected field from a subclass. This is due to this piece of code in org.python.core.PyJavaType.init(Class<?>, Set<PyJavaType>):

    // Add fields declared on this type
    Field[] fields;
    if (Options.respectJavaAccessibility) {
        // returns just the public fields
        fields = forClass.getFields();
    } else {
        fields = forClass.getDeclaredFields();
        for (Field field : fields) {
            field.setAccessible(true);
        }
    }
    

    However, you can call protected methods without setting respectJavaAccessibility to false, as method lookup uses a different algorithm. IMO this is a bug, I was not able to find any mentions that this behavior is intended.

    Or just use Java reflection to get the protected field value:

    class MyEdit(CompoundEdit):
        #...
        def get_edits(self):
            edits_field = CompoundEdit.getDeclaredField('edits')
            edits_field.setAccessible(True)
            return edits_field.get(self)