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

Jpython #1176

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open

Jpython #1176

wants to merge 8 commits into from

Conversation

Thrameos
Copy link
Contributor

@Thrameos Thrameos commented Mar 2, 2024

This is a PR which is intended to create a jpython executable that handles all the control over lifespan of Python and Java.

Highlights:

  • arguments of type -X and -D are passed to the JVM currently up until -- so we should be able to direct which VM is getting the options.
  • Java imports are active at the start so no need to import them separately.
  • Python main and Java main threads are isolated. Java main waits in destroy state until Python completes then attempts orderly freeing of resources.
  • We can create threads for OSX apphandler so we should be able to avoid gui lock.

Problems:

  • I have been unable to catch all of the different ways that Python exits and thus some like "quit()" are currently bypassing proper such down routines.
  • We need to convince py installer to place the exe in the bin directory which is not intuitive.
  • Py_RunMain is finalizing the Python VM rather than returning control so that we can shut it down safely at a defined point.
  • atexit routine are running from Python at the end of Py_RunMain end rather than when the JVM is being destroyed.
  • There was a weird stack corruption during development which was forcing the order of items a lot. I have no idea the source. It seems that the JPype initialization must happen on the main thread, but there is no good reason for this.

I am going to need assistance from @pelson on the install issues and other for the AppHandler logic. I need assistance from Python developers on how the operate Py_RunMain without it bypassing the proper shutdown order and finalizing before the application completes.

from ._core import getDefaultJVMPath
import os.path

_JPypeJarPath = "file://" + os.path.join(os.path.dirname(os.path.dirname(__file__)), "org.jpype.jar")

Check notice

Code scanning / CodeQL

Unused global variable Note

The global variable '_JPypeJarPath' is not used.
import os.path

_JPypeJarPath = "file://" + os.path.join(os.path.dirname(os.path.dirname(__file__)), "org.jpype.jar")
_JPypeJVMPath = getDefaultJVMPath()

Check notice

Code scanning / CodeQL

Unused global variable Note

