org.python.core.PyModule Maven / Gradle / Ivy
Show all versions of jython-slim Show documentation
// Copyright (c) Corporation for National Research Initiatives
package org.python.core;
import org.python.expose.ExposedDelete;
import org.python.expose.ExposedGet;
import org.python.expose.ExposedMethod;
import org.python.expose.ExposedNew;
import org.python.expose.ExposedSet;
import org.python.expose.ExposedType;
/**
* The Python Module object.
*
*/
@ExposedType(name = "module")
public class PyModule extends PyObject implements Traverseproc {
private final PyObject moduleDoc = Py.newBytes( //FIXME: not used (and not static)
"module(name[, doc])\n" +
"\n" +
"Create a module object.\n" +
"The name must be a string; the optional doc argument can have any type.");
/** The module's mutable dictionary */
@ExposedGet
public PyObject __dict__;
public PyModule() {
super();
}
public PyModule(PyType subType) {
super(subType);
}
public PyModule(PyType subType, String name) {
super(subType);
module___init__(new PyString(name), Py.None);
}
public PyModule(String name) {
this(name, null);
}
public PyModule(String name, PyObject dict) {
super();
__dict__ = dict;
module___init__(new PyString(name), Py.None);
}
@ExposedNew
@ExposedMethod
final void module___init__(PyObject[] args, String[] keywords) {
ArgParser ap = new ArgParser("__init__", args, keywords, new String[] {"name", "doc"});
PyObject name = ap.getPyObject(0);
PyObject docs = ap.getPyObject(1, Py.None);
module___init__(name, docs);
}
private void module___init__(PyObject name, PyObject doc) {
ensureDict();
__dict__.__setitem__("__name__", name);
__dict__.__setitem__("__doc__", doc);
if (name.equals(Py.newBytes("__main__"))) {
__dict__.__setitem__("__builtins__", Py.getSystemState().modules.__finditem__("__builtin__"));
__dict__.__setitem__("__package__", Py.None);
}
}
@Override
public PyObject fastGetDict() {
return __dict__;
}
@Override
public PyObject getDict() {
return __dict__;
}
@Override
@ExposedSet(name = "__dict__")
public void setDict(PyObject newDict) {
throw Py.TypeError("readonly attribute");
}
@Override
@ExposedDelete(name = "__dict__")
public void delDict() {
throw Py.TypeError("readonly attribute");
}
/**
* {@inheritDoc}
*
* Overridden in {@code PyModule} to search for a sub-module of this module (using the key
* {@code ".".join(self.__name__, name)}) in {@code sys.modules}, on the {@code self.__path__},
* and as a Java package with the same name. The named sub-module becomes an attribute of this
* module (in {@code __dict__}).
*/
@Override
protected PyObject impAttr(String name) {
// Some of our look-up needs the full name, deduced from __name__ and name.
String fullName = getFullName(name);
if (fullName != null) {
// Maybe the attribute is a Python sub-module
PyObject attr = findSubModule(name, fullName);
// Or is a Java package
if (attr == null) {
// Still looking: maybe it's a Java package?
attr = PySystemState.packageManager.lookupName(fullName);
}
// Add as an attribute the thing we found (if not still null)
return addedSubModule(name, fullName, attr);
}
return null;
}
/**
* Find Python sub-module within this object, within {@code sys.modules} or along this module's
* {@code __path__}.
*
* @param name simple name of sub package
* @param fullName of sub package
* @return module found or {@code null}
*/
private PyObject findSubModule(String name, String fullName) {
PyObject attr = null;
if (fullName != null) {
// The module may already have been loaded in sys.modules
attr = Py.getSystemState().modules.__finditem__(fullName);
// Or it may be found as a Python module along this module's __path__
if (attr == null) {
PyObject path = __dict__.__finditem__("__path__");
if (path == null) {
attr = imp.find_module(name, fullName, new PyList());
} else if (path instanceof PyList) {
attr = imp.find_module(name, fullName, (PyList) path);
} else if (path != Py.None) {
throw Py.TypeError("__path__ must be list or None");
}
}
}
return attr;
}
/**
* Add the given attribute to {@code __dict__}, if it is not {@code null} allowing
* {@code sys.modules[fullName]} to override.
*
* @param name of attribute to add
* @param fullName by which to check in {@code sys.modules}
* @param attr attribute to add (if not overridden)
* @return attribute value actually added (may be from {@code sys.modules}) or {@code null}
*/
private PyObject addedSubModule(String name, String fullName, PyObject attr) {
if (attr != null) {
if (fullName != null) {
// If a module by the full name exists in sys.modules, that takes precedence.
PyObject entry = Py.getSystemState().modules.__finditem__(fullName);
if (entry != null) {
attr = entry;
}
}
// Enter this as an attribute of this module.
__dict__.__setitem__(name, attr);
}
return attr;
}
/**
* Construct (and intern) the full name of a possible sub-module of this one, using the
* {@code __name__} attribute and a simple sub-module name. Return {@code null} if any of these
* requirements is missing.
*
* @param name simple name of (possible) sub-module
* @return interned full name or {@code null}
*/
private String getFullName(String name) {
if (__dict__ != null) {
PyObject pyName = __dict__.__finditem__("__name__");
if (pyName != null && name != null && name.length() > 0) {
return (pyName.__str__().toString() + '.' + name).intern();
}
}
return null;
}
/**
* {@inheritDoc}
*
* Overridden in {@code PyModule} so that if the base-class {@code __findattr_ex__} is
* unsuccessful, it will to search for the named attribute as a Java sub-package. This is
* responsible for the automagical import of Java (but not Python) packages when referred to as
* attributes.
*/
@Override
public PyObject __findattr_ex__(String name) {
// Find the attribute in the dictionary
PyObject attr = super.__findattr_ex__(name);
if (attr == null) {
// The attribute may be a Java sub-package to auto-import.
String fullName = getFullName(name);
if (fullName != null) {
attr = PySystemState.packageManager.lookupName(fullName);
// Any entry in sys.modules to takes precedence.
attr = addedSubModule(name, fullName, attr);
}
}
return attr;
}
@Override
public void __setattr__(String name, PyObject value) {
module___setattr__(name, value);
}
@ExposedMethod
final void module___setattr__(String name, PyObject value) {
if (name != "__dict__") {
ensureDict();
}
super.__setattr__(name, value);
}
@Override
public void __delattr__(String name) {
module___delattr__(name);
}
@ExposedMethod
final void module___delattr__(String name) {
super.__delattr__(name);
}
@Override
public String toString() {
return module_toString();
}
@ExposedMethod(names = {"__repr__"})
final String module_toString() {
PyObject name = null;
PyObject filename = null;
if (__dict__ != null) {
name = __dict__.__finditem__("__name__");
filename = __dict__.__finditem__("__file__");
}
if (name == null) {
name = Py.newString('?');
}
if (filename == null) {
return String.format("", name);
}
return String.format("", name, filename);
}
@Override
public PyObject __dir__() {
// Some special casing to ensure that classes deriving from PyModule
// can use their own __dict__. Although it would be nice to do this in
// PyModuleDerived, current templating in gderived.py does not support
// including from object, then overriding a specific method.
PyObject d;
if (this instanceof PyModuleDerived) {
d = __findattr_ex__("__dict__");
} else {
d = __dict__;
}
if (d == null ||
!(d instanceof AbstractDict ||
d instanceof PyDictProxy)) {
throw Py.TypeError(String.format("%.200s.__dict__ is not a dictionary",
getType().fastGetName().toLowerCase()));
}
return d.invoke("keys");
}
private void ensureDict() {
if (__dict__ == null) {
__dict__ = new PyStringMap();
}
}
/**
* Delegates to {@link Py#newJ(PyModule, Class, Object...)}. For keyword support use
* {@link #newJ(Class, String[], Object...)}.
*
* @see #newJ(Class, String[], Object...)
* @see Py#newJ(PyModule, Class, String[], Object...)
* @see Py#newJ(PyObject, Class, PyObject[], String[])
* @see Py#newJ(PyObject, Class, Object...)
* @see Py#newJ(PyObject, Class, String[], Object...)
*
* @param jcls Java-type of the desired clas, must have the same name
* @param args constructor-arguments
* @return a new instance of the desired class
*/
@SuppressWarnings("unchecked")
public T newJ(Class jcls, Object... args) {
return Py.newJ(this, jcls, args);
}
/**
* Delgates to {@link org.python.core.Py#newJ(PyModule, Class, String[], Object...)}.
* {@code keywords} are applied to the last {@code args} in the list.
*
* @see #newJ(Class, Object...)
* @see Py#newJ(PyModule, Class, String[], Object...)
* @see Py#newJ(PyObject, Class, PyObject[], String[])
* @see Py#newJ(PyObject, Class, Object...)
* @see Py#newJ(PyObject, Class, String[], Object...)
*
* @param jcls Java-type of the desired class, must have the same name
* @param keywords are applied to the last {@code args} in the list
* @param args constructor-arguments
* @return a new instance of the desired class
*/
@SuppressWarnings("unchecked")
public T newJ(Class jcls, String[] keywords, Object... args) {
return Py.newJ(this, jcls, keywords, args);
}
/* Traverseproc implementation */
@Override
public int traverse(Visitproc visit, Object arg) {
return __dict__ == null ? 0 : visit.visit(__dict__, arg);
}
@Override
public boolean refersDirectlyTo(PyObject ob) {
return ob != null && ob == __dict__;
}
}