Skip to content

Commit a7f22a5

Browse files
committed
[GR-71497] Support basic usage of foreign meta-objects that are not instantiable with typing.
PullRequest: graalpython/4113
2 parents a95e884 + d1cfb8a commit a7f22a5

File tree

5 files changed

+129
-8
lines changed

5 files changed

+129
-8
lines changed

graalpython/com.oracle.graal.python.tck/src/com/oracle/graal/python/tck/PythonProvider.java

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* The Universal Permissive License (UPL), Version 1.0
@@ -76,6 +76,7 @@
7676
import org.graalvm.polyglot.Value;
7777
import org.graalvm.polyglot.tck.LanguageProvider;
7878
import org.graalvm.polyglot.tck.ResultVerifier;
79+
import org.graalvm.polyglot.tck.ResultVerifier.SnippetRun;
7980
import org.graalvm.polyglot.tck.Snippet;
8081
import org.graalvm.polyglot.tck.TypeDescriptor;
8182
import org.junit.Assert;
@@ -262,8 +263,8 @@ public Collection<? extends Snippet> createExpressions(Context context) {
262263
addExpressionSnippet(context, snippets, "isinstance", "lambda x, y: isinstance(x, y)", BOOLEAN, ANY, META_OBJECT);
263264
addExpressionSnippet(context, snippets, "issubclass", "lambda x, y: issubclass(x, y)", BOOLEAN, META_OBJECT, META_OBJECT);
264265

265-
addExpressionSnippet(context, snippets, "[]", "lambda x, y: x[y]", ANY, GetItemVerifier.INSTANCE, union(array(ANY), STRING, hash(ANY, ANY)), ANY);
266-
addExpressionSnippet(context, snippets, "[a:b]", "lambda x: x[:]", union(STRING, array(ANY)), union(STRING, array(ANY)));
266+
addExpressionSnippet(context, snippets, "[]", "lambda x, y: x[y]", ANY, GetItemVerifier.INSTANCE, union(array(ANY), STRING, hash(ANY, ANY), META_OBJECT), ANY);
267+
addExpressionSnippet(context, snippets, "[a:b]", "lambda x: x[:]", union(STRING, array(ANY)), SliceVerifier.INSTANCE, union(STRING, array(ANY), META_OBJECT));
267268

268269
// @formatter:on
269270
return snippets;
@@ -493,6 +494,30 @@ public void accept(SnippetRun snippetRun) throws PolyglotException {
493494
private static final MulVerifier INSTANCE = new MulVerifier();
494495
}
495496

497+
private static void checkTypeSubscripting(SnippetRun snippetRun, Value self) {
498+
if (snippetRun.getException() != null) {
499+
// only specific Python types allow parameterization
500+
String metaName = self.getMetaQualifiedName();
501+
assert metaName.equals("object") || metaName.equals("type_user") : metaName;
502+
} else {
503+
assert snippetRun.getResult().getMetaObject().getMetaQualifiedName().equals("types.GenericAlias");
504+
}
505+
}
506+
507+
private static class SliceVerifier extends PResultVerifier {
508+
@Override
509+
public void accept(SnippetRun snippetRun) throws PolyglotException {
510+
Value self = snippetRun.getParameters().get(0);
511+
if (self.isMetaObject()) {
512+
checkTypeSubscripting(snippetRun, self);
513+
} else {
514+
ResultVerifier.getDefaultResultVerifier().accept(snippetRun);
515+
}
516+
}
517+
518+
private static final SliceVerifier INSTANCE = new SliceVerifier();
519+
}
520+
496521
private static class GetItemVerifier extends PResultVerifier {
497522
private static final String[] UNHASHABLE_TYPES = new String[]{"list", "dict", "bytearray", "set"};
498523

@@ -542,6 +567,8 @@ public void accept(SnippetRun snippetRun) throws PolyglotException {
542567
} else {
543568
assert snippetRun.getException() == null : snippetRun.getException();
544569
}
570+
} else if (self.isMetaObject()) {
571+
checkTypeSubscripting(snippetRun, self);
545572
} else {
546573
// argument type error, rethrow
547574
throw snippetRun.getException();

graalpython/com.oracle.graal.python.test/src/tests/test_interop.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -575,6 +575,19 @@ def test_java_import_star(self):
575575
assert "getGlobal" in d
576576
assert d["getGlobal"]().getName() == d["GLOBAL_LOGGER_NAME"]
577577

578+
def test_java_typing_support(self):
579+
import typing
580+
import java.lang.Integer as Integer
581+
import java.util.List as List
582+
assert List.__name__ == "List"
583+
assert List.__qualname__ == "java.util.List"
584+
assert List.__module__ == "polyglot"
585+
assert List.__type_params__ == ()
586+
assert List.__annotations__ == {}
587+
assert isinstance(List[Integer], typing.GenericAlias)
588+
assert List[Integer].__origin__ == List
589+
assert List[Integer].__args__ == (Integer,)
590+
578591
def test_java_null_is_none(self):
579592
import java.lang.Integer as Integer
580593
x = Integer.getInteger("something_that_does_not_exists")

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/PythonBuiltinClassType.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,7 @@
174174
import com.oracle.graal.python.builtins.objects.exception.UnicodeTranslateErrorBuiltins;
175175
import com.oracle.graal.python.builtins.objects.filter.FilterBuiltins;
176176
import com.oracle.graal.python.builtins.objects.floats.FloatBuiltins;
177+
import com.oracle.graal.python.builtins.objects.foreign.ForeignAbstractClassBuiltins;
177178
import com.oracle.graal.python.builtins.objects.foreign.ForeignBooleanBuiltins;
178179
import com.oracle.graal.python.builtins.objects.foreign.ForeignExecutableBuiltins;
179180
import com.oracle.graal.python.builtins.objects.foreign.ForeignInstantiableBuiltins;
@@ -829,7 +830,7 @@ It can be called either on the class (e.g. C.f()) or on an instance
829830
"ForeignBoolean",
830831
ForeignNumber,
831832
newBuilder().publishInModule(J_POLYGLOT).basetype().addDict().disallowInstantiation().slots(ForeignBooleanBuiltins.SLOTS)),
832-
ForeignAbstractClass("ForeignAbstractClass", ForeignObject, newBuilder().publishInModule(J_POLYGLOT).basetype().addDict().disallowInstantiation()),
833+
ForeignAbstractClass("ForeignAbstractClass", ForeignObject, newBuilder().publishInModule(J_POLYGLOT).basetype().addDict().disallowInstantiation().slots(ForeignAbstractClassBuiltins.SLOTS)),
833834
ForeignExecutable("ForeignExecutable", ForeignObject, newBuilder().publishInModule(J_POLYGLOT).basetype().addDict().disallowInstantiation().slots(ForeignExecutableBuiltins.SLOTS)),
834835
ForeignInstantiable("ForeignInstantiable", ForeignObject, newBuilder().publishInModule(J_POLYGLOT).basetype().addDict().slots(ForeignInstantiableBuiltins.SLOTS)),
835836
ForeignIterable("ForeignIterable", ForeignObject, newBuilder().publishInModule(J_POLYGLOT).basetype().addDict().disallowInstantiation().slots(ForeignIterableBuiltins.SLOTS)),

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/exception/ExceptionNodes.java

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,6 @@
6161
import com.oracle.graal.python.nodes.PGuards;
6262
import com.oracle.graal.python.nodes.PNodeWithContext;
6363
import com.oracle.graal.python.nodes.PRaiseNode;
64-
import com.oracle.graal.python.nodes.object.IsForeignObjectNode;
6564
import com.oracle.graal.python.runtime.PythonContext;
6665
import com.oracle.graal.python.runtime.exception.PException;
6766
import com.oracle.graal.python.runtime.formatting.ErrorMessageFormatter;
@@ -410,12 +409,11 @@ static PTuple doNative(@SuppressWarnings("unused") Node inliningTarget, PythonAb
410409
return (PTuple) noValueToNone(readObject.readFromObj(exception, CFields.PyBaseExceptionObject__args));
411410
}
412411

413-
@Specialization
414-
static PTuple doInterop(AbstractTruffleException exception,
412+
@Specialization(guards = "isForeignObject(exception)")
413+
static PTuple doInterop(Object exception,
415414
@Bind PythonLanguage language,
416415
@CachedLibrary(limit = "getCallSiteInlineCacheMaxDepth()") InteropLibrary interop,
417416
@Cached TruffleString.SwitchEncodingNode switchEncodingNode) {
418-
assert IsForeignObjectNode.executeUncached(exception);
419417
try {
420418
TruffleString message;
421419
if (interop.hasExceptionMessage(exception)) {

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignAbstractClassBuiltins.java

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,21 +26,32 @@
2626

2727
package com.oracle.graal.python.builtins.objects.foreign;
2828

29+
import static com.oracle.graal.python.nodes.BuiltinNames.T_POLYGLOT;
30+
import static com.oracle.graal.python.nodes.SpecialAttributeNames.J___ANNOTATIONS__;
2931
import static com.oracle.graal.python.nodes.SpecialAttributeNames.J___BASES__;
32+
import static com.oracle.graal.python.nodes.SpecialAttributeNames.J___MODULE__;
33+
import static com.oracle.graal.python.nodes.SpecialAttributeNames.J___NAME__;
34+
import static com.oracle.graal.python.nodes.SpecialAttributeNames.J___QUALNAME__;
35+
import static com.oracle.graal.python.nodes.SpecialAttributeNames.J___TYPE_PARAMS__;
3036
import static com.oracle.graal.python.nodes.SpecialMethodNames.J___INSTANCECHECK__;
3137

3238
import java.util.List;
3339

3440
import com.oracle.graal.python.PythonLanguage;
3541
import com.oracle.graal.python.annotations.Builtin;
42+
import com.oracle.graal.python.annotations.Slot;
43+
import com.oracle.graal.python.annotations.Slot.SlotKind;
3644
import com.oracle.graal.python.builtins.CoreFunctions;
3745
import com.oracle.graal.python.builtins.PythonBuiltinClassType;
3846
import com.oracle.graal.python.builtins.PythonBuiltins;
3947
import com.oracle.graal.python.builtins.objects.common.SequenceStorageNodes;
48+
import com.oracle.graal.python.builtins.objects.type.TpSlots;
49+
import com.oracle.graal.python.builtins.objects.type.slots.TpSlotBinaryFunc.MpSubscriptBuiltinNode;
4050
import com.oracle.graal.python.lib.PyObjectGetIter;
4151
import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode;
4252
import com.oracle.graal.python.nodes.function.builtins.PythonBinaryBuiltinNode;
4353
import com.oracle.graal.python.nodes.function.builtins.PythonUnaryBuiltinNode;
54+
import com.oracle.graal.python.nodes.interop.PForeignToPTypeNode;
4455
import com.oracle.graal.python.runtime.GilNode;
4556
import com.oracle.graal.python.runtime.object.PFactory;
4657
import com.oracle.graal.python.runtime.sequence.storage.SequenceStorage;
@@ -64,6 +75,8 @@
6475
*/
6576
@CoreFunctions(extendClasses = PythonBuiltinClassType.ForeignAbstractClass)
6677
public final class ForeignAbstractClassBuiltins extends PythonBuiltins {
78+
public static TpSlots SLOTS = ForeignAbstractClassBuiltinsSlotsGen.SLOTS;
79+
6780
@Override
6881
protected List<? extends NodeFactory<? extends PythonBuiltinBaseNode>> getNodeFactories() {
6982
return ForeignAbstractClassBuiltinsFactory.getFactories();
@@ -94,6 +107,66 @@ static Object getBases(VirtualFrame frame, Object self,
94107
}
95108
}
96109

110+
@Builtin(name = J___NAME__, minNumOfPositionalArgs = 1, isGetter = true, isSetter = false)
111+
@GenerateNodeFactory
112+
abstract static class ForeignNameNode extends PythonUnaryBuiltinNode {
113+
@Specialization(limit = "2")
114+
static Object foreignName(Object self,
115+
@CachedLibrary("self") InteropLibrary lib,
116+
@Cached PForeignToPTypeNode castStr) {
117+
try {
118+
return castStr.executeConvert(lib.getMetaSimpleName(self));
119+
} catch (UnsupportedMessageException e) {
120+
throw CompilerDirectives.shouldNotReachHere(e);
121+
}
122+
}
123+
}
124+
125+
@Builtin(name = J___QUALNAME__, minNumOfPositionalArgs = 1, isGetter = true, isSetter = false)
126+
@GenerateNodeFactory
127+
abstract static class ForeignQualnameNode extends PythonUnaryBuiltinNode {
128+
@Specialization(limit = "2")
129+
static Object foreignName(Object self,
130+
@CachedLibrary("self") InteropLibrary lib,
131+
@Cached PForeignToPTypeNode castStr) {
132+
try {
133+
return castStr.executeConvert(lib.getMetaQualifiedName(self));
134+
} catch (UnsupportedMessageException e) {
135+
throw CompilerDirectives.shouldNotReachHere(e);
136+
}
137+
}
138+
}
139+
140+
@Builtin(name = J___MODULE__, minNumOfPositionalArgs = 1, isGetter = true, isSetter = false)
141+
@GenerateNodeFactory
142+
abstract static class ForeignModuleNode extends PythonUnaryBuiltinNode {
143+
@Specialization
144+
static Object foreignModule(@SuppressWarnings("unused") Object self) {
145+
return T_POLYGLOT;
146+
}
147+
}
148+
149+
@Builtin(name = J___TYPE_PARAMS__, minNumOfPositionalArgs = 1, isGetter = true, isSetter = false)
150+
@GenerateNodeFactory
151+
abstract static class ForeignTypeParams extends PythonUnaryBuiltinNode {
152+
@Specialization
153+
static Object foreignTypeParams(@SuppressWarnings("unused") Object self,
154+
@Bind PythonLanguage lang) {
155+
return PFactory.createEmptyTuple(lang);
156+
}
157+
}
158+
159+
@Builtin(name = J___ANNOTATIONS__, minNumOfPositionalArgs = 1, isGetter = true, isSetter = false)
160+
@GenerateNodeFactory
161+
abstract static class ForeignAnnotations extends PythonUnaryBuiltinNode {
162+
@Specialization
163+
static Object foreignAnnotations(@SuppressWarnings("unused") Object self,
164+
@Bind PythonLanguage lang) {
165+
// TODO: use the declared members interop API to return annotations
166+
return PFactory.createDict(lang);
167+
}
168+
}
169+
97170
@Builtin(name = J___INSTANCECHECK__, minNumOfPositionalArgs = 2)
98171
@GenerateNodeFactory
99172
abstract static class InstancecheckNode extends PythonBinaryBuiltinNode {
@@ -112,4 +185,13 @@ static Object check(Object self, Object instance,
112185
}
113186
}
114187

188+
@Slot(value = SlotKind.mp_subscript, isComplex = true)
189+
@GenerateNodeFactory
190+
abstract static class ForeignClassGetItemNode extends MpSubscriptBuiltinNode {
191+
@Specialization
192+
static Object classGetItem(Object cls, Object key,
193+
@Bind PythonLanguage language) {
194+
return PFactory.createGenericAlias(language, cls, key);
195+
}
196+
}
115197
}

0 commit comments

Comments
 (0)