Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion src/embed_tests/Codecs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -421,13 +421,18 @@ public PyObject TryEncode(object value)
}
}

class InstancelessExceptionDecoder : IPyObjectDecoder
class InstancelessExceptionDecoder : IPyObjectDecoder, IDisposable
{
readonly PyObject PyErr = Py.Import("clr.interop").GetAttr("PyErr");

public bool CanDecode(PyType objectType, Type targetType)
=> PythonReferenceComparer.Instance.Equals(PyErr, objectType);

public void Dispose()
{
PyErr.Dispose();
}

public bool TryDecode<T>(PyObject pyObj, out T value)
{
if (pyObj.HasAttr("value"))
Expand Down
16 changes: 16 additions & 0 deletions src/embed_tests/GlobalTestsSetup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,22 @@ namespace Python.EmbeddingTest
[SetUpFixture]
public partial class GlobalTestsSetup
{
[OneTimeSetUp]
public void GlobalSetup()
{
Finalizer.Instance.ErrorHandler += FinalizerErrorHandler;
}

private void FinalizerErrorHandler(object sender, Finalizer.ErrorArgs e)
{
if (e.Error is RuntimeShutdownException)
{
// allow objects to leak after the python runtime run
// they were created in is gone
e.Handled = true;
}
}

[OneTimeTearDown]
public void FinalCleanup()
{
Expand Down
1 change: 1 addition & 0 deletions src/embed_tests/Inheritance.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ public void SetUp()
[OneTimeTearDown]
public void Dispose()
{
ExtraBaseTypeProvider.ExtraBase.Dispose();
PythonEngine.Shutdown();
}

Expand Down
110 changes: 0 additions & 110 deletions src/embed_tests/TestDomainReload.cs
Original file line number Diff line number Diff line change
Expand Up @@ -179,116 +179,6 @@ public static void CrossDomainObject()

#endregion

#region Tempary tests

// https://github.com/pythonnet/pythonnet/pull/1074#issuecomment-596139665
[Test]
public void CrossReleaseBuiltinType()
{
void ExecTest()
{
try
{
PythonEngine.Initialize();
var numRef = CreateNumReference();
Assert.True(numRef.IsAlive);
PythonEngine.Shutdown(); // <- "run" 1 ends
PythonEngine.Initialize(); // <- "run" 2 starts

GC.Collect();
GC.WaitForPendingFinalizers(); // <- this will put former `num` into Finalizer queue
Finalizer.Instance.Collect();
// ^- this will call PyObject.Dispose, which will call XDecref on `num.Handle`,
// but Python interpreter from "run" 1 is long gone, so it will corrupt memory instead.
Assert.False(numRef.IsAlive);
}
finally
{
PythonEngine.Shutdown();
}
}

var errorArgs = new List<Finalizer.ErrorArgs>();
void ErrorHandler(object sender, Finalizer.ErrorArgs e)
{
errorArgs.Add(e);
}
Finalizer.Instance.ErrorHandler += ErrorHandler;
try
{
for (int i = 0; i < 10; i++)
{
ExecTest();
}
}
finally
{
Finalizer.Instance.ErrorHandler -= ErrorHandler;
}
Assert.AreEqual(errorArgs.Count, 0);
}

[Test]
public void CrossReleaseCustomType()
{
void ExecTest()
{
try
{
PythonEngine.Initialize();
var objRef = CreateConcreateObject();
Assert.True(objRef.IsAlive);
PythonEngine.Shutdown(); // <- "run" 1 ends
PythonEngine.Initialize(); // <- "run" 2 starts
GC.Collect();
GC.WaitForPendingFinalizers();
Finalizer.Instance.Collect();
Assert.False(objRef.IsAlive);
}
finally
{
PythonEngine.Shutdown();
}
}

var errorArgs = new List<Finalizer.ErrorArgs>();
void ErrorHandler(object sender, Finalizer.ErrorArgs e)
{
errorArgs.Add(e);
}
Finalizer.Instance.ErrorHandler += ErrorHandler;
try
{
for (int i = 0; i < 10; i++)
{
ExecTest();
}
}
finally
{
Finalizer.Instance.ErrorHandler -= ErrorHandler;
}
Assert.AreEqual(errorArgs.Count, 0);
}

private static WeakReference CreateNumReference()
{
var num = 3216757418.ToPython();
Assert.AreEqual(num.Refcount, 1);
WeakReference numRef = new WeakReference(num, false);
return numRef;
}

private static WeakReference CreateConcreateObject()
{
var obj = new Domain.MyClass().ToPython();
Assert.AreEqual(obj.Refcount, 1);
WeakReference numRef = new WeakReference(obj, false);
return numRef;
}

#endregion Tempary tests

/// <summary>
/// This is a magic incantation required to run code in an application
/// domain other than the current one.
Expand Down
16 changes: 12 additions & 4 deletions src/embed_tests/pyinitialize.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,10 @@ public static void LoadSpecificArgs()
{
using (var argv = new PyList(Runtime.Runtime.PySys_GetObject("argv")))
{
Assert.AreEqual(args[0], argv[0].ToString());
Assert.AreEqual(args[1], argv[1].ToString());
using var v0 = argv[0];
using var v1 = argv[1];
Assert.AreEqual(args[0], v0.ToString());
Assert.AreEqual(args[1], v1.ToString());
}
}
}
Expand All @@ -54,12 +56,16 @@ public void ImportClassShutdownRefcount()

PyObject ns = Py.Import(typeof(ImportClassShutdownRefcountClass).Namespace);
PyObject cls = ns.GetAttr(nameof(ImportClassShutdownRefcountClass));
BorrowedReference clsRef = cls.Reference;
#pragma warning disable CS0618 // Type or member is obsolete
cls.Leak();
#pragma warning restore CS0618 // Type or member is obsolete
ns.Dispose();

Assert.Less(cls.Refcount, 256);
Assert.Less(Runtime.Runtime.Refcount(clsRef), 256);

PythonEngine.Shutdown();
Assert.Greater(cls.Refcount, 0);
Assert.Greater(Runtime.Runtime.Refcount(clsRef), 0);
}

/// <summary>
Expand Down Expand Up @@ -176,6 +182,7 @@ public static void TestRunExitFuncs()
{
Assert.Fail(msg);
}
PythonEngine.InteropConfiguration = InteropConfiguration.MakeDefault();
return;
}
bool called = false;
Expand All @@ -187,6 +194,7 @@ public static void TestRunExitFuncs()
atexit.Dispose();
Runtime.Runtime.Shutdown();
Assert.True(called);
PythonEngine.InteropConfiguration = InteropConfiguration.MakeDefault();
}
}

