Skip to content

Commit c4ae7ad

Browse files
timfelansalond
authored andcommitted
add test and fix for erroneously frozen package paths in pre-initialized python modules
(cherry picked from commit 353f2bb)
1 parent aa85650 commit c4ae7ad

File tree

2 files changed

+65
-3
lines changed

2 files changed

+65
-3
lines changed

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

Lines changed: 52 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
import java.util.concurrent.locks.ReentrantLock;
3838
import java.util.function.Supplier;
3939

40+
import org.graalvm.nativeimage.ImageInfo;
4041
import org.graalvm.options.OptionValues;
4142

4243
import com.oracle.graal.python.PythonLanguage;
@@ -45,7 +46,10 @@
4546
import com.oracle.graal.python.builtins.objects.cext.PythonNativeClass;
4647
import com.oracle.graal.python.builtins.objects.common.HashingStorage;
4748
import com.oracle.graal.python.builtins.objects.dict.PDict;
49+
import com.oracle.graal.python.builtins.objects.list.PList;
4850
import com.oracle.graal.python.builtins.objects.module.PythonModule;
51+
import com.oracle.graal.python.builtins.objects.str.PString;
52+
import com.oracle.graal.python.nodes.SpecialAttributeNames;
4953
import com.oracle.graal.python.runtime.AsyncHandler.AsyncAction;
5054
import com.oracle.graal.python.runtime.exception.PException;
5155
import com.oracle.truffle.api.Assumption;
@@ -234,17 +238,50 @@ public boolean isInitialized() {
234238

235239
public void initialize() {
236240
core.initialize(this);
237-
setupRuntimeInformation();
241+
setupRuntimeInformation(false);
238242
core.postInitialize();
239243
}
240244

241245
public void patch(Env newEnv) {
242246
setEnv(newEnv);
243-
setupRuntimeInformation();
247+
setupRuntimeInformation(true);
244248
core.postInitialize();
245249
}
246250

247-
private void setupRuntimeInformation() {
251+
/**
252+
* During pre-initialization, we're also loading code from the Python standard library. Since
253+
* some of those modules may be packages, they will have their __path__ attribute set to the
254+
* absolute path of the package on the build system. We use this function to patch the paths
255+
* during build time and after starting up from a pre-initialized context so they point to the
256+
* run-time package paths.
257+
*/
258+
private void patchPackagePaths(String from, String to) {
259+
for (Object v : sysModules.getDictStorage().values()) {
260+
if (v instanceof PythonModule) {
261+
Object path = ((PythonModule) v).getAttribute(SpecialAttributeNames.__PATH__);
262+
if (path instanceof PList) {
263+
Object[] paths = ((PList) path).getSequenceStorage().getCopyOfInternalArray();
264+
for (int i = 0; i < paths.length; i++) {
265+
Object pathElement = paths[i];
266+
String strPath;
267+
if (pathElement instanceof PString) {
268+
strPath = ((PString) pathElement).getValue();
269+
} else if (pathElement instanceof String) {
270+
strPath = (String) pathElement;
271+
} else {
272+
continue;
273+
}
274+
if (strPath.startsWith(from)) {
275+
paths[i] = strPath.replace(from, to);
276+
}
277+
}
278+
((PythonModule) v).setAttribute(SpecialAttributeNames.__PATH__, core.factory().createList(paths));
279+
}
280+
}
281+
}
282+
}
283+
284+
private void setupRuntimeInformation(boolean isPatching) {
248285
PythonModule sysModule = core.lookupBuiltinModule("sys");
249286
sysModules = (PDict) sysModule.getAttribute("modules");
250287

@@ -256,6 +293,18 @@ private void setupRuntimeInformation() {
256293

257294
sysModules.setItem(__MAIN__, mainModule);
258295

296+
final String stdLibPlaceholder = "!stdLibHome!";
297+
final String stdLibHome = PythonCore.getStdlibHome(getEnv());
298+
if (ImageInfo.inImageBuildtimeCode()) {
299+
// Patch any pre-loaded packages' paths if we're running
300+
// pre-initialization
301+
patchPackagePaths(stdLibHome, stdLibPlaceholder);
302+
} else if (isPatching && ImageInfo.inImageRuntimeCode()) {
303+
// Patch any pre-loaded packages' paths to the new stdlib home if
304+
// we're patching a pre-initialized context
305+
patchPackagePaths(stdLibPlaceholder, stdLibHome);
306+
}
307+
259308
currentException = null;
260309
isInitialized = true;
261310
}

mx.graalpython/mx_graalpython.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -394,6 +394,19 @@ def graalpython_gate_runner(args, tasks):
394394
])
395395
if success not in out.data:
396396
mx.abort('Output from generated SVM image "' + svm_image + '" did not match success pattern:\n' + success)
397+
# Test that stdlib paths are not cached on e.g. encodings module
398+
out = mx.OutputCapture()
399+
mx.run([svm_image, "-S", "--python.StdLibHome=/foobar", "-c", "import encodings; print(encodings.__path__)"], out=mx.TeeOutputCapture(out))
400+
if "/foobar" not in out.data:
401+
mx.abort('Output from generated SVM image "' + svm_image + '" did not have patched std lib path "/foobar", got:\n' + success)
402+
# Finally, test that we can start even if the graalvm was moved
403+
out = mx.OutputCapture()
404+
graalvm_home = svm_image.replace(os.path.sep.join(["", "bin", "graalpython"]), "")
405+
new_graalvm_home = graalvm_home + "_new"
406+
shutil.move(graalvm_home, new_graalvm_home)
407+
launcher = os.path.join(new_graalvm_home, "bin", "graalpython")
408+
mx.log(launcher)
409+
mx.run([launcher, "-S", "-c", "print(b'abc'.decode('ascii'))"]) # Should not fail
397410

398411

399412
mx_gate.add_gate_runner(SUITE, graalpython_gate_runner)

0 commit comments

Comments
 (0)