Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ language runtime. The main focus is on user-observable behavior of the engine.
* Add a new, more natural style of subclassing Java classes from Python by passing the `new_style=True` keyword. Multiple levels of inheritance are supported, and `super()` calls both in the constructor override via `__new__` as well as in Java method overrides work as expected.
* Add `polyglot.gil_locked_during_interop` context manager. By default, the global interpreter lock (GIL) is unlocked when interacting with objects from another language, to give other Python threads a chance to run in parallel. While this avoids potential deadlocks, repeated unlocking and locking of the GIL can decrease performance, so this context manager can be used to keep the lock held around short-running interop.
* Add Github workflows that run our gates from the same job definitions as our internal CI. This will make it easier for contributors opening PRs on Github to ensure code contributions pass the same tests that we are running internally.
* Added support for specifying generics on foreign classes, and inheriting from such classes. Especially when using Java classes that support generics, this allows expressing the generic types in Python type annotations as well.

## Version 25.0.1
* Allow users to keep going on unsupported JDK/OS/ARCH combinations at their own risk by opting out of early failure using `-Dtruffle.UseFallbackRuntime=true`, `-Dpolyglot.engine.userResourceCache=/set/to/a/writeable/dir`, `-Dpolyglot.engine.allowUnsupportedPlatform=true`, and `-Dpolyglot.python.UnsupportedPlatformEmulates=[linux|macos|windows]` and `-Dorg.graalvm.python.resources.exclude=native.files`.
Expand Down
16 changes: 16 additions & 0 deletions graalpython/com.oracle.graal.python.test/src/tests/test_interop.py
Original file line number Diff line number Diff line change
Expand Up @@ -587,6 +587,22 @@ def test_java_typing_support(self):
assert List[Integer].__origin__ == List
assert List[Integer].__args__ == (Integer,)

def test_java_generics_subclassing(self):
from typing import Generic, TypeVar
from java.util.function import Function

class MyFunction(Function[str, str], new_style=False):
def apply(self, val : str) -> str:
return val

assert MyFunction().apply(23) == 23

class MyFunction(Function[str, str], new_style=True):
def apply(self, val : str) -> str:
return val

assert MyFunction().apply(23) == 23

