-
Notifications
You must be signed in to change notification settings - Fork 181
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
Extension #890
base: master
Are you sure you want to change the base?
Extension #890
Conversation
This pull request introduces 23 alerts when merging 7fbc10b into 7256261 - view on LGTM.com new alerts:
|
Making progress, but still deep technical issues.
It is still not clear if we can invokespecial from JNI. |
This pull request introduces 20 alerts when merging 472b300 into 7256261 - view on LGTM.com new alerts:
|
This pull request introduces 20 alerts when merging 3855389 into 39ae320 - view on LGTM.com new alerts:
|
I added documentation on the initial guess of where I think the Java extension system will be headed. Any input on the direction would be appreciate. |
This pull request introduces 20 alerts when merging c3facdd into 39ae320 - view on LGTM.com new alerts:
|
This lists reads like we need a complete re-implementation of essential Java features. Ain't that a bit of an overkill for this feature? What do you think will be the expected performance, when cross-calling between the languages during handling extended classes? |
Extending a java class remains a requested feature. Some APIs in Java require a class to be extended. We can achieve this by writing a custom Java class and a stub interface that then has to loaded with the user code. But that is a real pain. This is the alternative where the user declares a Java extension in Python which is "compiled" at runtime. The performance of the compile is not great but you should not do it often. The actual call cost is the same as the existing Java JProxy code. Everything must be boxed. Each gets a Java handle for Python to use. Then Python gets called. The return has to be boxed again. Then we return to Java space. So lets assume 5 arguments with 2 primitives. That is 2 box objects then 5 python objects, the one java object on the return. We also have the tuple for the call transfer. So cost is 9 object creation. Compare this to one python method call. One object for the bind. One for the tuple. Several more if keyword args. So a Java to Python call is a slightly more heavy than a Python call and much more than a native Java call. |
Setting up the privilege flag is challenging. I need to be able to check the flag whenever a private method is called and verify the flag, but there is no space in the object currently for it (because there memory layout.) Options are:
|
I did some work on this over the weekend. I got as far as successfully extending a Java class, constructing this Python class from Python, subclassing that Python class, and then constructing that class. I didn't expect subclassing the Python class to work, but it did and worked just like the original.
Still need to do the privilege stuff but honestly, that can be done at the end. The way I see it is if it's not possible to prevent it, then oh well.. Need to be able to add annotations to the class. This isn't difficult, there is just no pythonic way of doing it yet. Maybe in the future PEP 638 can help with that. My use case would actually requires being able to add annotations to the class. I don't care if it's gross as long as it works. I've done it in the past when I hacked around jpype limitations from an external python module, i just haven't implemented it here yet.
I'm not sure what this is needed for. I had to reformat some of the Java files because I couldn't stand it anymore. I also took care of the 100+ warnings about unparameterized generics, resource leaks, etc. Oh and I made it so you can add python attributes to the class that aren't visible to Java. Which can be important considering it "is a Python class". |
@Thrameos how did you intend for declaring fields to work? I'm was trying to implement test cases for adding and accessing fields in an extended class but the intent isn't clear. You can't put a decorator on a class variable. It could be done by decorating a property or maybe by an annotation by having |
I believe it should work no different than the proxy. You create the stub class which forwards to the interface and an interface to implement. You then Create a proxy. The tricky part is connecting the proxy class. The best policy would be to rename the overriden methods so that user called points go back to Java which route back to Python. The Java declared fields would appear on the "super", the Python declared ones is more challenging. You don't declare fields in python, they just appear in the constructor. That means that we need to make the Python wrapper open to adding new fields. We could make a "this" in addition to the "super" which is specifically for holding the fields if it would keep the class closed. |
I did some testing with the annotation and noticed a useable pattern right when I was about to give up. You can do the following; def testPublicField(self):
class MyObject(JClass("jpype.extension.TestBase")):
myField: JPublic[JInt]
def __init__(self):
self.pythonMember = None
@JPublic
def __init__(self):
# java exposed constructor, called before python __init__
...
o = MyObject()
o.myField When At the moment I have all this functional, with some minor things broken elsewhere that I have to go back and fix. Edit: Ignore the "FIXME" in |
I've gotten quite a bit done on this. I think all I have left to implement is make some way to apply annotations, even if it is crufty (my use case requires it at least on the class). After that it's just document everything. I'm currently flying blind with respect to test coverage and leaks since I expect to have something presentable in a few more weekends. Once it is there, how would you prefer it to be submitted? Should I open a pull request to Thrameos's fork or should I open a new pull request? Oh, I also wanted to mention that I switched to testing on 3.13rc3 this morning so I'm pretty confident things are working well for 3.13, at least in normal GIL mode. I don't expect the experimental free-threaded mode to work since heap types aren't shared and the global type pointers in jpype will probably throw a wrench into that. The experimental free-threaded mode is disabled by default and you have to explicitly start that version of python and build your wheels with that version. This means it is unlikely many people will want it, so there is probably no need to care until someone complains about it. |
Either way works for me. I ma make some changes to optimize pathways but lets wait until it is submitted before making any decisions. As I understating it you are adding JPublic and JProtected to the API and the rest of the mechanics is on the class new. Is that correct? |
Yes |
I would like to know what the deal is with marking entire *.cpp files as |
Mostly historical. The entire python module was grown out of a single Python hello-world application which unfortunately had extern "C" declared. Sections were moved out as needed. As far as I am aware the fast majority of those calls are directly connected to Python slots so they really need to be extern "C". We can of course mark individual functions rather than blanket, but that will take some effort. |
This is the situation I was mostly concerned about and it appears to be moot. I expected this to terminate but it doesn't. https://gcc.godbolt.org/z/fxPKzxhhK I'll leave it alone but I won't pretend like it doesn't bother me 😂. |
I retract this statement. It seems I had this mixed up with PEP 684. I've enabled it the free threaded build and it seems to work fine. I'll put in a pr shortly. |
This is a work in progress PR for extending classes within Python.