diff --git a/docs/user/Python-on-JVM.md b/docs/user/Python-on-JVM.md index 5aa929c537..0814144482 100644 --- a/docs/user/Python-on-JVM.md +++ b/docs/user/Python-on-JVM.md @@ -333,6 +333,10 @@ assert issubclass(PythonLevel, Level) assert PythonLevel.parse("INFO").getName() == "INFO" ``` +Two important caveats: + + 1. You cannot use other metaclasses, so inheriting from `ABC` and a Java class is not supported. + 2. If you want to implement `__init__` to accept additional arguments, you must also override `__new__` to avoid passing the additional arguments to the Java constructor, which would not know how to handle them. ## Embedding Python into Java diff --git a/graalpython/com.oracle.graal.python.test/src/tests/test_interop.py b/graalpython/com.oracle.graal.python.test/src/tests/test_interop.py index 8e586345f3..ab01a4c18d 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/test_interop.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/test_interop.py @@ -675,7 +675,7 @@ class MyHandler (Handler): counter = 0; def isLoggable(self, logrecord): self.counter = self.counter + 1 - return self.__super__.isLoggable(logrecord) + return super().isLoggable(logrecord) def sayHello(self): return 'Hello' @@ -726,9 +726,9 @@ def test_extend_java_class_03(self): class MyLogRecord(LogRecord): def getLevel(self): - if self.__super__.getLevel() == Level.FINEST: - self.__super__.setLevel(Level.WARNING) - return self.__super__.getLevel() + if super().getLevel() == Level.FINEST: + super().setLevel(Level.WARNING) + return super().getLevel() message = "log message" my_lr1 = MyLogRecord(Level.WARNING, message) @@ -1503,6 +1503,18 @@ def getName(self): assert pl.callStaticFromPython("INFO").getName() == "INFO" assert PythonLevel2.parse("INFO").getName() == "INFO" + def test_java_subclassing_with_new_arguments(self): + from java.util.logging import Level + + class PythonLevel(Level, new_style=True): + def __new__(cls, misc_value): + return super().__new__(cls, "default name", 2) + + def __init__(self, misc_value): + self.misc_value = misc_value + + pl = PythonLevel(123) + assert pl.misc_value == 123 def test_jython_star_import(self): if __graalpython__.jython_emulation_enabled: diff --git a/graalpython/lib-graalpython/__graalpython__.py b/graalpython/lib-graalpython/__graalpython__.py index 937f2147ac..f10f2dda5e 100644 --- a/graalpython/lib-graalpython/__graalpython__.py +++ b/graalpython/lib-graalpython/__graalpython__.py @@ -1,4 +1,4 @@ -# Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # The Universal Permissive License (UPL), Version 1.0 @@ -98,7 +98,7 @@ def import_current_as_named_module_with_delegate(module, module_name, delegate_n @builtin -def build_java_class(module, ns, name, base, new_style=False): +def build_java_class(module, ns, name, base, new_style=True): if new_style: return build_new_style_java_class(ns, name, base) import warnings @@ -174,6 +174,11 @@ def __instancecheck__(cls, obj): def __subclasscheck__(cls, derived): return cls is derived or issubclass(derived, JavaClass) + def __call__(cls, *args, **kwds): + java_object = cls.__new__(cls, *args, **kwds) + java_object.this.__init__(*args, **kwds) + return java_object + def __new__(mcls, name, bases, namespace): if bases: new_class = None @@ -232,7 +237,6 @@ def __new__(cls, *args, **kwds): delegate = object.__new__(cls) java_object = polyglot.__new__(JavaClass, *(args + (delegate,))) delegate.__this__ = java_object - delegate.__init__(*args, **kwds) return java_object return type(name, (DelegateSuperclass,), ns)