def test_java_null_is_none(self):
import java.lang.Integer as Integer
x = Integer.getInteger("something_that_does_not_exists")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2332,19 +2332,20 @@ protected ArgumentClinicProvider getArgumentClinic() {
@GenerateInline(false) // footprint reduction 72 -> 53
abstract static class UpdateBasesNode extends Node {

abstract PTuple execute(PTuple bases, Object[] arguments, int nargs);
abstract Object[] execute(Object[] bases);

@Specialization
static PTuple update(PTuple bases, Object[] arguments, int nargs,
static Object[] update(Object[] bases,
@Bind Node inliningTarget,
@Bind PythonLanguage language,
@Cached PyObjectLookupAttr getMroEntries,
@Cached CallUnaryMethodNode callMroEntries,
@Cached PRaiseNode raiseNode) {
CompilerAsserts.neverPartOfCompilation();
PTuple originalBases = null;
ArrayList<Object> newBases = null;
for (int i = 0; i < nargs; i++) {
Object base = arguments[i];
for (int i = 0; i < bases.length; i++) {
Object base = bases[i];
if (IsTypeNode.executeUncached(base)) {
if (newBases != null) {
// If we already have made a replacement, then we append every normal base,
Expand All @@ -2361,7 +2362,10 @@ static PTuple update(PTuple bases, Object[] arguments, int nargs,
}
continue;
}
Object newBase = callMroEntries.executeObject(null, meth, bases);
if (originalBases == null) {
originalBases = PFactory.createTuple(language, bases);
}
Object newBase = callMroEntries.executeObject(null, meth, originalBases);
if (!PGuards.isPTuple(newBase)) {
throw raiseNode.raise(inliningTarget, PythonErrorType.TypeError, ErrorMessages.MRO_ENTRIES_MUST_RETURN_TUPLE);
}
Expand All @@ -2371,7 +2375,7 @@ static PTuple update(PTuple bases, Object[] arguments, int nargs,
// previously encountered bases.
newBases = new ArrayList<>();
for (int j = 0; j < i; j++) {
newBases.add(arguments[j]);
newBases.add(bases[j]);
}
}
SequenceStorage storage = newBaseTuple.getSequenceStorage();
Expand All @@ -2382,18 +2386,18 @@ static PTuple update(PTuple bases, Object[] arguments, int nargs,
if (newBases == null) {
return bases;
}
return PFactory.createTuple(language, newBases.toArray());
return newBases.toArray();
}
}

@GenerateInline(false) // footprint reduction 36 -> 19
abstract static class CalculateMetaclassNode extends Node {

abstract Object execute(Object metatype, PTuple bases);
abstract Object execute(Object metatype, Object[] bases);

/* Determine the most derived metatype. */
@Specialization
static Object calculate(Object metatype, PTuple bases,
static Object calculate(Object metatype, Object[] bases,
@Bind Node inliningTarget,
@Cached GetClassNode getClass,
@Cached IsSubtypeNode isSubType,
Expand All @@ -2405,12 +2409,8 @@ static Object calculate(Object metatype, PTuple bases,
* while we're at it. Note that if some other metatype wins to contract, it's possible
* that its instances are not types.
*/

SequenceStorage storage = bases.getSequenceStorage();
int nbases = storage.length();
Object winner = metatype;
for (int i = 0; i < nbases; i++) {
Object tmp = SequenceStorageNodes.GetItemScalarNode.executeUncached(storage, i);
for (Object tmp : bases) {
Object tmpType = getClass.execute(inliningTarget, tmp);
if (isSubType.execute(winner, tmpType)) {
// nothing to do
Expand Down Expand Up @@ -2439,17 +2439,6 @@ private static Object buildJavaClass(Object namespace, TruffleString name, Objec
return CallNode.executeUncached(buildFunction, new Object[]{namespace, name, base}, keywords);
}

@InliningCutoff
private static Object buildJavaClass(VirtualFrame frame, Node inliningTarget, PythonLanguage language, PFunction function, Object[] arguments,
PKeyword[] keywords, CallDispatchers.FunctionCachedInvokeNode invokeBody,
TruffleString name) {
PDict ns = PFactory.createDict(language, new DynamicObjectStorage(language));
Object[] args = PArguments.create(0);
PArguments.setSpecialArgument(args, ns);
invokeBody.execute(frame, inliningTarget, function, args);
return buildJavaClass(ns, name, arguments[1], keywords);
}

@Specialization
protected Object doItNonFunction(VirtualFrame frame, Object function, Object[] arguments, PKeyword[] keywords,
@Bind Node inliningTarget,
Expand Down Expand Up @@ -2486,26 +2475,16 @@ protected Object doItNonFunction(VirtualFrame frame, Object function, Object[] a
PythonLanguage language = ctx.getLanguage(inliningTarget);

Object[] basesArray = Arrays.copyOfRange(arguments, 1, arguments.length);
PTuple origBases = PFactory.createTuple(language, basesArray);

if (arguments.length == 2) {
InteropLibrary lib = lazyInteropLibrary.get(inliningTarget);
if (lib.isHostObject(arguments[1]) && lib.isMetaObject(arguments[1])) {
// we want to subclass a Java class
return buildJavaClass(frame, inliningTarget, language, (PFunction) function, arguments, keywords, invokeBody, name);
}
}

class InitializeBuildClass {
boolean isClass;
Object meta;
PKeyword[] mkw;
PTuple bases;
Object[] bases;

@TruffleBoundary
InitializeBuildClass(PythonContext ctx) {

bases = update.execute(origBases, basesArray, basesArray.length);
InitializeBuildClass() {
bases = update.execute(basesArray);

mkw = keywords;
for (int i = 0; i < keywords.length; i++) {
Expand All @@ -2522,12 +2501,12 @@ class InitializeBuildClass {
}
}
if (meta == null) {
// if there are no bases, use type:
if (bases.getSequenceStorage().length() == 0) {
// if there are no bases, use type
if (bases.length == 0) {
meta = ctx.lookupType(PythonBuiltinClassType.PythonClass);
} else {
// else get the type of the first base
meta = getClass.execute(inliningTarget, SequenceStorageNodes.GetItemScalarNode.executeUncached(bases.getSequenceStorage(), 0));
meta = getClass.execute(inliningTarget, bases[0]);
}
isClass = true; // meta is really a class
}
Expand All @@ -2544,15 +2523,16 @@ class InitializeBuildClass {
Object savedState = BoundaryCallContext.enter(frame, language, ctx, boundaryCallData);
InitializeBuildClass init;
try {
init = new InitializeBuildClass(ctx);
init = new InitializeBuildClass();
} finally {
BoundaryCallContext.exit(frame, language, ctx, savedState);
}

Object ns;
PTuple bases = PFactory.createTuple(language, init.bases);
try {
Object prep = getPrepare.execute(frame, init.meta);
ns = callPrep.execute(frame, prep, new Object[]{name, init.bases}, init.mkw);
ns = callPrep.execute(frame, prep, new Object[]{name, bases}, init.mkw);
} catch (PException p) {
p.expectAttributeError(inliningTarget, noAttributeProfile);
ns = PFactory.createDict(language, new DynamicObjectStorage(language));
Expand All @@ -2563,10 +2543,20 @@ class InitializeBuildClass {
Object[] bodyArguments = PArguments.create(0);
PArguments.setSpecialArgument(bodyArguments, ns);
invokeBody.execute(frame, inliningTarget, (PFunction) function, bodyArguments);
if (init.bases != origBases) {
setOrigBases.execute(frame, inliningTarget, ns, SpecialAttributeNames.T___ORIG_BASES__, origBases);
if (init.bases != basesArray) {
setOrigBases.execute(frame, inliningTarget, ns, SpecialAttributeNames.T___ORIG_BASES__, PFactory.createTuple(language, basesArray));
}

if (init.bases.length == 1) {
InteropLibrary lib = lazyInteropLibrary.get(inliningTarget);
Object base = init.bases[0];
if (lib.isHostObject(base) && lib.isMetaObject(base)) {
// we want to subclass a Java class
return buildJavaClass(ns, name, base, keywords);
}
}
Object cls = callType.execute(frame, init.meta, new Object[]{name, init.bases, ns}, init.mkw);

Object cls = callType.execute(frame, init.meta, new Object[]{name, bases, ns}, init.mkw);

/*
* We could check here and throw "__class__ not set defining..." errors.
Expand Down
Loading