1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238
|
# *****************************************************************************
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# See NOTICE file for details.
#
# *****************************************************************************
import _jpype
__all__ = ["JProxy", "JImplements"]
# FIXME the java.lang.method we are overriding should be passes to the lookup function
# so we can properly handle name mangling on the override.
def _checkInterfaceOverrides(interfaces, overrides):
# Verify all methods are overriden
for interface in interfaces:
for method in interface.class_.getMethods():
if method.getModifiers() & 1024 == 0:
continue
if not str(method.getName()) in overrides:
raise NotImplementedError("Interface '%s' requires method '%s' to be implemented." % (
interface.class_.getName(), method.getName()))
def _classOverrides(cls):
# Find all class defined overrides
overrides = {}
for k, v in cls.__dict__.items():
try:
attr = object.__getattribute__(v, "__joverride__")
overrides[k] = (v, attr)
except AttributeError:
pass
return overrides
def _prepareInterfaces(cls, intf):
# Convert the interfaces list
actualIntf = _convertInterfaces(intf)
overrides = _classOverrides(cls)
_checkInterfaceOverrides(actualIntf, overrides)
return actualIntf
def _createJProxyDeferred(cls, *intf):
""" (internal) Create a proxy from a Python class with
@JOverride notation on methods evaluated at first
instantiation.
"""
if not isinstance(cls, type):
raise TypeError("JImplements only applies to types, not %s" % (type(cls)))
def new(tp, *args, **kwargs):
# Attach a __jpype_interfaces__ attribute to this class if
# one doesn't already exist.
actualIntf = getattr(tp, "__jpype_interfaces__", None)
if actualIntf is None:
actualIntf = _prepareInterfaces(cls, intf)
tp.__jpype_interfaces__ = actualIntf
return _jpype._JProxy.__new__(tp, None, actualIntf)
members = {'__new__': new}
# Return the augmented class
return type("proxy.%s" % cls.__name__, (cls, _jpype._JProxy), members)
def _createJProxy(cls, *intf):
""" (internal) Create a proxy from a Python class with
@JOverride notation on methods evaluated at declaration.
"""
if not isinstance(cls, type):
raise TypeError("JImplements only applies to types, not %s" % (type(cls)))
actualIntf = _prepareInterfaces(cls, intf)
def new(tp, *args, **kwargs):
self = _jpype._JProxy.__new__(tp, None, actualIntf)
tp.__init__(self, *args, **kwargs)
return self
members = {'__new__': new}
# Return the augmented class
return type("proxy.%s" % cls.__name__, (cls, _jpype._JProxy), members)
def JImplements(*interfaces, deferred=False, **kwargs):
""" Annotation for creating a new proxy that implements one or more
Java interfaces.
This annotation is placed on an ordinary Python class. The annotation
requires a list of interfaces. It must implement all of the java
methods for each of the interfaces. Each implemented method
should have a @JOverride annotation. The JVM must be running in
order to validate the class.
Args:
interfaces (str*,JClass*): Strings or JClasses for each Java interface
this proxy is to implement.
Kwargs:
deferred (bool):
Whether to defer validation of the interfaces and overrides until
the first instance instantiation (True) or validate at declaration
(False). Deferred validation allows a proxy class to be declared prior
to starting the JVM. Validation only occurs once per proxy class,
thus there is no performance penalty. Default False.
Example:
.. code-block:: python
@JImplement("java.lang.Runnable")
class MyImpl(object):
@JOverride
def run(self, arg):
pass
@JImplement("org.my.Interface1", "org.my.Interface2")
class MyImpl(object):
@JOverride
def method(self, arg):
pass
"""
if deferred:
def JProxyCreator(cls):
return _createJProxyDeferred(cls, *interfaces, **kwargs)
else:
def JProxyCreator(cls):
return _createJProxy(cls, *interfaces, **kwargs)
return JProxyCreator
def _convertInterfaces(intf):
""" (internal) Convert a list of interface names into
a list of interfaces suitable for a proxy.
"""
# Flatten the list
intflist = []
for item in intf:
if isinstance(item, str) or not hasattr(item, '__iter__'):
intflist.append(item)
else:
intflist.extend(item)
# Look up the classes if given as a string
actualIntf = set()
for item in intflist:
if isinstance(item, str):
actualIntf.add(_jpype.JClass(item))
else:
actualIntf.add(item)
# Check that all are interfaces
if not actualIntf:
raise TypeError("At least one Java interface must be specified")
for cls in actualIntf:
# If it isn't a JClass, then it cannot be a Java interface
if not isinstance(cls, _jpype.JClass):
raise TypeError("'%s' is not a Java interface" %
type(cls).__name__)
# Java concrete and abstract classes cannot be proxied
if not issubclass(cls, _jpype.JInterface):
raise TypeError("'%s' is not a Java interface" % cls.__name__)
return tuple(actualIntf)
class _JFromDict(object):
def __init__(self, dict):
self.dict = dict
def __getattribute__(self, name):
try:
return object.__getattribute__(self, 'dict')[name]
except KeyError:
pass
raise AttributeError("attribute not found")
class JProxy(_jpype._JProxy):
""" Define a proxy for a Java interface.
This is an older style JPype proxy interface that uses either a
dictionary or an object instance to implement methods defined
in java. The python object can be held by java and its lifespan
will continue as long as java holds a reference to the object
instance. New code should use ``@JImplements`` annotation as
it will support improved type safety and error handling.
Name lookups can either made using a dictionary or an object
instance. One of these two options must be specified.
Args:
intf: either a single interface or a list of java interfaces.
The interfaces can either be defined by strings or
JClass instance. Only interfaces may be used in a
proxy,
dict (dict[string, callable], optional): specifies a dictionary
containing the methods to be called when executing the
java interface methods.
inst (object, optional): specifies an object with methods
whose names matches the java interfaces methods.
"""
def __new__(cls, intf, dict=None, inst=None, convert=False):
# Convert the interfaces
actualIntf = _convertInterfaces([intf])
# Verify that one of the options has been selected
if dict is not None and inst is not None:
raise TypeError("Specify only one of dict and inst")
if dict is not None:
return _jpype._JProxy(_JFromDict(dict), actualIntf, convert)
if inst is not None:
return _jpype._JProxy.__new__(cls, inst, actualIntf, convert)
raise TypeError("a dict or inst must be specified")
@staticmethod
def unwrap(obj):
if not isinstance(obj, _jpype._JProxy):
return obj
return obj.__javainst__
|