8000 Rework the serialization of reflected types · pythonnet/pythonnet@3dad96e · GitHub
[go: up one dir, main page]

Skip to content

Commit 3dad96e

Browse files
committed
Rework the serialization of reflected types
Serialization of System.Type, MemberInfo and MethodBase is now string based. At deserialization, use reflection to attempt to recreate the object, which may fail safely instead of throwing a SerializaitonException during the deserialization of the whoie data stream. Appropriate Exceptions will now be raised when the Maybe*'s Value property. ClasseBase objects are now de-initialized and re-initialized in Reload mode so that it's tp_dict picks up newly added members and removed members no longer linger. ModuleObject clears it's cache and remove cached members from it's tp_dict. Minor refactoring and modernization of MethodObject and MethodBinder
1 parent 0ee931e commit 3dad96e

22 files changed

+681
-302
lines changed

src/domain_tests/test_domain_reload.py

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,47 +14,36 @@ def _run_test(testname):
1414

1515
assert proc.returncode == 0
1616

17-
@pytest.mark.xfail(reason="Issue not yet fixed.")
1817
def test_rename_class():
1918
_run_test('class_rename')
2019

21-
@pytest.mark.xfail(reason="Issue not yet fixed.")
2220
def test_rename_class_member_static_function():
2321
_run_test('static_member_rename')
2422

25-
@pytest.mark.xfail(reason="Issue not yet fixed.")
2623
def test_rename_class_member_function():
2724
_run_test('member_rename')
2825

29-
@pytest.mark.xfail(reason="Issue not yet fixed.")
3026
def test_rename_class_member_field():
3127
_run_test('field_rename')
3228

33-
@pytest.mark.xfail(reason="Issue not yet fixed.")
3429
def test_rename_class_member_property():
3530
_run_test('property_rename')
3631

37-
@pytest.mark.xfail(reason="Issue not yet fixed.")
3832
def test_rename_namespace():
3933
_run_test('namespace_rename')
4034

41-
@pytest.mark.xfail(reason="Issue not yet fixed.")
4235
def test_field_visibility_change():
4336
_run_test("field_visibility_change")
4437

45-
@pytest.mark.xfail(reason="Issue not yet fixed.")
4638
def test_method_visibility_change():
4739
_run_test("method_visibility_change")
4840

49-
@pytest.mark.xfail(reason="Issue not yet fixed.")
5041
def test_property_visibility_change():
5142
_run_test("property_visibility_change")
5243

53-
@pytest.mark.xfail(reason="Issue not yet fixed.")
5444
def test_class_visibility_change():
5545
_run_test("class_visibility_change")
5646

57-
@pytest.mark.xfail(reason="Issue not yet fixed.")
5847
def test_method_parameters_change():
5948
_run_test("method_parameters_change")
6049

src/runtime/Python.Runtime.csproj

Lines changed: 192 additions & 191 deletions
Large diffs are not rendered by default.

