ninia / jep

Embed Python in Java
Other
1.32k stars 149 forks source link

matplotlib.pyplot.plot() not working with Jep (python 3.7.3) #187

Open pizzonia opened 5 years ago

pizzonia commented 5 years ago

The following code shows the issue calling matplotlib.pyplot.plot()

import jep.*;
public class JepIssue {
    public static void main(String[] args) throws JepException {
        // Jep Shared library should be linkable 
        Jep jep = new SharedInterpreter();
        jep.eval("from java.lang import System" );
        jep.eval("import sys" );
        jep.eval("System.out.println('I am jep! sys.argv: '+str(sys.argv) )" );
        jep.eval("import matplotlib.pyplot as plt");
        jep.eval("plt.plot([1,2,3], [4,5,6])");
        jep.eval("plt.show()");
    }
}

It gives the following output/error

I am jep! argv: []
Exception in thread "main" jep.JepException: <class 'IndexError'>: list index out of range
    at /home/pizzonia/.pyenv/versions/3.7.3/lib/python3.7/tkinter/__init__.__init__(__init__.py:2018)
    at /home/pizzonia/.pyenv/versions/3.7.3/lib/python3.7/site-packages/matplotlib/backends/_backend_tk.new_figure_manager_given_figure(_backend_tk.py:1008)
    at /home/pizzonia/.pyenv/versions/3.7.3/lib/python3.7/site-packages/matplotlib/backend_bases.new_figure_manager(backend_bases.py:3218)
    at /home/pizzonia/.pyenv/versions/3.7.3/lib/python3.7/site-packages/matplotlib/pyplot.figure(pyplot.py:525)
    at /home/pizzonia/.pyenv/versions/3.7.3/lib/python3.7/site-packages/matplotlib/pyplot.gcf(pyplot.py:578)
    at /home/pizzonia/.pyenv/versions/3.7.3/lib/python3.7/site-packages/matplotlib/pyplot.gca(pyplot.py:935)
    at /home/pizzonia/.pyenv/versions/3.7.3/lib/python3.7/site-packages/matplotlib/pyplot.plot(pyplot.py:2809)
    at <string>.<module>(<string>:1)
    at jep.Jep.eval(Native Method)
    at jep.Jep.eval(Jep.java:507)
    at JepIssue.main(JepIssue.java:10)

The problem seems to be related with the fact that sys.argv is an empty list when python is called as embedded interpreter by Jep. In fact, in tkinter/init.py:2018 we find baseName = os.path.basename(sys.argv[0]) which assumes len(sys.argv)>0. Is this a Jep or a tkinter issue? Python doc about sys.argv says

If no script name was passed to the Python interpreter, argv[0] is the empty string.

which implicitly states that argv[0] should exists. Hence, I suppose this should be considered a Jep bug.

Workaround

After creating the interpreter, just executes the following.

        jep.eval("import sys" );
        jep.eval("sys.argv.append('')");

Details

ndjensen commented 5 years ago

Thank you for the link. Given the Python doc you linked to states it should be the empty string, I am inclined to agree. However, I am reluctant to change it as I don't know what side effects this would have on existing projects using Jep and CPython extensions.

ndjensen commented 5 years ago

There was a lot of discussion on #81 about sys.argv. We may want to revisit that for a 4.0 that breaks backwards API.

bsteffensmeier commented 5 years ago

In the past the best solution seems to be to change the backend for matplotlib, see #115.

Another option is to use MainInterpreter.setSharedModulesArgv()

I'm not completely convinced we should change, I try to stick to being a wrapper around python embedded interpreters, and according to the python documentation they leave responsibility for argv to developers using the API, we just follow their example and leave it to developers using jep. Having said all that I don't see any actual harm in setting it to empty string by default.

Klodovsky commented 3 years ago

Hi guys, I've been facing 2 similar issues with jep :

The first is that python modules are not being recognized :

Exception in thread "main" jep.JepException: <class 'ModuleNotFoundError'>: No module named 'pandas' at .(:1) at jep.Jep.eval(Native Method) at jep.Jep.eval(Jep.java:451) at jep_package.JepIssue.main(JepIssue.java:27)

code :
public static void main(String[] args) throws JepException { // Jep Shared library should be linkable Jep jep = new SharedInterpreter(); jep.eval("from java.lang import System" ); jep.eval("import sys" ); jep.eval("System.out.println('Hello from Java ')" ); jep.eval("print('hello from Python')");

    jep.eval("pays = {\"pays\": [\"France\", \"Tunisie\"]}");
    jep.eval("import pandas as pd");
    jep.eval("tab = pd.DataFrame(pays)");
    jep.eval("print(tab)");

The second is even simple methods like jep.eval("print('hello from Python')") doesn't show anything in console (no errors), where the same jep.eval("System.out.println('Hello from Java ')" ) prints the message correctly, what I'm I missing ? my guess it's something related to PYTHONHOME maybe ?