Skip to content

Commit f731914

Browse files
committed
- improved java imports in case of incomplete or missing cachedir
- standalone mode now explicitly disables the package scan - this makes test386.py pass
1 parent 8215d37 commit f731914

4 files changed

Lines changed: 287 additions & 35 deletions

File tree

Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
package org.python.core;
2+
3+
import java.util.Iterator;
4+
import java.util.Map;
5+
import java.util.TreeMap;
6+
7+
/**
8+
* Helper class handling the VM specific java package detection.
9+
*/
10+
public class JavaImportHelper {
11+
12+
/**
13+
* Try to add the java package.
14+
* <p>
15+
* This is handy in cases where the package scan cannot run, or when the initial classpath does not contain all .jar
16+
* files (such as in J2EE containers).
17+
* <p>
18+
* There is some self-healing in the sense that a correct, explicit import of a java class will succeed even if
19+
* sys.modules already contains a Py.None entry for the corresponding java package.
20+
*
21+
* @param packageName The dotted name of the java package
22+
* @param fromlist A tuple with the from names to import. Can be null or empty.
23+
*
24+
* @return <code>true</code> if a java package was doubtlessly identified and added, <code>false</code>
25+
* otherwise.
26+
*/
27+
protected static boolean tryAddPackage(String packageName, PyObject fromlist) {
28+
// build the actual map with the packages known to the VM
29+
Map packages = buildLoadedPackages();
30+
31+
// make sure we do not turn off the added flag, once it is set
32+
boolean packageAdded = false;
33+
34+
// handle package name
35+
if (isLoadedPackage(packageName, packages)) {
36+
packageAdded = addPackage(packageName);
37+
}
38+
String parentPackageName = packageName;
39+
int dotPos = 0;
40+
do {
41+
dotPos = parentPackageName.lastIndexOf(".");
42+
if (dotPos > 0) {
43+
parentPackageName = parentPackageName.substring(0, dotPos);
44+
if (isLoadedPackage(parentPackageName, packages)) {
45+
boolean parentAdded = addPackage(parentPackageName);
46+
if (parentAdded) {
47+
packageAdded = true;
48+
}
49+
}
50+
}
51+
} while (dotPos > 0);
52+
53+
// handle fromlist
54+
if (fromlist != null && fromlist != Py.EmptyTuple && fromlist instanceof PyTuple) {
55+
Iterator iterator = ((PyTuple) fromlist).iterator();
56+
while (iterator.hasNext()) {
57+
Object obj = iterator.next();
58+
if (obj instanceof String) {
59+
String fromName = (String) obj;
60+
if (!"*".equals(fromName)) {
61+
boolean fromAdded = false;
62+
if (isJavaClass(packageName, fromName)) {
63+
fromAdded = addPackage(packageName);
64+
if (fromAdded) {
65+
packageAdded = true;
66+
}
67+
} else {
68+
// handle cases like: from java import math
69+
String fromPackageName = packageName + "." + fromName;
70+
if (isLoadedPackage(fromPackageName, packages)) {
71+
fromAdded = addPackage(fromPackageName);
72+
if (fromAdded) {
73+
packageAdded = true;
74+
}
75+
}
76+
}
77+
}
78+
}
79+
}
80+
}
81+
return packageAdded;
82+
}
83+
84+
/**
85+
* Check if a java package is already known to the VM.
86+
* <p>
87+
* May return <code>false</code> even if the given package name is a valid java package !
88+
*
89+
* @param packageName
90+
*
91+
* @return <code>true</code> if the package with the given name is already loaded by the VM, <code>false</code>
92+
* otherwise.
93+
*/
94+
protected static boolean isLoadedPackage(String packageName) {
95+
return isLoadedPackage(packageName, buildLoadedPackages());
96+
}
97+
98+
/**
99+
* Faster way to check if a java package is already known to the VM.
100+
* <p>
101+
* May return <code>false</code> even if the given package name is a valid java package !
102+
*
103+
* @param packageName
104+
* @param packages A Map containing all packages actually known to the VM. Such a Map can be obtained using
105+
* {@link JavaImportHelper.buildLoadedPackagesTree()}
106+
*
107+
* @return <code>true</code> if the package with the given name is already loaded by the VM, <code>false</code>
108+
* otherwise.
109+
*/
110+
private static boolean isLoadedPackage(String javaPackageName, Map packages) {
111+
return packages.containsKey(javaPackageName);
112+
}
113+
114+
/**
115+
* Build a <code>Map</code> of the currently known packages to the VM.
116+
* <p>
117+
* All parent packages appear as single entries like python modules, e.g. <code>java</code>,
118+
* <code>java.lang</code>, <code>java.lang.reflect</code>,
119+
*/
120+
private static Map buildLoadedPackages() {
121+
TreeMap packageMap = new TreeMap();
122+
Package[] packages = Package.getPackages();
123+
for (int i = 0; i < packages.length; i++) {
124+
String packageName = packages[i].getName();
125+
packageMap.put(packageName, "");
126+
int dotPos = 0;
127+
do {
128+
dotPos = packageName.lastIndexOf(".");
129+
if (dotPos > 0) {
130+
packageName = packageName.substring(0, dotPos);
131+
packageMap.put(packageName, "");
132+
}
133+
} while (dotPos > 0);
134+
}
135+
return packageMap;
136+
}
137+
138+
/**
139+
* Check a java class on VM level.
140+
*
141+
* @param packageName
142+
* @param className
143+
*
144+
* @return <code>true</code> if the java class can be doubtlessly identified, <code>false</code> otherwise.
145+
*/
146+
private static boolean isJavaClass(String packageName, String className) {
147+
if (className != null && className.length() > 0) {
148+
className = packageName.replace('.', '/') + "/" + className + ".class";
149+
return Thread.currentThread().getContextClassLoader().getResource(className) != null;
150+
} else {
151+
return false;
152+
}
153+
}
154+
155+
/**
156+
* Add a java package to sys.modules, if not already done
157+
*
158+
* @return <code>true</code> if something was really added, <code>false</code> otherwise
159+
*/
160+
private static boolean addPackage(String packageName) {
161+
boolean added = false;
162+
PyObject module = Py.getSystemState().modules.__finditem__(packageName.intern());
163+
if (module == null || module == Py.None) {
164+
PyObject modules = Py.getSystemState().modules;
165+
int dotPos;
166+
do {
167+
String internedPackageName = packageName.intern();
168+
if (modules.__finditem__(internedPackageName) == Py.None) {
169+
// a previously failed import could have created a Py.None entry in sys.modules
170+
modules.__delitem__(internedPackageName);
171+
}
172+
PyJavaPackage p = PySystemState.add_package(packageName);
173+
Py.getSystemState().modules.__setitem__(internedPackageName, p);
174+
added = true;
175+
dotPos = packageName.lastIndexOf(".");
176+
if (dotPos > 0) {
177+
packageName = packageName.substring(0, dotPos);
178+
}
179+
} while (dotPos > 0);
180+
}
181+
return added;
182+
}
183+
184+
}

src/org/python/core/PySystemState.java

Lines changed: 37 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import java.util.StringTokenizer;
1818
import java.util.jar.JarEntry;
1919
import java.util.jar.JarFile;
20+
2021
import org.python.modules.Setup;
2122

2223
/**
@@ -32,6 +33,10 @@ public class PySystemState extends PyObject
3233
private static final String JAR_SEPARATOR = "!";
3334
private static final String URL_BLANK_REPLACEMENT = "%20";
3435

36+
private static final String PYTHON_CACHEDIR = "python.cachedir";
37+
protected static final String PYTHON_CACHEDIR_SKIP = "python.cachedir.skip";
38+
protected static final String CACHEDIR_DEFAULT_NAME = "cachedir";
39+
3540
/**
3641
* The current version of Jython.
3742
*/
@@ -331,8 +336,8 @@ private static String findRoot(Properties preProperties,
331336
return classpath.substring(start, jpy);
332337
}
333338

334-
private static void initRegistry(Properties preProperties,
335-
Properties postProperties)
339+
private static void initRegistry(Properties preProperties, Properties postProperties,
340+
boolean standalone)
336341
{
337342
if (registry != null) {
338343
Py.writeError("systemState", "trying to reinitialize registry");
@@ -363,6 +368,12 @@ private static void initRegistry(Properties preProperties,
363368
registry.put(key, value);
364369
}
365370
}
371+
if (standalone) {
372+
// set default standalone property (if not yet set)
373+
if (!registry.containsKey(PYTHON_CACHEDIR_SKIP)) {
374+
registry.put(PYTHON_CACHEDIR_SKIP, "true");
375+
}
376+
}
366377
// Set up options from registry
367378
Options.setFromRegistry();
368379
}
@@ -396,6 +407,7 @@ public static Properties getBaseProperties(){
396407
return new Properties();
397408
}
398409
}
410+
399411
public static synchronized void initialize() {
400412
if (initialized)
401413
return;
@@ -428,15 +440,21 @@ public static synchronized void initialize(Properties preProperties,
428440
}
429441
initialized = true;
430442

443+
boolean standalone = false;
444+
String jarFileName = getJarFileName();
445+
if (jarFileName != null) {
446+
standalone = isStandalone(jarFileName);
447+
}
448+
431449
// initialize the JPython registry
432-
initRegistry(preProperties, postProperties);
450+
initRegistry(preProperties, postProperties, standalone);
433451

434452
// other initializations
435453
initBuiltins(registry);
436454
initStaticFields();
437455

438456
// Initialize the path (and add system defaults)
439-
defaultPath = initPath(registry);
457+
defaultPath = initPath(registry, standalone, jarFileName);
440458
defaultArgv = initArgv(argv);
441459

442460
// Set up the known Java packages
@@ -501,12 +519,12 @@ private static void initCacheDirectory(Properties props) {
501519
cachedir = null;
502520
return;
503521
}
504-
String skip = props.getProperty("python.cachedir.skip", "false");
522+
String skip = props.getProperty(PYTHON_CACHEDIR_SKIP, "false");
505523
if (skip.equalsIgnoreCase("true")) {
506524
cachedir = null;
507525
return;
508526
}
509-
cachedir = new File(props.getProperty("python.cachedir", "cachedir"));
527+
cachedir = new File(props.getProperty(PYTHON_CACHEDIR, CACHEDIR_DEFAULT_NAME));
510528
if (!cachedir.isAbsolute()) {
511529
cachedir = new File(PySystemState.prefix, cachedir.getPath());
512530
}
@@ -583,7 +601,7 @@ static String getBuiltin(String name) {
583601
return (String)builtinNames.get(name);
584602
}
585603

586-
private static PyList initPath(Properties props) {
604+
private static PyList initPath(Properties props, boolean standalone, String jarFileName) {
587605
PyList path = new PyList();
588606
if (!Py.frozen) {
589607
addPaths(path, props.getProperty("python.prepath", "."));
@@ -595,26 +613,29 @@ private static PyList initPath(Properties props) {
595613

596614
addPaths(path, props.getProperty("python.path", ""));
597615
}
598-
addJarLibPath(path);
616+
if (standalone) {
617+
// standalone jython: add the /Lib directory inside JYTHON_JAR to the path
618+
addPaths(path, jarFileName + "/Lib");
619+
}
599620

600621
return path;
601622
}
602623

603624
/**
604-
* standalone jython: add the /Lib directory inside JYTHON_JAR to the path (if present)
625+
* Check if we are in standalone mode.
605626
*
606-
* @param path the sys.path to be updated eventually
627+
* @param jarFileName The name of the jar file
628+
*
629+
* @return <code>true</code> if we have a standalone .jar file, <code>false</code> otherwise.
607630
*/
608-
private static void addJarLibPath(PyList path) {
609-
String jarFileName = getJarFileName();
631+
private static boolean isStandalone(String jarFileName) {
632+
boolean standalone = false;
610633
if (jarFileName != null) {
611634
JarFile jarFile = null;
612635
try {
613636
jarFile = new JarFile(jarFileName);
614637
JarEntry jarEntry = jarFile.getJarEntry("Lib/javaos.py");
615-
if (jarEntry != null) {
616-
addPaths(path, jarFileName + "/Lib");
617-
}
638+
standalone = jarEntry != null;
618639
} catch (IOException ioe) {
619640
} finally {
620641
if (jarFile != null) {
@@ -625,6 +646,7 @@ private static void addJarLibPath(PyList path) {
625646
}
626647
}
627648
}
649+
return standalone;
628650
}
629651

630652
/**

src/org/python/core/__builtin__.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -977,7 +977,7 @@ public PyObject __call__(PyObject args[], String keywords[]) {
977977

978978
private PyObject load(String module, PyObject globals, PyObject fromlist) {
979979
PyObject mod = imp.importName(module.intern(), fromlist.__len__() == 0,
980-
globals);
980+
globals, fromlist);
981981
return mod;
982982
}
983983

0 commit comments

Comments
 (0)