src/runtime/arrayobject.cs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,18 @@ internal override bool CanSubclass()
2323
public static IntPtr tp_new(IntPtr tp, IntPtr args, IntPtr kw)
2424
{
2525
var self = GetManagedObject(tp) as ArrayObject;
26+
if (!self.type.Valid)
27+
{
28+
return Exceptions.RaiseTypeError(self.type.DeletedMessage);
29+
}
2630
if (Runtime.PyTuple_Size(args) != 1)
2731
{
2832
return Exceptions.RaiseTypeError("array expects 1 argument");
2933
}
3034
IntPtr op = Runtime.PyTuple_GetItem(args, 0);
3135
object result;
3236

33-
if (!Converter.ToManaged(op, self.type, out result, true))
37+
if (!Converter.ToManaged(op, self.type.Value, out result, true))
3438
{
3539
return IntPtr.Zero;
3640
}
@@ -45,8 +49,12 @@ public static IntPtr tp_new(IntPtr tp, IntPtr args, IntPtr kw)
4549
{
4650
var obj = (CLRObject)GetManagedObject(ob);
4751
var arrObj = (ArrayObject)GetManagedObjectType(ob);
52+
if (!arrObj.type.Valid)
53+
{
54+
return Exceptions.RaiseTypeError(arrObj.type.DeletedMessage);
55+
}
4856
var items = obj.inst as Array;
49-
Type itemType = arrObj.type.GetElementType();
57+
Type itemType = arrObj.type.Value.GetElementType();
5058
int rank = items.Rank;
5159
int index;
5260
object value;

src/runtime/classbase.cs

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,18 +18,21 @@ namespace Python.Runtime
1818
[Serializable]
1919
internal class ClassBase : ManagedType
2020
{
21+
[NonSerialized]
22+
internal List<string> dotNetMembers;
2123
internal Indexer indexer;
22-
internal Type type;
24+
internal MaybeType type;
2325

2426
internal ClassBase(Type tp)
2527
{
28+
dotNetMembers = new List<string>();
2629
indexer = null;
2730
type = tp;
2831
}
2932

3033
internal virtual bool CanSubclass()
3134
{
32-
return !type.IsEnum;
35+
return !type.Value.IsEnum;
3336
}
3437

3538

@@ -44,7 +47,12 @@ public virtual IntPtr type_subscript(IntPtr idx)
4447
return Exceptions.RaiseTypeError("type(s) expected");
4548
}
4649

47-
Type target = GenericUtil.GenericForType(type, types.Length);
50+
if (!type.Valid)
51+
{
52+
return Exceptions.RaiseTypeError(type.DeletedMessage);
53+
}
54+
55+
Type target = GenericUtil.GenericForType(type.Value, types.Length);
4856

4957
if (target != null)
5058
{

src/runtime/classmanager.cs

Lines changed: 47 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ namespace Python.Runtime
1818
/// </summary>
1919
internal class ClassManager
2020
{
21-
private static Dictionary<Type, ClassBase> cache;
21+
private static Dictionary<MaybeType, ClassBase> cache;
2222
private static readonly Type dtype;
2323

2424
private ClassManager()
@@ -36,7 +36,7 @@ static ClassManager()
3636

3737
public static void Reset()
3838
{
39-
cache = new Dictionary<Type, ClassBase>(128);
39+
cache = new Dictionary<MaybeType, ClassBase>(128);
4040
}
4141

4242
internal static void DisposePythonWrappersForClrTypes()
@@ -85,26 +85,56 @@ internal static void SaveRuntimeData(RuntimeDataStorage storage)
8585
var contexts = storage.AddValue("contexts",
8686
new Dictionary<IntPtr, InterDomainContext>());
8787
storage.AddValue("cache", cache);
88-
foreach (var cls in cache.Values)
88+
foreach (var cls in cache)
8989
{
90+
if (!cls.Key.Valid)
91+
{
92+
// Don't serialize an invalid class
93+
continue;
94+
}
9095
// This incref is for cache to hold the cls,
9196
// thus no need for decreasing it at RestoreRuntimeData.
92-
Runtime.XIncref(cls.pyHandle);
93-
var context = contexts[cls.pyHandle] = new InterDomainContext();
94-
cls.Save(context);
97+
Runtime.XIncref(cls.Value.pyHandle);
98+
var context = contexts[cls.Value.pyHandle] = new InterDomainContext();
99+
cls.Value.Save(context);
100+
101+
// Remove all members added in InitBaseClass.
102+
// this is done so that if domain reloads and a member of a
103+
// reflected dotnet class is removed, it is removed from the
104+
// Python object's dictionary tool; thus raising an AttributeError
105+
// instead of a TypeError.
106+
// Classes are re-initialized on in RestoreRuntimeData.
107+
IntPtr dict = Marshal.ReadIntPtr(cls.Value.tpHandle, TypeOffset.tp_dict);
108+
foreach (var member in cls.Value.dotNetMembers)
109+
{
110+
// No need to decref the member, the ClassBase instance does
111+
// not own the reference.
112+
Runtime.PyDict_DelItemString(dict, member);
113+
}
114+
// Trying to remove a key that's not in the dictionary may
115+
// raise an error. We don't care about it.
116+
Runtime.PyErr_Clear();
95117
}
96118
}
97119

98120
internal static Dictionary<ManagedType, InterDomainContext> RestoreRuntimeData(RuntimeDataStorage storage)
99121
{
100-
cache = storage.GetValue<Dictionary<Type, ClassBase>>("cache");
122+
var _cache = storage.GetValue<Dictionary<MaybeType, ClassBase>>("cache");
101123
var contexts = storage.GetValue <Dictionary<IntPtr, InterDomainContext>>("contexts");
102124
var loadedObjs = new Dictionary<ManagedType, InterDomainContext>();
103-
foreach (var cls in cache.Values)
125+
foreach (var pair in _cache)
104126
{
105-
var context = contexts[cls.pyHandle];
106-
cls.Load(context);
107-
loadedObjs.Add(cls, context);
127+
if (!pair.Key.Valid)
128+
{
129+
Runtime.XDecref(pair.Value.pyHandle);
130+
continue;
131+
}
132+
// re-init the class
133+
InitClassBase(pair.Key.Value, pair.Value);
134+
cache.Add(pair.Key, pair.Value);
135+
var context = contexts[pair.Value.pyHandle];
136+
pair.Value.Load(context);
137+
loadedObjs.Add(pair.Value, context);
108138
}
109139
return loadedObjs;
110140
}
@@ -209,11 +239,16 @@ private static void InitClassBase(Type type, ClassBase impl)
209239
IntPtr dict = Marshal.ReadIntPtr(tp, TypeOffset.tp_dict);
210240

211241

242+
if (impl.dotNetMembers == null)
243+
{
244+
impl.dotNetMembers = new List<string>();
245+
}
212246
IDictionaryEnumerator iter = info.members.GetEnumerator();
213247
while (iter.MoveNext())
214248
{
215249
var item = (ManagedType)iter.Value;
216250
var name = (string)iter.Key;
251+
impl.dotNetMembers.Add(name);
217252
Runtime.PyDict_SetItemString(dict, name, item.pyHandle);
218253
// Decref the item now that it's been used.
219254
item.DecrRefCount();
@@ -241,7 +276,7 @@ private static void InitClassBase(Type type, ClassBase impl)
241276
// required that the ClassObject.ctors be changed to internal
242277
if (co != null)
243278
{
244-
if (co.ctors.Length > 0)
279+
if (co.NumCtors > 0)
245280
{
246281
// Implement Overloads on the class object
247282
if (!CLRModule._SuppressOverloads)

src/runtime/classobject.cs

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,14 @@ namespace Python.Runtime
1313
internal class ClassObject : ClassBase
1414
{
1515
internal ConstructorBinder binder;
16-
internal ConstructorInfo[] ctors;
16+
internal int NumCtors = 0;
1717

1818
internal ClassObject(Type tp) : base(tp)
1919
{
20-
ctors = type.GetConstructors();
21-
binder = new ConstructorBinder(type);
22-
23-
foreach (ConstructorInfo t in ctors)
20+
var _ctors = type.Value.GetConstructors();
21+
NumCtors = _ctors.Length;
22+
binder = new ConstructorBinder(type.Value);
23+
foreach (ConstructorInfo t in _ctors)
2424
{
2525
binder.AddMethod(t);
2626
}
@@ -61,7 +61,11 @@ public static IntPtr tp_new(IntPtr tp, IntPtr args, IntPtr kw)
6161
return Exceptions.RaiseTypeError("invalid object");
6262
}
6363

64-
Type type = self.type;
64+
if (!self.type.Valid)
65+
{
66+
return Exceptions.RaiseTypeError(self.type.DeletedMessage);
67+
}
68+
Type type = self.type.Value;
6569

6670
// Primitive types do not have constructors, but they look like
6771
// they do from Python. If the ClassObject represents one of the
@@ -114,16 +118,21 @@ public static IntPtr tp_new(IntPtr tp, IntPtr args, IntPtr kw)
114118
/// </summary>
115119
public override IntPtr type_subscript(IntPtr idx)
116120
{
121+
if (!type.Valid)
122+
{
123+
return Exceptions.RaiseTypeError(type.DeletedMessage);
124+
}
125+
117126
// If this type is the Array type, the [<type>] means we need to
118127
// construct and return an array type of the given element type.
119-
if (type == typeof(Array))
128+
if (type.Value == typeof(Array))
120129
{
121130
if (Runtime.PyTuple_Check(idx))
122131
{
123132
return Exceptions.RaiseTypeError("type expected");
124133
}
125134
var c = GetManagedObject(idx) as ClassBase;
126-
Type t = c != null ? c.type : Converter.GetTypeByAlias(idx);
135+
Type t = c != null ? c.type.Value : Converter.GetTypeByAlias(idx);
127136
if (t == null)
128137
{
129138
return Exceptions.RaiseTypeError("type expected");
@@ -143,7 +152,7 @@ public override IntPtr type_subscript(IntPtr idx)
143152
return Exceptions.RaiseTypeError("type(s) expected");
144153
}
145154

146-
Type gtype = AssemblyManager.LookupType($"{type.FullName}`{types.Length}");
155+
Type gtype = AssemblyManager.LookupType($"{type.Value.FullName}`{types.Length}");
147156
if (gtype != null)
148157
{
149158
var g = ClassManager.GetClass(gtype) as GenericType;

src/runtime/constructorbinder.cs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ namespace Python.Runtime
1414
[Serializable]
1515
internal class ConstructorBinder : MethodBinder
1616
{
17-
private Type _containingType;
17+
private MaybeType _containingType;
1818

1919
internal ConstructorBinder(Type containingType)
2020
{
@@ -51,10 +51,14 @@ internal object InvokeRaw(IntPtr inst, IntPtr args, IntPtr kw)
5151
/// </remarks>
5252
internal object InvokeRaw(IntPtr inst, IntPtr args, IntPtr kw, MethodBase info)
5353
{
54+
if (!_containingType.Valid)
55+
{
56+
return Exceptions.RaiseTypeError(_containingType.DeletedMessage);
57+
}
5458
object result;
5559

56-
if (_containingType.IsValueType && !_containingType.IsPrimitive &&
57-
!_containingType.IsEnum && _containingType != typeof(decimal) &&
60+
if (_containingType.Value.IsValueType && !_containingType.Value.IsPrimitive &&
61+
!_containingType.Value.IsEnum && _containingType.Value != typeof(decimal) &&
5862
Runtime.PyTuple_Size(args) == 0)
5963
{
6064
// If you are trying to construct an instance of a struct by
@@ -64,7 +68,7 @@ internal object InvokeRaw(IntPtr inst, IntPtr args, IntPtr kw, MethodBase info)
6468
// Activator.CreateInstance().
6569
try
6670
{
67-
result = Activator.CreateInstance(_containingType);
71+
result = Activator.CreateInstance(_containingType.Value);
6872
}
6973
catch (Exception e)
7074
{

src/runtime/constructorbinding.cs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ namespace Python.Runtime
2222
[Serializable]
2323
internal class ConstructorBinding : ExtensionType
2424
{
25-
private Type type; // The managed Type being wrapped in a ClassObject
25+
private MaybeType type; // The managed Type being wrapped in a ClassObject
2626
private IntPtr pyTypeHndl; // The python type tells GetInstHandle which Type to create.
2727
private ConstructorBinder ctorBinder;
2828

@@ -92,6 +92,10 @@ public static IntPtr tp_descr_get(IntPtr op, IntPtr instance, IntPtr owner)
9292
public static IntPtr mp_subscript(IntPtr op, IntPtr key)
9393
{
9494
var self = (ConstructorBinding)GetManagedObject(op);
95+
if (!self.type.Valid)
96+
{
97+
return Exceptions.RaiseTypeError(self.type.DeletedMessage);
98+
}
9599

96100
Type[] types = Runtime.PythonArgsToTypeArray(key);
97101
if (types == null)
@@ -100,12 +104,12 @@ public static IntPtr mp_subscript(IntPtr op, IntPtr key)
100104
}
101105
//MethodBase[] methBaseArray = self.ctorBinder.GetMethods();
102106
//MethodBase ci = MatchSignature(methBaseArray, types);
103-
ConstructorInfo ci = self.type.GetConstructor(types);
107+
ConstructorInfo ci = self.type.Value.GetConstructor(types);
104108
if (ci == null)
105109
{
106110
return Exceptions.RaiseTypeError("No match found for constructor signature");
107111
}
108-
var boundCtor = new BoundContructor(self.type, self.pyTypeHndl, self.ctorBinder, ci);
112+
var boundCtor = new BoundContructor(self.type.Value, self.pyTypeHndl, self.ctorBinder, ci);
109113

110114
return boundCtor.pyHandle;
111115
}
@@ -122,7 +126,7 @@ public static IntPtr tp_repr(IntPtr ob)
122126
return self.repr;
123127
}
124128
MethodBase[] methods = self.ctorBinder.GetMethods();
125-
string name = self.type.FullName;
129+
string name = self.type.Value.FullName;
126130
var doc = "";
127131
foreach (MethodBase t in methods)
128132
{

src/runtime/converter.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -344,7 +344,7 @@ internal static bool ToManagedValue(IntPtr value, Type obType,
344344
}
345345
if (mt is ClassBase)
346346
{
347-
result = ((ClassBase)mt).type;
347+
result = ((ClassBase)mt).type.Value;
348348
return true;
349349
}
350350
// shouldn't happen

0 commit comments

Comments
 (0)
0