Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Boolean conversions between Java and Python #602

Open
lg8080 opened this issue Aug 10, 2021 · 8 comments
Open

Boolean conversions between Java and Python #602

lg8080 opened this issue Aug 10, 2021 · 8 comments
Labels

Comments

@lg8080
Copy link

lg8080 commented Aug 10, 2021

When trying to use booleans with Pyjnius, they get converted to integers in some situations, which causes errors.

>>> import jnius
>>> jnius.__version__
'1.3.0'
>>> Boolean = jnius.autoclass('java.lang.Boolean')
>>> Boolean("true")  # this is consistent with java.lang.Integer behavior
<java.lang.Boolean at 0x11b53d5c8 jclass=java/lang/Boolean jself=<LocalRef obj=0x7fd240748ff0 at 0x10e942ef0>>
>>> Boolean.TRUE  # should return True (to be consistent with java.lang.Integer.MAX_VALUE
1
>>> Boolean("true").compareTo(True)  # should return 0
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "jnius/jnius_export_class.pxi", line 1145, in jnius.JavaMultipleMethod.__call__
  File "jnius/jnius_export_class.pxi", line 857, in jnius.JavaMethod.__call__
  File "jnius/jnius_export_class.pxi", line 954, in jnius.JavaMethod.call_method
  File "jnius/jnius_jvm_dlopen.pxi", line 91, in jnius.create_jnienv
jnius.JavaException: JVM exception occurred: java.lang.Integer cannot be cast to java.lang.Boolean java.lang.ClassCastException
>>> Stack = jnius.autoclass('java.util.Stack')
>>> s = Stack()
>>> s.isEmpty()
True
>>> s.push(4)
4
>>> s.push(True)  # should return True
1
>>> s.push(Boolean("true"))  # should return True
1

The following test shows a change in behaviour between the current master branch and pyjnius 1.3.0:

  1. Create a Java class with a function that accepts {{Boolean}} as argument:

     class Test {
       public static void func(Boolean x) {
         System.out.println(x);
       }
     }
    
  2. Call it from Python using pyjnius with a Python boolean:

     python -c "from jnius import autoclass; test = autoclass('Test'); test.func(True)"
    

This will run on Pyjnius 1.3.0 (and will print out 1 instead of true), but on the current master branch of pyjnius this will fail with TypeError: Invalid instance of 'java/lang/Integer' passed for a 'java/lang/Boolean'

If you use boolean instead of Boolean, it will work correctly on both versions and will print out true.

@tshirtman tshirtman added the bug label Oct 31, 2021
jmuhlich added a commit to jmuhlich/ashlar that referenced this issue Apr 26, 2022
Bools are now auto-converted to Integers instead of Booleans, so we have to
perform the conversion explicitly now. Tracked upstream in kivy/pyjnius#602 .
jmuhlich added a commit to labsyspharm/ashlar that referenced this issue Apr 26, 2022
Bools are now auto-converted to Integers instead of Booleans, so we have to
perform the conversion explicitly now. Tracked upstream in kivy/pyjnius#602 .
@mmj579
Copy link

mmj579 commented Jul 25, 2023

Confirmed: There is no way to specify instances of Booleans in the current release. Booleans get converted to Integer(1).
Unfortunately my Java code expects instances of Boolean, not of Integer.
This bug ate up 3 hours of my time just to identify it :-( Didn't expect anything like it.
And I don't know how I can work around it. The only way seems to be to reimplement my python logic in Java :-(

@cmacdonald
Copy link
Contributor

Cant you pass python True and Java autoboxing will deal with it?

@mmj579
Copy link

mmj579 commented Jul 25, 2023

Unfortunately: No.

I did something like this:

myList = jnius.autoclass("java.util.ArrayList")
myList.add(jnius.autoclass("java.lang.Boolean").TRUE)

(BTW: jnius.autoclass("java.lang.Boolean").TRUE should return an instance of Boolean.)

The contents of that list seems to be an Integer(1). Somehow for some reason (= details are still unclear to me) instances of Boolean get converted to Integer. My Java code (correctly) complains that it receives instances of Integer instead of Boolean.

I took this quite uncommon approach of using Boolean.TRUE because of passing Python True and relying on autoboxing did lead to the unexpected result of receiving an Integer, not a Boolean.

@mmj579
Copy link

mmj579 commented Jul 25, 2023

Ah, that's interesting. If my Java code returns Boolean.TRUE as a return value of a method I receive an integer 1 in Python.

@mmj579
Copy link

mmj579 commented Jul 25, 2023

In Python 1 as equivalent to true. So it's quite natural that most code works quite well though we've a bug here.

Please note:

>>> type(True)
<class 'bool'>
>>> type(1)
<class 'int'>

And:

>>> isinstance(1, bool)
False
>>> isinstance(1, int)
True

But:

>>> isinstance(True, bool)
True
>>> isinstance(True, int)
True

@cmacdonald
Copy link
Contributor

cmacdonald commented Jul 25, 2023 via email

@lg8080
Copy link
Author

lg8080 commented Jul 25, 2023

@mmj579 As a workaround while this is not fixed yet, you can try this:

myList = jnius.autoclass("java.util.ArrayList")()
myList.add(jnius.autoclass("java.lang.Boolean")("true"))

@mmj579
Copy link

mmj579 commented Jul 25, 2023

I moved essential logic from Python to Java so I can work around that bug for now.
The only remaining problems are values returned as integers instead of booleans. That's not ideal but it's okay. I can live with that.

Thank you for your comprehensive support! In my experience, it's pretty unusual to get feedback this quickly, especially on open source projects. I really appreciate that!

And: Pyjnius is a fantastic Python module! Very good work!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

4 participants