Skip to content

Commit

Permalink
Add a jython initializer service.
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
groves committed Jan 21, 2009
1 parent 51d6144 commit 55dbb1f
Show file tree
Hide file tree
Showing 8 changed files with 206 additions and 8 deletions.
2 changes: 2 additions & 0 deletions Lib/test/check_for_initializer_in_syspath.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import sys
assert "/from_SyspathAppendingInitializer_with_love" in sys.path
17 changes: 17 additions & 0 deletions Lib/test/test_jython_initializer.py
Original file line number Diff line number Diff line change
@@ -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()
10 changes: 10 additions & 0 deletions build.xml
Original file line number Diff line number Diff line change
Expand Up @@ -482,6 +482,16 @@ The readme text for the next release will be like:
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"
Expand Down
35 changes: 35 additions & 0 deletions src/org/python/core/JythonInitializer.java
Original file line number Diff line number Diff line change
@@ -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);
}
126 changes: 119 additions & 7 deletions src/org/python/core/PySystemState.java
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -539,9 +544,6 @@ private static void initRegistry(Properties preProperties, Properties postProper
Py.writeError("systemState", "trying to reinitialize registry");
return;
}
if (preProperties == null) {
preProperties = getBaseProperties();
}

registry = preProperties;
String prefix = findRoot(preProperties, postProperties, jarFileName);
Expand Down Expand Up @@ -572,9 +574,7 @@ private static void initRegistry(Properties preProperties, Properties postProper
}
} 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)) {
Expand Down Expand Up @@ -642,6 +642,116 @@ public static synchronized void initialize(Properties preProperties,
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;
Expand All @@ -663,11 +773,13 @@ public static synchronized void initialize(Properties preProperties,
// 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() {
Expand Down
5 changes: 4 additions & 1 deletion src/org/python/core/ThreadStateMapping.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,13 @@ public ThreadState getThreadState(PySystemState newSystemState) {
return ts;
}

Thread t = Thread.currentThread();

Thread t = Thread.currentThread();
if (newSystemState == null) {
Py.writeDebug("threadstate", "no current system state");
if (Py.defaultSystemState == null) {
PySystemState.initialize();
}
newSystemState = Py.defaultSystemState;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
SyspathAppendingInitializer
18 changes: 18 additions & 0 deletions tests/data/initializer/SyspathAppendingInitializer.java
Original file line number Diff line number Diff line change
@@ -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"));
}
}

0 comments on commit 55dbb1f

Please sign in to comment.