From: <cg...@us...> - 2009-01-21 23:32:59
|
Revision: 5955 http://jython.svn.sourceforge.net/jython/?rev=5955&view=rev Author: cgroves Date: 2009-01-21 23:32:55 +0000 (Wed, 21 Jan 2009) Log Message: ----------- Add a jython initializer service. If META-INF/services/org.python.core.JythonInitializer is on the classpath, the class named in that file will be instantiated and used in Jython's initialization. This is useful when Jython is initialized by a library outside of your control, but some customization still needs to be done to Jython's environment. I promise not to add a JythonFactory or a JythonFactoryFactory. Modified Paths: -------------- trunk/jython/build.xml trunk/jython/src/org/python/core/PySystemState.java trunk/jython/src/org/python/core/ThreadStateMapping.java Added Paths: ----------- trunk/jython/Lib/test/check_for_initializer_in_syspath.py trunk/jython/Lib/test/test_jython_initializer.py trunk/jython/src/org/python/core/JythonInitializer.java trunk/jython/tests/data/ trunk/jython/tests/data/initializer/ trunk/jython/tests/data/initializer/META-INF/ trunk/jython/tests/data/initializer/META-INF/services/ trunk/jython/tests/data/initializer/META-INF/services/org.python.core.JythonInitializer trunk/jython/tests/data/initializer/SyspathAppendingInitializer.java Added: trunk/jython/Lib/test/check_for_initializer_in_syspath.py =================================================================== --- trunk/jython/Lib/test/check_for_initializer_in_syspath.py (rev 0) +++ trunk/jython/Lib/test/check_for_initializer_in_syspath.py 2009-01-21 23:32:55 UTC (rev 5955) @@ -0,0 +1,2 @@ +import sys +assert "/from_SyspathAppendingInitializer_with_love" in sys.path Added: trunk/jython/Lib/test/test_jython_initializer.py =================================================================== --- trunk/jython/Lib/test/test_jython_initializer.py (rev 0) +++ trunk/jython/Lib/test/test_jython_initializer.py 2009-01-21 23:32:55 UTC (rev 5955) @@ -0,0 +1,17 @@ +import unittest +import subprocess +import sys +from test import test_support + +class TestUsingInitializer(unittest.TestCase): + def test_syspath_initializer(self): + fn = test_support.findfile("check_for_initializer_in_syspath.py") + ret = subprocess.Popen([sys.executable, fn], + env={"CLASSPATH":"tests/data/initializer"}).wait() + self.assertEquals(0, ret) + +def test_main(): + test_support.run_unittest(TestUsingInitializer) + +if __name__ == "__main__": + test_main() Modified: trunk/jython/build.xml =================================================================== --- trunk/jython/build.xml 2009-01-21 21:26:50 UTC (rev 5954) +++ trunk/jython/build.xml 2009-01-21 23:32:55 UTC (rev 5955) @@ -482,7 +482,17 @@ nowarn="${nowarn}"> <classpath refid="test.classpath" /> </javac> + <javac srcdir="tests/data/initializer" + destdir="tests/data/initializer" + target="${jdk.target.version}" + source="${jdk.source.version}" + debug="${debug}" + deprecation="${deprecation}" + nowarn="${nowarn}"> + <classpath refid="test.classpath" /> + </javac> + <copy file="${source.dir}/org/python/modules/ucnhash.dat" todir="${compile.dir}/org/python/modules" preservelastmodified="true" /> Added: trunk/jython/src/org/python/core/JythonInitializer.java =================================================================== --- trunk/jython/src/org/python/core/JythonInitializer.java (rev 0) +++ trunk/jython/src/org/python/core/JythonInitializer.java 2009-01-21 23:32:55 UTC (rev 5955) @@ -0,0 +1,35 @@ +package org.python.core; + +import java.util.Properties; + +import org.python.core.adapter.ExtensiblePyObjectAdapter; + +/** + * A service for initializing Jython without explicitly calling {@link PySystemState#initialize}. If + * a file META-INF/services/org.python.core.JythonInitializer is on the classpath, Jython will + * instantiate the class named in that file and use it in Jython's initialization. The given class + * must be an implementation of this interface with a no-arg constructor. + * + * @see <a href="http://java.sun.com/j2se/1.5.0/docs/guide/jar/jar.html#Service%20Provider">Java + Service Providers</a> + */ +public interface JythonInitializer { + + /** + * Called from {@link PySystemState#initialize} with the full set of initialization arguments. + * Implementations may modify or replace the given arguments, and must call + * {@link PySystemState#doInitialize}. + * + * @param argv + * - The command line arguments the jython interpreter was started with, or an empty + * array if jython wasn't started directly from the command line. + * @param classLoader + * - The classloader to be used by sys, or null if no sys-specific classloader was + * specified + */ + void initialize(Properties preProperties, + Properties postProperties, + String[] argv, + ClassLoader classLoader, + ExtensiblePyObjectAdapter adapter); +} Modified: trunk/jython/src/org/python/core/PySystemState.java =================================================================== --- trunk/jython/src/org/python/core/PySystemState.java 2009-01-21 21:26:50 UTC (rev 5954) +++ trunk/jython/src/org/python/core/PySystemState.java 2009-01-21 23:32:55 UTC (rev 5955) @@ -1,11 +1,16 @@ // Copyright (c) Corporation for National Research Initiatives package org.python.core; +import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; import java.net.URL; import java.net.URLDecoder; +import java.nio.charset.Charset; +import java.nio.charset.UnsupportedCharsetException; import java.security.AccessControlException; import java.util.Map; import java.util.Properties; @@ -539,9 +544,6 @@ Py.writeError("systemState", "trying to reinitialize registry"); return; } - if (preProperties == null) { - preProperties = getBaseProperties(); - } registry = preProperties; String prefix = findRoot(preProperties, postProperties, jarFileName); @@ -572,9 +574,7 @@ } } catch (SecurityException e) { } - if (postProperties != null) { - registry.putAll(postProperties); - } + registry.putAll(postProperties); if (standalone) { // set default standalone property (if not yet set) if (!registry.containsKey(PYTHON_CACHEDIR_SKIP)) { @@ -642,6 +642,116 @@ if (initialized) { return; } + if (preProperties == null) { + preProperties = getBaseProperties(); + } + if (postProperties == null) { + postProperties = new Properties(); + } + try { + ClassLoader context = Thread.currentThread().getContextClassLoader(); + if (context != null) { + if (initialize(preProperties, postProperties, argv, classLoader, adapter, context)) { + return; + } + } else { + Py.writeDebug("initializer", "Context class loader null, skipping"); + } + ClassLoader sysStateLoader = PySystemState.class.getClassLoader(); + if (sysStateLoader != null) { + if (initialize(preProperties, + postProperties, + argv, + classLoader, + adapter, + sysStateLoader)) { + return; + } + } else { + Py.writeDebug("initializer", "PySystemState.class class loader null, skipping"); + } + } catch (UnsupportedCharsetException e) { + Py.writeWarning("initializer", "Unable to load the UTF-8 charset to read an initializer definition"); + e.printStackTrace(System.err); + } catch (SecurityException e) { + // Must be running in a security environment that doesn't allow access to the class + // loader + } catch (Exception e) { + Py.writeWarning("initializer", + "Unexpected exception thrown while trying to use initializer service"); + e.printStackTrace(System.err); + } + doInitialize(preProperties, postProperties, argv, classLoader, adapter); + } + + private static final String INITIALIZER_SERVICE = + "META-INF/services/org.python.core.JythonInitializer"; + + /** + * Attempts to read a SystemStateInitializer service from the given classloader, instantiate it, + * and initialize with it. + * + * @throws UnsupportedCharsetException + * if unable to load UTF-8 to read a service definition + * @return true if a service is found and successfully initializes. + */ + private static boolean initialize(Properties pre, + Properties post, + String[] argv, + ClassLoader sysClassLoader, + ExtensiblePyObjectAdapter adapter, + ClassLoader initializerClassLoader) { + InputStream in = initializerClassLoader.getResourceAsStream(INITIALIZER_SERVICE); + if (in == null) { + Py.writeDebug("initializer", "'" + INITIALIZER_SERVICE + "' not found on " + initializerClassLoader); + return false; + } + BufferedReader r = new BufferedReader(new InputStreamReader(in, Charset.forName("UTF-8"))); + String className; + try { + className = r.readLine(); + } catch (IOException e) { + Py.writeWarning("initializer", "Failed reading '" + INITIALIZER_SERVICE + "' from " + + initializerClassLoader); + e.printStackTrace(System.err); + return false; + } + Class<?> initializer; + try { + initializer = initializerClassLoader.loadClass(className); + } catch (ClassNotFoundException e) { + Py.writeWarning("initializer", "Specified initializer class '" + className + + "' not found, continuing"); + return false; + } + try { + ((JythonInitializer)initializer.newInstance()).initialize(pre, + post, + argv, + sysClassLoader, + adapter); + } catch (Exception e) { + Py.writeWarning("initializer", "Failed initializing with class '" + className + + "', continuing"); + e.printStackTrace(System.err); + return false; + } + if (!initialized) { + Py.writeWarning("initializer", "Initializer '" + className + + "' failed to call doInitialize, using default initialization"); + } + return initialized; + } + + + public static synchronized PySystemState doInitialize(Properties preProperties, + Properties postProperties, + String[] argv, + ClassLoader classLoader, + ExtensiblePyObjectAdapter adapter) { + if (initialized) { + return Py.defaultSystemState; + } initialized = true; Py.setAdapter(adapter); boolean standalone = false; @@ -663,11 +773,13 @@ // Finish up standard Python initialization... Py.defaultSystemState = new PySystemState(); Py.setSystemState(Py.defaultSystemState); - if (classLoader != null) + if (classLoader != null) { Py.defaultSystemState.setClassLoader(classLoader); + } Py.initClassExceptions(getDefaultBuiltins()); // Make sure that Exception classes have been loaded new PySyntaxError("", 1, 1, "", ""); + return Py.defaultSystemState; } private static void initStaticFields() { Modified: trunk/jython/src/org/python/core/ThreadStateMapping.java =================================================================== --- trunk/jython/src/org/python/core/ThreadStateMapping.java 2009-01-21 21:26:50 UTC (rev 5954) +++ trunk/jython/src/org/python/core/ThreadStateMapping.java 2009-01-21 23:32:55 UTC (rev 5955) @@ -9,10 +9,13 @@ return ts; } + Thread t = Thread.currentThread(); - if (newSystemState == null) { Py.writeDebug("threadstate", "no current system state"); + if (Py.defaultSystemState == null) { + PySystemState.initialize(); + } newSystemState = Py.defaultSystemState; } Property changes on: trunk/jython/tests/data/initializer ___________________________________________________________________ Added: svn:ignore + SyspathAppendingInitializer.class Added: trunk/jython/tests/data/initializer/META-INF/services/org.python.core.JythonInitializer =================================================================== --- trunk/jython/tests/data/initializer/META-INF/services/org.python.core.JythonInitializer (rev 0) +++ trunk/jython/tests/data/initializer/META-INF/services/org.python.core.JythonInitializer 2009-01-21 23:32:55 UTC (rev 5955) @@ -0,0 +1 @@ +SyspathAppendingInitializer Added: trunk/jython/tests/data/initializer/SyspathAppendingInitializer.java =================================================================== --- trunk/jython/tests/data/initializer/SyspathAppendingInitializer.java (rev 0) +++ trunk/jython/tests/data/initializer/SyspathAppendingInitializer.java 2009-01-21 23:32:55 UTC (rev 5955) @@ -0,0 +1,18 @@ +import java.util.Properties; +import org.python.core.JythonInitializer; +import org.python.core.Py; +import org.python.core.PySystemState; +import org.python.core.adapter.ExtensiblePyObjectAdapter; + +public class SyspathAppendingInitializer implements JythonInitializer { + public void initialize(Properties preProperties, + Properties postProperties, + String[] argv, + ClassLoader classLoader, + ExtensiblePyObjectAdapter adapter) { + postProperties.put(PySystemState.PYTHON_CACHEDIR_SKIP, "true"); + PySystemState defaultState = + PySystemState.doInitialize(preProperties, postProperties, argv, classLoader, adapter); + defaultState.path.append(Py.newString("/from_SyspathAppendingInitializer_with_love")); + } +} This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |