-
Notifications
You must be signed in to change notification settings - Fork 312
Expand file tree
/
Copy pathPythonModule.cs
More file actions
executable file
·406 lines (337 loc) · 15 KB
/
PythonModule.cs
File metadata and controls
executable file
·406 lines (337 loc) · 15 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
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the Apache 2.0 License.
// See the LICENSE file in the project root for more information.
using System.Linq.Expressions;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Dynamic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Threading;
using Microsoft.Scripting;
using Microsoft.Scripting.Ast;
using Microsoft.Scripting.Runtime;
using IronPython.Runtime.Operations;
using IronPython.Runtime.Types;
using IronPython.Runtime.Binding;
namespace IronPython.Runtime {
/// <summary>
/// Python module. Stores classes, functions, and data. Usually a module
/// is created by importing a file or package from disk. But a module can also
/// be directly created by calling the module type and providing a name or
/// optionally a documentation string.
/// </summary>
[PythonType("module"), DebuggerTypeProxy(typeof(PythonModule.DebugProxy)), DebuggerDisplay("module: {GetName()}")]
public class PythonModule : IDynamicMetaObjectProvider, IPythonMembersList {
private readonly PythonDictionary _dict;
private Scope _scope;
private bool _cleanedUp = false;
public PythonModule() {
_dict = new PythonDictionary();
if (GetType() != typeof(PythonModule) && this is IPythonObject) {
// we should share the user dict w/ our dict.
((IPythonObject)this).ReplaceDict(_dict);
}
}
/// <summary>
/// Creates a new module backed by a Scope. Used for creating modules for foreign Scope's.
/// </summary>
internal PythonModule(PythonContext context, Scope scope) {
_dict = new PythonDictionary(new ScopeDictionaryStorage(context, scope));
_scope = scope;
}
/// <summary>
/// Creates a new PythonModule with the specified dictionary.
///
/// Used for creating modules for builtin modules which don't have any code associated with them.
/// </summary>
internal PythonModule(PythonDictionary dict) {
_dict = dict;
}
public static PythonModule/*!*/ __new__(CodeContext/*!*/ context, PythonType/*!*/ cls, params object[]/*!*/ args\u00F8) {
PythonModule res;
if (cls == TypeCache.Module) {
res = new PythonModule();
} else if (cls.IsSubclassOf(TypeCache.Module)) {
res = (PythonModule)cls.CreateInstance(context);
} else {
throw PythonOps.TypeError("{0} is not a subtype of module", cls.Name);
}
return res;
}
[StaticExtensionMethod]
public static PythonModule/*!*/ __new__(CodeContext/*!*/ context, PythonType/*!*/ cls, [ParamDictionary]IDictionary<object, object> kwDict0, params object[]/*!*/ args\u00F8) {
return __new__(context, cls, args\u00F8);
}
public void __init__(string name) {
__init__(name, null);
}
public void __init__(string name, string doc) {
_dict["__name__"] = name;
_dict["__doc__"] = doc;
}
public object __getattribute__(CodeContext/*!*/ context, string name) {
PythonTypeSlot slot;
object res;
if (GetType() != typeof(PythonModule) &&
DynamicHelpers.GetPythonType(this).TryResolveSlot(context, name, out slot) &&
slot.TryGetValue(context, this, DynamicHelpers.GetPythonType(this), out res)) {
return res;
}
switch (name) {
// never look in the dict for these...
case "__dict__": return __dict__;
case "__class__": return DynamicHelpers.GetPythonType(this);
}
if (_dict.TryGetValue(name, out res)) {
return res;
}
// fall back to object to provide all of our other attributes (e.g. __setattr__, etc...)
return ObjectOps.__getattribute__(context, this, name);
}
internal object GetAttributeNoThrow(CodeContext/*!*/ context, string name) {
PythonTypeSlot slot;
object res;
if (GetType() != typeof(PythonModule) &&
DynamicHelpers.GetPythonType(this).TryResolveSlot(context, name, out slot) &&
slot.TryGetValue(context, this, DynamicHelpers.GetPythonType(this), out res)) {
return res;
}
switch (name) {
// never look in the dict for these...
case "__dict__": return __dict__;
case "__class__": return DynamicHelpers.GetPythonType(this);
}
if (_dict.TryGetValue(name, out res)) {
return res;
} else if (DynamicHelpers.GetPythonType(this).TryGetNonCustomMember(context, this, name, out res)) {
return res;
} else if (DynamicHelpers.GetPythonType(this).TryResolveSlot(context, "__getattr__", out slot) &&
slot.TryGetValue(context, this, DynamicHelpers.GetPythonType(this), out res)) {
return PythonCalls.Call(context, res, name);
}
return OperationFailed.Value;
}
public void __setattr__(CodeContext/*!*/ context, string name, object value) {
PythonTypeSlot slot;
if (GetType() != typeof(PythonModule) &&
DynamicHelpers.GetPythonType(this).TryResolveSlot(context, name, out slot) &&
slot.TrySetValue(context, this, DynamicHelpers.GetPythonType(this), value)) {
return;
}
switch (name) {
case "__dict__": throw PythonOps.AttributeError("readonly attribute");
case "__class__": throw PythonOps.TypeError("__class__ assignment: only for heap types");
}
Debug.Assert(value != Uninitialized.Instance);
_dict[name] = value;
}
public void __delattr__(CodeContext/*!*/ context, string name) {
PythonTypeSlot slot;
if (GetType() != typeof(PythonModule) &&
DynamicHelpers.GetPythonType(this).TryResolveSlot(context, name, out slot) &&
slot.TryDeleteValue(context, this, DynamicHelpers.GetPythonType(this))) {
return;
}
switch (name) {
case "__dict__": throw PythonOps.AttributeError("readonly attribute");
case "__class__": throw PythonOps.TypeError("can't delete __class__ attribute");
}
object value;
if (!_dict.TryRemoveValue(name, out value)) {
throw PythonOps.AttributeErrorForMissingAttribute("module", name);
}
}
public string/*!*/ __repr__() {
return __str__();
}
public string/*!*/ __str__() {
object fileObj, nameObj;
if (!_dict.TryGetValue("__file__", out fileObj)) {
fileObj = null;
}
if (!_dict._storage.TryGetName(out nameObj)) {
nameObj = null;
}
string file = fileObj as string;
string name = nameObj as string ?? "?";
if (file == null) {
return String.Format("<module '{0}' (built-in)>", name);
}
return String.Format("<module '{0}' from '{1}'>", name, file);
}
internal PythonDictionary __dict__ {
get {
return _dict;
}
}
[SpecialName, PropertyMethod]
public PythonDictionary Get__dict__() {
return _dict;
}
[SpecialName, PropertyMethod]
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")]
public void Set__dict__(object value) {
throw PythonOps.AttributeError("readonly attribute");
}
[SpecialName, PropertyMethod]
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")]
public void Delete__dict__() {
throw PythonOps.AttributeError("readonly attribute");
}
public Scope Scope {
get {
if (_scope == null) {
Interlocked.CompareExchange(ref _scope, new Scope(new ObjectDictionaryExpando(_dict)), null);
}
return _scope;
}
}
#region IDynamicMetaObjectProvider Members
[PythonHidden] // needs to be public so that we can override it.
public DynamicMetaObject GetMetaObject(Expression parameter) {
return new MetaModule(this, parameter);
}
#endregion
private class MetaModule : MetaPythonObject, IPythonGetable {
public MetaModule(PythonModule module, Expression self)
: base(self, BindingRestrictions.Empty, module) {
}
public override DynamicMetaObject BindGetMember(GetMemberBinder binder) {
return GetMemberWorker(binder, PythonContext.GetCodeContextMO(binder));
}
#region IPythonGetable Members
public DynamicMetaObject GetMember(PythonGetMemberBinder member, DynamicMetaObject codeContext) {
return GetMemberWorker(member, codeContext);
}
#endregion
private DynamicMetaObject GetMemberWorker(DynamicMetaObjectBinder binder, DynamicMetaObject codeContext) {
string name = GetGetMemberName(binder);
var tmp = Expression.Variable(typeof(object), "res");
return new DynamicMetaObject(
Expression.Block(
new[] { tmp },
Expression.Condition(
Expression.Call(
typeof(PythonOps).GetMethod(nameof(PythonOps.ModuleTryGetMember)),
PythonContext.GetCodeContext(binder),
Utils.Convert(Expression, typeof(PythonModule)),
Expression.Constant(name),
tmp
),
tmp,
Expression.Convert(GetMemberFallback(this, binder, codeContext).Expression, typeof(object))
)
),
BindingRestrictions.GetTypeRestriction(Expression, Value.GetType())
);
}
public override DynamicMetaObject/*!*/ BindInvokeMember(InvokeMemberBinder/*!*/ action, DynamicMetaObject/*!*/[]/*!*/ args) {
return BindingHelpers.GenericInvokeMember(action, null, this, args);
}
public override DynamicMetaObject BindSetMember(SetMemberBinder binder, DynamicMetaObject value) {
Debug.Assert(value.Value != Uninitialized.Instance);
return new DynamicMetaObject(
Expression.Block(
Expression.Call(
Utils.Convert(Expression, typeof(PythonModule)),
typeof(PythonModule).GetMethod(nameof(PythonModule.__setattr__)),
PythonContext.GetCodeContext(binder),
Expression.Constant(binder.Name),
Expression.Convert(value.Expression, typeof(object))
),
Expression.Convert(value.Expression, typeof(object))
),
BindingRestrictions.GetTypeRestriction(Expression, Value.GetType())
);
}
public override DynamicMetaObject BindDeleteMember(DeleteMemberBinder binder) {
return new DynamicMetaObject(
Expression.Call(
Utils.Convert(Expression, typeof(PythonModule)),
typeof(PythonModule).GetMethod(nameof(PythonModule.__delattr__)),
PythonContext.GetCodeContext(binder),
Expression.Constant(binder.Name)
),
BindingRestrictions.GetTypeRestriction(Expression, Value.GetType())
);
}
public override IEnumerable<string> GetDynamicMemberNames() {
foreach (object o in ((PythonModule)Value).__dict__.Keys) {
if (o is string str) {
yield return str;
}
}
}
}
internal string GetFile() {
object res;
if (_dict.TryGetValue("__file__", out res)) {
return res as string;
}
return null;
}
internal string GetName() {
object res;
if (_dict._storage.TryGetName(out res)) {
return res as string;
}
return null;
}
#region IPythonMembersList Members
IList<object> IPythonMembersList.GetMemberNames(CodeContext context) {
return new List<object>(__dict__.Keys);
}
#endregion
#region IMembersList Members
IList<string> IMembersList.GetMemberNames() {
List<string> res = new List<string>(__dict__.Keys.Count);
foreach (object o in __dict__.Keys) {
if (o is string strKey) {
res.Add(strKey);
}
}
return res;
}
#endregion
internal class DebugProxy {
private readonly PythonModule _module;
public DebugProxy(PythonModule module) {
_module = module;
}
[DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
public List<ObjectDebugView> Members {
get {
var res = new List<ObjectDebugView>();
foreach (var v in _module._dict) {
res.Add(new ObjectDebugView(v.Key, v.Value));
}
return res;
}
}
}
/// <summary>
/// Cleanup globals so that they could be garbage collected.
/// Note that it cleans python sourced modules only,
/// because C# modules are more fundamental and their globals may be required when other python object's finalizer is executing.
/// </summary>
public void Cleanup(CodeContext context) {
if (_cleanedUp) {
return;
}
_cleanedUp = true;
if (!_dict.ContainsKey("__file__")) {
return; // not from Python source, skip clean up
}
foreach (var key in _dict.Keys.ToList()) {
var obj = _dict[key];
if (obj is PythonModule module) {
module.Cleanup(context);
} else if (key is string name) {
__delattr__(context, name);
}
}
}
}
}