Expand Down
11 changes: 10 additions & 1 deletion src/runtime/Codecs/DecoderGroup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ namespace Python.Runtime.Codecs
/// <summary>
/// Represents a group of <see cref="IPyObjectDecoder"/>s. Useful to group them by priority.
/// </summary>
public sealed class DecoderGroup: IPyObjectDecoder, IEnumerable<IPyObjectDecoder>
public sealed class DecoderGroup: IPyObjectDecoder, IEnumerable<IPyObjectDecoder>, IDisposable
{
readonly List<IPyObjectDecoder> decoders = new List<IPyObjectDecoder>();

Expand Down Expand Up @@ -46,6 +46,15 @@ public bool TryDecode<T>(PyObject pyObj, out T value)
/// <inheritdoc />
public IEnumerator<IPyObjectDecoder> GetEnumerator() => this.decoders.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => this.decoders.GetEnumerator();

public void Dispose()
{
foreach (var decoder in this.decoders.OfType<IDisposable>())
{
decoder.Dispose();
}
this.decoders.Clear();
}
}

public static class DecoderGroupExtensions
Expand Down
11 changes: 10 additions & 1 deletion src/runtime/Codecs/EncoderGroup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ namespace Python.Runtime.Codecs
/// <summary>
/// Represents a group of <see cref="IPyObjectDecoder"/>s. Useful to group them by priority.
/// </summary>
public sealed class EncoderGroup: IPyObjectEncoder, IEnumerable<IPyObjectEncoder>
public sealed class EncoderGroup: IPyObjectEncoder, IEnumerable<IPyObjectEncoder>, IDisposable
{
readonly List<IPyObjectEncoder> encoders = new List<IPyObjectEncoder>();

Expand Down Expand Up @@ -47,6 +47,15 @@ public PyObject TryEncode(object value)
/// <inheritdoc />
public IEnumerator<IPyObjectEncoder> GetEnumerator() => this.encoders.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => this.encoders.GetEnumerator();

public void Dispose()
{
foreach (var encoder in this.encoders.OfType<IDisposable>())
{
encoder.Dispose();
}
this.encoders.Clear();
}
}

public static class EncoderGroupExtensions
Expand Down
12 changes: 11 additions & 1 deletion src/runtime/InteropConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@ namespace Python.Runtime
{
using System;
using System.Collections.Generic;
using System.Linq;

using Python.Runtime.Mixins;

public sealed class InteropConfiguration
public sealed class InteropConfiguration: IDisposable
{
internal readonly PythonBaseTypeProviderGroup pythonBaseTypeProviders
= new PythonBaseTypeProviderGroup();
Expand All @@ -24,5 +25,14 @@ public static InteropConfiguration MakeDefault()
},
};
}

public void Dispose()
{
foreach (var provider in PythonBaseTypeProviders.OfType<IDisposable>())
{
provider.Dispose();
}
PythonBaseTypeProviders.Clear();
}
}
}
10 changes: 9 additions & 1 deletion src/runtime/Mixins/CollectionMixinsProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

namespace Python.Runtime.Mixins
{
class CollectionMixinsProvider : IPythonBaseTypeProvider
class CollectionMixinsProvider : IPythonBaseTypeProvider, IDisposable
{
readonly Lazy<PyObject> mixinsModule;
public CollectionMixinsProvider(Lazy<PyObject> mixinsModule)
Expand Down Expand Up @@ -86,5 +86,13 @@ static Type[] NewInterfaces(Type type)

static Type GetDefinition(Type type)
=> type.IsGenericType ? type.GetGenericTypeDefinition() : type;

public void Dispose()
{
if (this.mixinsModule.IsValueCreated)
{
this.mixinsModule.Value.Dispose();
}
}
}
}
4 changes: 2 additions & 2 deletions src/runtime/converterextensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -166,8 +166,8 @@ internal static void Reset()
{
clrToPython.Clear();
pythonToClr.Clear();
encoders.Clear();
decoders.Clear();
encoders.Dispose();
decoders.Dispose();
}
}

Expand Down
Loading