diff --git a/CHANGELOG.md b/CHANGELOG.md
index e131c327b..ef9358793 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -27,6 +27,8 @@ This document follows the conventions laid out in [Keep a CHANGELOG][].
### Fixed
+- Fixed secondary PythonEngine.Initialize call, all sensitive static variables now reseted.
+ This is a hidden bug. Once python cleaning up enough memory, objects from previous engine run becomes corrupted. ([#534][p534])
- Fixed Visual Studio 2017 compat ([#434][i434]) for setup.py
- Fixed crashes when integrating pythonnet in Unity3d ([#714][i714]),
related to unloading the Application Domain
@@ -695,3 +697,4 @@ This document follows the conventions laid out in [Keep a CHANGELOG][].
[i131]: https://github.com/pythonnet/pythonnet/issues/131
[p531]: https://github.com/pythonnet/pythonnet/pull/531
[i755]: https://github.com/pythonnet/pythonnet/pull/755
+[p534]: https://github.com/pythonnet/pythonnet/pull/534
\ No newline at end of file
diff --git a/src/embed_tests/GlobalTestsSetup.cs b/src/embed_tests/GlobalTestsSetup.cs
new file mode 100644
index 000000000..458ab6a99
--- /dev/null
+++ b/src/embed_tests/GlobalTestsSetup.cs
@@ -0,0 +1,21 @@
+using NUnit.Framework;
+using Python.Runtime;
+
+namespace Python.EmbeddingTest
+{
+
+ // As the SetUpFixture, the OneTimeTearDown of this class is executed after
+ // all tests have run.
+ [SetUpFixture]
+ public class GlobalTestsSetup
+ {
+ [OneTimeTearDown]
+ public void FinalCleanup()
+ {
+ if (PythonEngine.IsInitialized)
+ {
+ PythonEngine.Shutdown();
+ }
+ }
+ }
+}
diff --git a/src/embed_tests/Python.EmbeddingTest.csproj b/src/embed_tests/Python.EmbeddingTest.csproj
index e50053f07..1033fbb20 100644
--- a/src/embed_tests/Python.EmbeddingTest.csproj
+++ b/src/embed_tests/Python.EmbeddingTest.csproj
@@ -105,6 +105,7 @@
+
diff --git a/src/embed_tests/TestPythonEngineProperties.cs b/src/embed_tests/TestPythonEngineProperties.cs
index 01c6ae7e3..243349b82 100644
--- a/src/embed_tests/TestPythonEngineProperties.cs
+++ b/src/embed_tests/TestPythonEngineProperties.cs
@@ -109,18 +109,41 @@ public static void GetPythonHomeDefault()
[Test]
public void SetPythonHome()
{
+ // We needs to ensure that engine was started and shutdown at least once before setting dummy home.
+ // Otherwise engine will not run with dummy path with random problem.
+ if (!PythonEngine.IsInitialized)
+ {
+ PythonEngine.Initialize();
+ }
+
+ PythonEngine.Shutdown();
+
+ var pythonHomeBackup = PythonEngine.PythonHome;
+
var pythonHome = "/dummypath/";
PythonEngine.PythonHome = pythonHome;
PythonEngine.Initialize();
- Assert.AreEqual(pythonHome, PythonEngine.PythonHome);
PythonEngine.Shutdown();
+
+ // Restoring valid pythonhome.
+ PythonEngine.PythonHome = pythonHomeBackup;
}
[Test]
public void SetPythonHomeTwice()
{
+ // We needs to ensure that engine was started and shutdown at least once before setting dummy home.
+ // Otherwise engine will not run with dummy path with random problem.
+ if (!PythonEngine.IsInitialized)
+ {
+ PythonEngine.Initialize();
+ }
+ PythonEngine.Shutdown();
+
+ var pythonHomeBackup = PythonEngine.PythonHome;
+
var pythonHome = "/dummypath/";
PythonEngine.PythonHome = "/dummypath2/";
@@ -129,11 +152,20 @@ public void SetPythonHomeTwice()
Assert.AreEqual(pythonHome, PythonEngine.PythonHome);
PythonEngine.Shutdown();
+
+ PythonEngine.PythonHome = pythonHomeBackup;
}
[Test]
public void SetProgramName()
{
+ if (PythonEngine.IsInitialized)
+ {
+ PythonEngine.Shutdown();
+ }
+
+ var programNameBackup = PythonEngine.ProgramName;
+
var programName = "FooBar";
PythonEngine.ProgramName = programName;
@@ -141,6 +173,8 @@ public void SetProgramName()
Assert.AreEqual(programName, PythonEngine.ProgramName);
PythonEngine.Shutdown();
+
+ PythonEngine.ProgramName = programNameBackup;
}
[Test]
@@ -156,7 +190,7 @@ public void SetPythonPath()
string path = PythonEngine.PythonPath;
PythonEngine.Shutdown();
- PythonEngine.ProgramName = path;
+ PythonEngine.PythonPath = path;
PythonEngine.Initialize();
Assert.AreEqual(path, PythonEngine.PythonPath);
@@ -171,7 +205,6 @@ public void SetPythonPathExceptionOn27()
Assert.Pass();
}
- // Get previous path to avoid crashing Python
PythonEngine.Initialize();
string path = PythonEngine.PythonPath;
PythonEngine.Shutdown();
diff --git a/src/embed_tests/TestRuntime.cs b/src/embed_tests/TestRuntime.cs
index f26a1e4b4..ac1fa1ac0 100644
--- a/src/embed_tests/TestRuntime.cs
+++ b/src/embed_tests/TestRuntime.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using NUnit.Framework;
using Python.Runtime;
@@ -6,6 +6,16 @@ namespace Python.EmbeddingTest
{
public class TestRuntime
{
+ [OneTimeSetUp]
+ public void SetUp()
+ {
+ // We needs to ensure that no any engines are running.
+ if (PythonEngine.IsInitialized)
+ {
+ PythonEngine.Shutdown();
+ }
+ }
+
///
/// Test the cache of the information from the platform module.
///
@@ -24,12 +34,12 @@ public static void PlatformCache()
// Don't shut down the runtime: if the python engine was initialized
// but not shut down by another test, we'd end up in a bad state.
- }
+ }
[Test]
public static void Py_IsInitializedValue()
{
- Runtime.Runtime.Py_Finalize(); // In case another test left it on.
+ Runtime.Runtime.Py_Finalize();
Assert.AreEqual(0, Runtime.Runtime.Py_IsInitialized());
Runtime.Runtime.Py_Initialize();
Assert.AreEqual(1, Runtime.Runtime.Py_IsInitialized());
diff --git a/src/runtime/assemblymanager.cs b/src/runtime/assemblymanager.cs
index 2df7ad2f5..0709eedad 100644
--- a/src/runtime/assemblymanager.cs
+++ b/src/runtime/assemblymanager.cs
@@ -24,14 +24,14 @@ internal class AssemblyManager
// than it can end up referring to assemblies that are already unloaded (default behavior after unload appDomain -
// unless LoaderOptimization.MultiDomain is used);
// So for multidomain support it is better to have the dict. recreated for each app-domain initialization
- private static ConcurrentDictionary> namespaces;
-
+ private static ConcurrentDictionary> namespaces =
+ new ConcurrentDictionary>();
//private static Dictionary> generics;
private static AssemblyLoadEventHandler lhandler;
private static ResolveEventHandler rhandler;
// updated only under GIL?
- private static Dictionary probed;
+ private static Dictionary probed = new Dictionary(32);
// modified from event handlers below, potentially triggered from different .NET threads
private static ConcurrentQueue assemblies;
@@ -48,9 +48,6 @@ private AssemblyManager()
///
internal static void Initialize()
{
- namespaces = new ConcurrentDictionary>();
- probed = new Dictionary(32);
- //generics = new Dictionary>();
assemblies = new ConcurrentQueue();
pypath = new List(16);
diff --git a/src/runtime/classderived.cs b/src/runtime/classderived.cs
index 16d3b99db..ec3734ea5 100644
--- a/src/runtime/classderived.cs
+++ b/src/runtime/classderived.cs
@@ -1,8 +1,9 @@
-using System;
+using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
+using System.Resources;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
@@ -32,6 +33,12 @@ static ClassDerivedObject()
moduleBuilders = new Dictionary, ModuleBuilder>();
}
+ public static void Reset()
+ {
+ assemblyBuilders = new Dictionary();
+ moduleBuilders = new Dictionary, ModuleBuilder>();
+ }
+
internal ClassDerivedObject(Type tp) : base(tp)
{
}
diff --git a/src/runtime/classmanager.cs b/src/runtime/classmanager.cs
index 6a9d40ebd..0b084a49d 100644
--- a/src/runtime/classmanager.cs
+++ b/src/runtime/classmanager.cs
@@ -34,6 +34,11 @@ static ClassManager()
dtype = typeof(MulticastDelegate);
}
+ public static void Reset()
+ {
+ cache = new Dictionary(128);
+ }
+
///
/// Return the ClassBase-derived instance that implements a particular
/// reflected managed type, creating it if it doesn't yet exist.
diff --git a/src/runtime/genericutil.cs b/src/runtime/genericutil.cs
index 9772d082f..3a230e12c 100644
--- a/src/runtime/genericutil.cs
+++ b/src/runtime/genericutil.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.Resources;
namespace Python.Runtime
{
@@ -20,6 +21,11 @@ static GenericUtil()
mapping = new Dictionary>>();
}
+ public static void Reset()
+ {
+ mapping = new Dictionary>>();
+ }
+
///
/// Register a generic type that appears in a given namespace.
///
diff --git a/src/runtime/moduleobject.cs b/src/runtime/moduleobject.cs
index ea1e8e8d0..8af722d29 100644
--- a/src/runtime/moduleobject.cs
+++ b/src/runtime/moduleobject.cs
@@ -335,6 +335,17 @@ public CLRModule() : base("clr")
}
}
+ public static void Reset()
+ {
+ hacked = false;
+ interactive_preload = true;
+ preload = false;
+
+ // XXX Test performance of new features //
+ _SuppressDocs = false;
+ _SuppressOverloads = false;
+ }
+
///
/// The initializing of the preload hook has to happen as late as
/// possible since sys.ps1 is created after the CLR module is
diff --git a/src/runtime/pyscope.cs b/src/runtime/pyscope.cs
index 67f93c6e2..8e6957855 100644
--- a/src/runtime/pyscope.cs
+++ b/src/runtime/pyscope.cs
@@ -537,10 +537,15 @@ public void Dispose()
public class PyScopeManager
{
- public readonly static PyScopeManager Global = new PyScopeManager();
+ public static PyScopeManager Global;
private Dictionary NamedScopes = new Dictionary();
+ internal static void Reset()
+ {
+ Global = new PyScopeManager();
+ }
+
internal PyScope NewScope(string name)
{
if (name == null)
diff --git a/src/runtime/pythonengine.cs b/src/runtime/pythonengine.cs
index 38bd33099..4c51224f2 100644
--- a/src/runtime/pythonengine.cs
+++ b/src/runtime/pythonengine.cs
@@ -309,19 +309,14 @@ public static void Shutdown()
{
if (initialized)
{
+ PyScopeManager.Global.Clear();
+
// If the shutdown handlers trigger a domain unload,
// don't call shutdown again.
AppDomain.CurrentDomain.DomainUnload -= OnDomainUnload;
ExecuteShutdownHandlers();
- Marshal.FreeHGlobal(_pythonHome);
- _pythonHome = IntPtr.Zero;
- Marshal.FreeHGlobal(_programName);
- _programName = IntPtr.Zero;
- Marshal.FreeHGlobal(_pythonPath);
- _pythonPath = IntPtr.Zero;
-
initialized = false;
}
}
diff --git a/src/runtime/runtime.cs b/src/runtime/runtime.cs
index 831864ef9..ef492a7b8 100644
--- a/src/runtime/runtime.cs
+++ b/src/runtime/runtime.cs
@@ -281,6 +281,15 @@ internal static void Initialize()
PyEval_InitThreads();
}
+ IsFinalizing = false;
+
+ CLRModule.Reset();
+ GenericUtil.Reset();
+ PyScopeManager.Reset();
+ ClassManager.Reset();
+ ClassDerivedObject.Reset();
+ TypeManager.Reset();
+
IntPtr op;
IntPtr dict;
if (IsPython3)
diff --git a/src/runtime/typemanager.cs b/src/runtime/typemanager.cs
index df2e71be0..d19c8737f 100644
--- a/src/runtime/typemanager.cs
+++ b/src/runtime/typemanager.cs
@@ -21,6 +21,10 @@ static TypeManager()
cache = new Dictionary(128);
}
+ public static void Reset()
+ {
+ cache = new Dictionary(128);
+ }
///
/// Given a managed Type derived from ExtensionType, get the handle to