| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 0826fc0 commit bf389cf
3 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -198,12 +198,15 @@ internal static Type CreateDerivedType(string name, | |||
| 198 | 198 | ||
| 199 | 199 | // Override any properties explicitly overridden in python | |
| 200 | 200 | var pyProperties = new HashSet<string>(); | |
| 201 | + var dictKeys = new HashSet<string>(); | ||
| 201 | 202 | if (py_dict != null && Runtime.PyDict_Check(py_dict)) | |
| 202 | 203 | { | |
| 203 | 204 | using var dict = new PyDict(py_dict); | |
| 204 | 205 | using var keys = dict.Keys(); | |
| 205 | 206 | foreach (PyObject pyKey in keys) | |
| 206 | 207 | { | |
| 208 | + var keyString = pyKey.As<string>(); | ||
| 209 | + dictKeys.Add(keyString); | ||
| 207 | 210 | using var value = dict[pyKey]; | |
| 208 | 211 | if (value.HasAttr("_clr_property_type_")) | |
| 209 | 212 | { | |
@@ -239,11 +242,18 @@ internal static Type CreateDerivedType(string name, | |||
| 239 | 242 | continue; | |
| 240 | 243 | } | |
| 241 | 244 | ||
| 245 | + // if the name of the method is not in the dict keys, then the method is not explicitly | ||
| 246 | + // declared in the python code and we dont need to add it here. | ||
| 247 | + bool isDeclared = dictKeys.Contains(method.Name); | ||
| 248 | + if (!isDeclared) | ||
| 249 | + continue; | ||
| 250 | + | ||
| 242 | 251 | // keep track of the virtual methods redirected to the python instance | |
| 243 | 252 | virtualMethods.Add(method.Name); | |
| 244 | 253 | ||
| 254 | + | ||
| 245 | 255 | // override the virtual method to call out to the python method, if there is one. | |
| 246 | - AddVirtualMethod(method, baseType, typeBuilder); | ||
| 256 | + AddVirtualMethod(method, baseType, typeBuilder, isDeclared); | ||
| 247 | 257 | } | |
| 248 | 258 | ||
| 249 | 259 | // Add any additional methods and properties explicitly exposed from Python. | |
@@ -271,35 +281,43 @@ internal static Type CreateDerivedType(string name, | |||
| 271 | 281 | } | |
| 272 | 282 | } | |
| 273 | 283 | ||
| 274 | - // add the destructor so the python object created in the constructor gets destroyed | ||
| 275 | - MethodBuilder methodBuilder = typeBuilder.DefineMethod("Finalize", | ||
| 276 | - MethodAttributes.Family | | ||
| 277 | - MethodAttributes.Virtual | | ||
| 278 | - MethodAttributes.HideBySig, | ||
| 279 | - CallingConventions.Standard, | ||
| 280 | - typeof(void), | ||
| 281 | - Type.EmptyTypes); | ||
| 282 | - ILGenerator il = methodBuilder.GetILGenerator(); | ||
| 283 | - il.Emit(OpCodes.Ldarg_0); | ||
| 284 | + | ||
| 285 | + // only add finalizer if it has not allready been added on a base type. | ||
| 286 | + // otherwise PyFinalize will be called multiple times for the same object, | ||
| 287 | + // causing an access violation exception on some platforms. | ||
| 288 | + // to see if this is the case, we can check if the base type is a IPythonDerivedType if so, it already | ||
| 289 | + // has the finalizer. | ||
| 290 | + if (typeof(IPythonDerivedType).IsAssignableFrom(baseType) == false) | ||
| 291 | + { | ||
| 292 | + // add the destructor so the python object created in the constructor gets destroyed | ||
| 293 | + MethodBuilder methodBuilder = typeBuilder.DefineMethod("Finalize", | ||
| 294 | + MethodAttributes.Family | | ||
| 295 | + MethodAttributes.Virtual | | ||
| 296 | + MethodAttributes.HideBySig, | ||
| 297 | + CallingConventions.Standard, | ||
| 298 | + typeof(void), | ||
| 299 | + Type.EmptyTypes); | ||
| 300 | + ILGenerator il = methodBuilder.GetILGenerator(); | ||
| 301 | + il.Emit(OpCodes.Ldarg_0); | ||
| 284 | 302 | #pragma warning disable CS0618 // PythonDerivedType is for internal use only | |
| 285 | - il.Emit(OpCodes.Call, typeof(PythonDerivedType).GetMethod(nameof(PyFinalize))); | ||
| 303 | + il.Emit(OpCodes.Call, typeof(PythonDerivedType).GetMethod(nameof(PyFinalize))); | ||
| 286 | 304 | #pragma warning restore CS0618 // PythonDerivedType is for internal use only | |
| 287 | - il.Emit(OpCodes.Ldarg_0); | ||
| 288 | - il.Emit(OpCodes.Call, baseClass.GetMethod("Finalize", BindingFlags.NonPublic | BindingFlags.Instance)); | ||
| 289 | - il.Emit(OpCodes.Ret); | ||
| 305 | + il.Emit(OpCodes.Ldarg_0); | ||
| 306 | + il.Emit(OpCodes.Call, baseClass.GetMethod("Finalize", BindingFlags.NonPublic | BindingFlags.Instance)); | ||
| 307 | + il.Emit(OpCodes.Ret); | ||
| 308 | + } | ||
| 290 | 309 | ||
| 291 | 310 | Type type = typeBuilder.CreateType(); | |
| 292 | 311 | ||
| 293 | - // scan the assembly so the newly added class can be imported | ||
| 312 | + // scan the assembly so the newly added class can be imported. | ||
| 294 | 313 | Assembly assembly = Assembly.GetAssembly(type); | |
| 295 | 314 | AssemblyManager.ScanAssembly(assembly); | |
| 296 | 315 | ||
| 297 | - // FIXME: assemblyBuilder not used | ||
| 298 | - AssemblyBuilder assemblyBuilder = assemblyBuilders[assemblyName]; | ||
| 299 | - | ||
| 300 | 316 | return type; | |
| 301 | 317 | } | |
| 302 | 318 | ||
| 319 | + | ||
| 320 | + | ||
| 303 | 321 | /// <summary> | |
| 304 | 322 | /// Add a constructor override that calls the python ctor after calling the base type constructor. | |
| 305 | 323 | /// </summary> | |
@@ -368,7 +386,8 @@ private static void AddConstructor(ConstructorInfo ctor, Type baseType, TypeBuil | |||
| 368 | 386 | /// <param name="method">virtual method to be overridden</param> | |
| 369 | 387 | /// <param name="baseType">Python callable object</param> | |
| 370 | 388 | /// <param name="typeBuilder">TypeBuilder for the new type the method is to be added to</param> | |
| 371 | - private static void AddVirtualMethod(MethodInfo method, Type baseType, TypeBuilder typeBuilder) | ||
| 389 | + /// <param name="isDeclared"></param> | ||
| 390 | + private static void AddVirtualMethod(MethodInfo method, Type baseType, TypeBuilder typeBuilder, bool isDeclared) | ||
| 372 | 391 | { | |
| 373 | 392 | ParameterInfo[] parameters = method.GetParameters(); | |
| 374 | 393 | Type[] parameterTypes = (from param in parameters select param.ParameterType).ToArray(); | |
@@ -720,12 +739,15 @@ public class PythonDerivedType | |||
| 720 | 739 | { | |
| 721 | 740 | var disposeList = new List<PyObject>(); | |
| 722 | 741 | PyGILState gs = Runtime.PyGILState_Ensure(); | |
| 742 | + | ||
| 743 | + | ||
| 723 | 744 | try | |
| 724 | 745 | { | |
| 725 | 746 | using var pyself = new PyObject(self.CheckRun()); | |
| 726 | 747 | using PyObject method = pyself.GetAttr(methodName, Runtime.None); | |
| 727 | 748 | if (method.Reference != Runtime.PyNone) | |
| 728 | 749 | { | |
| 750 | + | ||
| 729 | 751 | // if the method hasn't been overridden then it will be a managed object | |
| 730 | 752 | ManagedType? managedMethod = ManagedType.GetManagedObject(method.Reference); | |
| 731 | 753 | if (null == managedMethod) | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -70,5 +70,10 @@ public static void TestObject(object obj) | |||
| 70 | 70 | throw new Exception("Expected ISayHello and SimpleClass instance"); | |
| 71 | 71 | } | |
| 72 | 72 | } | |
| 73 | + | ||
| 74 | + public virtual string SayGoodbye() | ||
| 75 | + { | ||
| 76 | + return "!"; | ||
| 77 | + } | ||
| 73 | 78 | } | |
| 74 | 79 | } | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -339,8 +339,42 @@ class OverloadingSubclass2(OverloadingSubclass): | |||
| 339 | 339 | assert obj.VirtMethod[int](5) == 5 | |
| 340 | 340 | ||
| 341 | 341 | def test_implement_interface_and_class(): | |
| 342 | + import clr | ||
| 343 | + class DualSubClass0(ISayHello1, SimpleClass): | ||
| 344 | + __namespace__ = "Test0" | ||
| 345 | + def SayHello(self): | ||
| 346 | + return "hello" | ||
| 347 | + | ||
| 348 | + @clr.clrmethod(str, []) | ||
| 349 | + def SayGoodbye(self): | ||
| 350 | + return "bye" + super().SayGoodbye() | ||
| 351 | + | ||
| 352 | + def test_multi_level_subclass(): | ||
| 353 | + """ | ||
| 354 | + Test multi levels of subclassing. This has shown verious issues, like stack overflow | ||
| 355 | + exception if a method was not implemented in the middle of the tree. | ||
| 356 | + """ | ||
| 357 | + import clr | ||
| 342 | 358 | class DualSubClass0(ISayHello1, SimpleClass): | |
| 343 | 359 | __namespace__ = "Test" | |
| 344 | 360 | def SayHello(self): | |
| 345 | 361 | return "hello" | |
| 346 | - obj = DualSubClass0() | ||
| 362 | + def SayGoodbye(self): | ||
| 363 | + return "bye" + super().SayGoodbye() | ||
| 364 | + class DualSubClass1(DualSubClass0): | ||
| 365 | + __namespace__ = "Test" | ||
| 366 | + def SayHello(self): | ||
| 367 | + return super().SayHello() + " hi1" | ||
| 368 | + class DualSubClass2(DualSubClass1): | ||
| 369 | + __namespace__ = "Test" | ||
| 370 | + class DualSubClass3(DualSubClass2): | ||
| 371 | + __namespace__ = "Test" | ||
| 372 | + def SayHello(self): | ||
| 373 | + return super().SayHello() + " hi3" | ||
| 374 | + def SayGoodbye(self): | ||
| 375 | + return super().SayGoodbye() + "!" | ||
| 376 | + obj = DualSubClass3() | ||
| 377 | + helloResult = obj.SayHello() | ||
| 378 | + goodByeResult = obj.SayGoodbye() | ||
| 379 | + assert goodByeResult =="bye!!" | ||
| 380 | + assert helloResult == "hello hi1 hi3" | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments