-
Notifications
You must be signed in to change notification settings - Fork 149
Expand file tree
/
Copy path__graalpython__.py
More file actions
242 lines (205 loc) · 10.5 KB
/
__graalpython__.py
File metadata and controls
242 lines (205 loc) · 10.5 KB
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
239
240
241
242
# Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved.
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
#
# The Universal Permissive License (UPL), Version 1.0
#
# Subject to the condition set forth below, permission is hereby granted to any
# person obtaining a copy of this software, associated documentation and/or
# data (collectively the "Software"), free of charge and under any and all
# copyright rights in the Software, and any and all patent rights owned or
# freely licensable by each licensor hereunder covering either (i) the
# unmodified Software as contributed to or provided by such licensor, or (ii)
# the Larger Works (as defined below), to deal in both
#
# (a) the Software, and
#
# (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
# one is included with the Software each a "Larger Work" to which the Software
# is contributed by such licensors),
#
# without restriction, including without limitation the rights to copy, create
# derivative works of, display, perform, and distribute the Software and make,
# use, sell, offer for sale, import, export, have made, and have sold the
# Software and the Larger Work(s), and to sublicense the foregoing rights on
# either these or other terms.
#
# This license is subject to the following condition:
#
# The above copyright notice and either this complete permission notice or at a
# minimum a reference to the UPL must be included in all copies or substantial
# portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
import sys
@builtin
def import_current_as_named_module(module, name, owner_globals=None):
"""
load a builtin anonymous module which does not have a Truffle land builtin module counter part
:param name: the module name to load as
:param owner_globals: the module globals (to update)
:return: the loaded module
"""
if owner_globals is None:
owner_globals = {}
module = sys.modules.get(name, None)
if not module:
module = type(sys)(name)
sys.modules[name] = module
new_globals = dict(**owner_globals)
new_globals.update(**module.__dict__)
module.__dict__.update(**new_globals)
return module
@builtin
def lazy_attributes_from_delegate(module, delegate_name, attributes, owner_module):
"""
used to lazily load attributes defined in another module via the __getattr__ mechanism.
This will only cache the attributes in the caller module.
:param delegate_name: the delegate module
:param attributes: a list of attributes names to be loaded lazily from the delegate module
:param owner_module: the owner module (where this is called from)
:param on_import_error: a dict of replacement attributes in case of import error
:return:
"""
def __getattr__(attr):
if attr in attributes:
delegate_module = __import__(delegate_name)
new_globals = dict(**delegate_module.__dict__)
new_globals = { key: new_globals[key] for key in attributes }
new_globals.update(**owner_module.__dict__)
owner_module.__dict__.update(**new_globals)
if '__getattr__' in owner_module.__dict__:
del owner_module.__dict__['__getattr__']
return getattr(delegate_module, attr)
raise AttributeError("module '{}' does not have '{}' attribute".format(delegate_name, attr))
owner_module.__dict__['__getattr__'] = __getattr__
@builtin
def import_current_as_named_module_with_delegate(module, module_name, delegate_name, delegate_attributes, owner_globals=None):
owner_module = import_current_as_named_module(module_name, owner_globals=owner_globals)
lazy_attributes_from_delegate(delegate_name, delegate_attributes, owner_module)
@builtin
def build_java_class(module, ns, name, base, new_style=True):
if new_style:
return build_new_style_java_class(ns, name, base)
import warnings
warnings.warn("Subclassing Java classes is going to change "
"to a new instance layout that is hopefully "
"more intuitive. Pass the keyword new_style=True "
"to your class definition to try the new style. "
"The new style will become the default in the next "
"release and the old style will be removed soon after.", DeprecationWarning, 1)
ns['__super__'] = None # place where store the original java class when instance is created
ExtenderClass = type("PythonJavaExtenderClass", (object, ), ns)
HostAdapter = __graalpython__.extend(base)
resultClass = type(name, (object, ), {})
def factory (cls, *args):
# creates extender object and store the super java class
extenderInstance = ExtenderClass()
args = args[1:] + (extenderInstance, ) # remove the class and add the extender instance object
hostObject = HostAdapter(*args) # create new adapter
extenderInstance.__super__ = __graalpython__.super(hostObject) #set the super java object
return hostObject
resultClass.__new__ = classmethod(factory)
return resultClass
_CUSTOM_JAVA_SUBCLASS_BACKSTOPS = {}
@builtin
def build_new_style_java_class(module, ns, name, base):
import polyglot
import types
JavaClass = __graalpython__.extend(base)
if JavaClass not in _CUSTOM_JAVA_SUBCLASS_BACKSTOPS:
class MroClass:
def __getattr__(self, name):
sentinel = object()
# An attribute access on the Java instance failed, check the
# delegate and then the static Java members
result = getattr(self.this, name, sentinel)
if result is sentinel:
return getattr(self.getClass().static, name)
else:
return result
def __setattr__(self, name, value):
# An attribute access on the Java instance failed, use the delegate
setattr(self.this, name, value)
def __delattr__(self, name):
# An attribute access on the Java instance failed, use the delegate
delattr(self.this, name)
# This may race, so we allow_method_overwrites, at the only danger to
# insert a few useless classes into the MRO
polyglot.register_interop_type(JavaClass, MroClass, allow_method_overwrites=True)
# A class to make sure that the returned Python class can be used for
# issubclass and isinstance checks with the Java instances, and to wrap all
# methods in created subclasses to provide the proper `self` (the Java
# instance) and still make `super()` work.
class JavaSubclassMeta(type):
@property
def __bases__(self):
return (JavaClass,)
def __instancecheck__(cls, obj):
return isinstance(obj, JavaClass)
def __subclasscheck__(cls, derived):
return cls is derived or issubclass(derived, JavaClass)
def __call__(cls, *args, **kwds):
java_object = cls.__new__(cls, *args, **kwds)
java_object.this.__init__(*args, **kwds)
return java_object
def __new__(mcls, name, bases, namespace):
if bases:
new_class = None
class custom_super():
def __init__(self, start_type=None, object_or_type=None):
assert start_type is None and object_or_type is None, "super() calls in Python class inheriting from Java must not receive arguments"
f = sys._getframe(1)
self.self = f.f_locals[f.f_code.co_varnames[0]]
def __getattribute__(self, name):
if name == "__class__":
return __class__
if name == "self":
return object.__getattribute__(self, "self")
for t in new_class.mro()[1:]:
if t == DelegateSuperclass:
break
if name in t.__dict__:
value = t.__dict__[name]
if get := getattr(value, "__get__", None):
return get(self.self.this)
return value
return getattr(__graalpython__.super(self.self), name)
# Wrap all methods so that the `self` inside is always a Java object, and
# adapt the globals in the functions to provide a custom super() if
# necessary
def self_as_java_wrapper(k, value):
if type(value) is not types.FunctionType:
return value
if k in ("__new__", "__class_getitem__"):
return value
if "super" in value.__code__.co_names:
value = types.FunctionType(
value.__code__,
value.__globals__ | {"super": custom_super},
name=value.__name__,
argdefs=value.__defaults__,
closure=value.__closure__,
)
return lambda self, *args, **kwds: value(self.__this__, *args, **kwds)
namespace = {k: self_as_java_wrapper(k, v) for k, v in namespace.items()}
new_class = type.__new__(mcls, name, bases, namespace)
return new_class
return type.__new__(mcls, name, bases, namespace)
def __getattr__(self, name):
return getattr(JavaClass, name)
# A class that defines the required construction for the Java instances, so
# the Python code can actually override __new__ to affect the construction
# of the Java object
class DelegateSuperclass(metaclass=JavaSubclassMeta):
def __new__(cls, *args, **kwds):
delegate = object.__new__(cls)
java_object = polyglot.__new__(JavaClass, *(args + (delegate,)))
delegate.__this__ = java_object
return java_object
return type(name, (DelegateSuperclass,), ns)