The global variable '_JPypeJVMPath' is not used.
}
if (argv[i][1] == '-')
{
i++;

Check notice

Code scanning / CodeQL

For loop variable changed in body Note

Loop counters should not be modified in the body of the
loop
.
}
if (argv[i][1] == '-')
{
i++;

Check notice

Code scanning / CodeQL

For loop variable changed in body Note

Loop counters should not be modified in the body of the
loop
.
Copy link
Contributor

@pelson pelson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Basic question first: Is this the same as epypj? Is it intended that one should be able to start the Python interpreter from within Java?

From a strategic perspective, I personally have always disliked custom executables for things that embed Python or Java (I think of the spark executable) - whilst it is convenient for people to avoid manually doing setup correctly, it introduces another layer of complexity and indirection to use with other tools (e.g. can you use spark with jpython...). It is for this reason I ask about starting Python from within Java directly - it is far more appealing to me that we simply use python and java executables directly.

From an implementation perspective, there are clearly still quite a number of things to do (tidying up the implementation [lots of prints still lying around], introducing a test suite, writing the docs, getting the build environment working, etc., etc.). Happy to help with whatever I can on the setupext - it isn't clear what the "installation issues" are just yet though.

@@ -216,6 +214,7 @@ void JPContext::initializeResources(JNIEnv* env, bool interrupt)

if (!m_Embedded)
{
printf("not embedded\n"); fflush(stdout);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👀

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR is still a work in progress. So still a lot of rough edges. I have yet to come up with a logical reason that jpype needs to be started on main thread rather than on the spawned python thread. And i need to resolve how to prevent the user from calling shutdown.

That is unfortunately one of the key architectural problems with jpype. Shutdown should never have been exposed in the first place. The entry point should have spawned a thread, the original thread should have attached, then the launch thread should call destroy jvm. Modules in Python don't generally unload themselves or have time bombs to render themselves unusable, but that is what shutdown does.

Unfortunately like the calling classpath from startJVM as "-Dclasspath" which should never be done(it prevents our jar from being loaded requiring sideloading with a URLClassLoader), the shutdownJVM is in all the old docs and thus ilI will forever be forced to deal with.


// Set up the GC
PyJPModule_installGC(privateModule.get());
PyObject_SetAttrString(builtins.get(), "jpype", publicModule.get());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The objective is to add jpype to the built-ins? 🤔

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. Like jython the goal was to have enough symbols available. Though i agree it is completely optional and i was just testing if ther was a pattern for adding to builtins.

@@ -27,7 +27,20 @@ class Develop(develop_cmd):
('enable-coverage', None, 'Instrument c++ code for code coverage measuring'),
]

#<<<<<<< HEAD:setupext/dist.py
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

merge conflict here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it. I guess i missed testing that one. There were a lot of conflicts when the setup got changed.

@Thrameos
Copy link
Contributor Author

Thrameos commented Mar 4, 2024

No this is not the same as epypj. With epypj the goal was to build a java library in which python was accessable as java code. That would mean you could use matplotlib directly from java or spawn a python subshell. It required that we call asm to convert python libraries into subs that java a compiled language could link against. The reason it couldn't move forward was to test the thousands of potential entry points from java calling python required some serious user community help as my laboratory would not fund it, and declined to authorize me to work on it personally by declining to sign the python contributor agreement.

Jpython is simpler. It is simply a binary in which the order of python and java startup is defined. Both virtual machines start at the same time, have the same lifetime and hopefully end at the same time. The direction of the bridge is the same from python to java. It can do some nicer things like get apple gui thread going.

The point of this is to remove the need for JForward and other ackwardness in which one machine is running and the other is not. I could do the same with a jpython module style like say "pip" but the entry point if java without a real executable requires starting python then calling spawn which starts a second copy of python with the jvm going.

Epype could do this same task by simply having a simple main in the jar calling java and launching the python main as one statement. But without epypj this is the best I can do. Java cant call python currently in a meaningful way, and fighting the conflicts in packaging systems is posing serious problems. We are losing users because osx released incompatible java and python versions. Which given i can't access such a messed up machine (mixed architectures and mixed security flags) is a serious drag to this project as users think it is the duty of jpype to somehow fix a major companies deployment problem.

@Thrameos
Copy link
Contributor Author

Thrameos commented Mar 4, 2024

The main installation issue is placing the binary in the python install location (ie like executable python modules) such that it is treated no different than others. IE follows a virtual env install etc.

At some point disttools in python was actually intended to support binaries. I found the linker stubs with EXECUTABLE flags meaning it was a planned use case. But the flag is almost inaccessable and the new system is getting farther and farther from allowing general purpose code. Thus it it is not at all clear how to tell the manifest that one piece belongs in the bin directory.

@Thrameos
Copy link
Contributor Author

Thrameos commented Mar 4, 2024

The other fundemental problem we have is packaging in general, though not the subject of this PR. The only system for getting python and java together is conda. And it isn't clear that is working on OSX. It should be possible for any java and python to work with jpype. (And if python had just a little more support we could build a python version agnostic support library, but that is hung up on me writting a pep). But that is failing in practice.

As for the goals of this PR the idea is to get rid of the need to start the JVM or control its lifespan. Many users requested it in the past. We have the current and on going problem that starting the JVM is a one shot thing, and as modules using Java as a backend all start the JVM they are all fundimentally incompatible. Though as I only use JPype with code I have written (being mainly a Java user) I can only take shots in the dark as to how to solve it.

@pelson
Copy link
Contributor

pelson commented Mar 15, 2024

Thanks for all the details. From my perspective, as I already hinted at in my last comment, I'm quite strongly against the idea of a new JPype Python executable (jpython) the main reason being that it changes JPype from being a Python package which integrates into the standard Python environment, to being a semi-Python implementation.

To clarify that (strong) view a bit, imagine we wanted to use a Jupyter notebook, and use some JPype stuff... do I need to make a JPype jpython kernel, such that it guarantees that when the interpreter starts-up, the JVM also is started? Another example (already given) - what about combining this with other embedded Python interpreters (e.g. spark, gdb/pdb, blender, qt designer, etc).

One major concern from my perspective is that the normal JPype workflow would become a second citizen to jpython - you've already given an example where we would potentially no longer look for a solution to JForward because in jpython it "just works" ™️ (because the JVM is already running).


From a technical perspective, I don't know how I would go about packaging such an executable. Perhaps I would make my life simple and simply ship a bash script which launches the jpython executable found in the package's directory (in site-packages).

@Thrameos
Copy link
Contributor Author

Thrameos commented Mar 15, 2024 via email

@pelson pelson mentioned this pull request Mar 18, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants