Skip to content

Commit 3da5267

Browse files
committed
[GR-18308] allow walking not-yet-classloaded java packages during import
PullRequest: graalpython/658
2 parents 98dac2d + cb61f18 commit 3da5267

File tree

6 files changed

+82
-2
lines changed

6 files changed

+82
-2
lines changed

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/BuiltinFunctions.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@
6161
import static com.oracle.graal.python.nodes.BuiltinNames.__BUILTIN__;
6262
import static com.oracle.graal.python.nodes.BuiltinNames.__DEBUG__;
6363
import static com.oracle.graal.python.nodes.BuiltinNames.__DUMP_TRUFFLE_AST__;
64+
import static com.oracle.graal.python.nodes.BuiltinNames.__JYTHON_CURRENT_IMPORT__;
6465
import static com.oracle.graal.python.nodes.HiddenAttributes.ID_KEY;
6566
import static com.oracle.graal.python.nodes.SpecialAttributeNames.__NAME__;
6667
import static com.oracle.graal.python.nodes.SpecialMethodNames.__INSTANCECHECK__;
@@ -1881,6 +1882,15 @@ protected static boolean isFunction(Object callee) {
18811882
}
18821883
}
18831884

1885+
@Builtin(name = __JYTHON_CURRENT_IMPORT__, minNumOfPositionalArgs = 0)
1886+
@GenerateNodeFactory
1887+
public abstract static class CurrentImport extends PythonBuiltinNode {
1888+
@Specialization
1889+
String doIt() {
1890+
return getContext().getCurrentImport();
1891+
}
1892+
}
1893+
18841894
@Builtin(name = "input", parameterNames = {"prompt"})
18851895
@GenerateNodeFactory
18861896
abstract static class InputNode extends PythonUnaryBuiltinNode {

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/BuiltinNames.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,4 +135,5 @@ public abstract class BuiltinNames {
135135
// truffle specific builtins
136136
public static final String __BUILTIN__ = "__builtin__";
137137
public static final String __DUMP_TRUFFLE_AST__ = "__dump_truffle_ast__";
138+
public static final String __JYTHON_CURRENT_IMPORT__ = "__jython_current_import__";
138139
}

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/statement/AbstractImportNode.java

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@
5454
import com.oracle.graal.python.nodes.object.GetDictNode;
5555
import com.oracle.graal.python.nodes.util.ExceptionStateNodes.PassCaughtExceptionNode;
5656
import com.oracle.graal.python.runtime.PythonContext;
57+
import com.oracle.graal.python.runtime.PythonOptions;
5758
import com.oracle.graal.python.runtime.object.PythonObjectFactory;
5859
import com.oracle.truffle.api.CompilerDirectives;
5960
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
@@ -69,6 +70,7 @@ public abstract class AbstractImportNode extends StatementNode {
6970
@Child private CallNode callNode;
7071
@Child private GetDictNode getDictNode;
7172
@Child private PassCaughtExceptionNode passExceptionNode;
73+
@CompilationFinal private Boolean emulateJython;
7274

7375
public AbstractImportNode() {
7476
super();
@@ -122,7 +124,20 @@ protected Object importModule(VirtualFrame frame, String name, Object globals, S
122124
return builtinModule;
123125
}
124126
}
125-
return __import__(frame, name, globals, fromList, level);
127+
if (emulateJython()) {
128+
if (fromList.length > 0) {
129+
getContext().pushCurrentImport(name + "." + fromList[0]);
130+
} else {
131+
getContext().pushCurrentImport(name);
132+
}
133+
}
134+
try {
135+
return __import__(frame, name, globals, fromList, level);
136+
} finally {
137+
if (emulateJython()) {
138+
getContext().popCurrentImport();
139+
}
140+
}
126141
}
127142

128143
Object __import__(VirtualFrame frame, String name, Object globals, String[] fromList, int level) {
@@ -138,6 +153,14 @@ Object __import__(VirtualFrame frame, String name, Object globals, String[] from
138153
});
139154
}
140155

156+
private boolean emulateJython() {
157+
if (emulateJython == null) {
158+
CompilerDirectives.transferToInterpreterAndInvalidate();
159+
emulateJython = PythonOptions.getFlag(getContext(), PythonOptions.EmulateJython);
160+
}
161+
return emulateJython;
162+
}
163+
141164
@Override
142165
public boolean hasTag(Class<? extends Tag> tag) {
143166
return tag == StandardTags.CallTag.class || super.hasTag(tag);

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PythonContext.java

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
import java.io.OutputStream;
3737
import java.nio.file.LinkOption;
3838
import java.text.MessageFormat;
39+
import java.util.ArrayDeque;
3940
import java.util.ArrayList;
4041
import java.util.HashMap;
4142
import java.util.List;
@@ -142,6 +143,10 @@ public final class PythonContext {
142143
private final PosixResources resources;
143144
private final AsyncHandler handler;
144145

146+
// A thread-local to store the full path to the currently active import statement, for Jython
147+
// compat
148+
private final ThreadLocal<ArrayDeque<String>> currentImport = new ThreadLocal<>();
149+
145150
public PythonContext(PythonLanguage language, TruffleLanguage.Env env, PythonCore core) {
146151
this.language = language;
147152
this.core = core;
@@ -721,4 +726,30 @@ public boolean isPyFileInLanguageHome(TruffleFile path) {
721726
PythonLanguage.getLogger().log(Level.FINE, () -> "Cannot access file " + path + " because there is no language home.");
722727
return false;
723728
}
729+
730+
@TruffleBoundary
731+
public String getCurrentImport() {
732+
ArrayDeque<String> ci = currentImport.get();
733+
if (ci == null || ci.isEmpty()) {
734+
return "";
735+
} else {
736+
return ci.peek();
737+
}
738+
}
739+
740+
@TruffleBoundary
741+
public void pushCurrentImport(String object) {
742+
ArrayDeque<String> ci = currentImport.get();
743+
if (ci == null) {
744+
ci = new ArrayDeque<>();
745+
currentImport.set(ci);
746+
}
747+
ci.push(object);
748+
}
749+
750+
@TruffleBoundary
751+
public void popCurrentImport() {
752+
assert currentImport.get() != null && currentImport.get().peek() != null : "invalid popCurrentImport without push";
753+
currentImport.get().pop();
754+
}
724755
}

graalpython/lib-graalpython/java.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,16 @@ class JavaImportFinder:
130130
if sys.graal_python_jython_emulation_enabled:
131131
@staticmethod
132132
def find_spec(fullname, path, target=None):
133+
# Because of how Jython allows you to import classes that have not
134+
# been loaded, yet, we need attempt to load types with the full
135+
# string. This will ensure that if there is such a Java class, its
136+
# package will have been initialized and thus the is_java_package
137+
# check below will work
138+
try:
139+
type(__jython_current_import__())
140+
except KeyError:
141+
pass
142+
# continue normally with import
133143
if JavaPackageLoader.is_java_package(fullname):
134144
return _frozen_importlib.ModuleSpec(fullname, JavaPackageLoader, is_package=True)
135145
else:
@@ -151,3 +161,8 @@ def find_spec(fullname, path, target=None):
151161
sys.meta_path.append(JavaImportFinder)
152162
if sys.graal_python_jython_emulation_enabled:
153163
__getattr__ = JavaPackageLoader._make_getattr("java")
164+
else:
165+
# remove __jython_current_import__ function from builtins module
166+
import builtins
167+
del builtins.__jython_current_import__
168+
del builtins

mx.graalpython/mx_graalpython.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@
5353
# compatibility between Python versions
5454
PY3 = sys.version_info[0] == 3
5555
if PY3:
56-
raw_input = input
56+
raw_input = input # pylint: disable=redefined-builtin;
5757

5858

5959
def _get_core_home():

0 commit comments

Comments
 (0)