Skip to content

Commit a3a3ef5

Browse files
authored
Merge branch 'master' into master
2 parents ee33931 + 88d61a9 commit a3a3ef5

14 files changed

+128
-21
lines changed

CHANGELOG.md

+3
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ This document follows the conventions laid out in [Keep a CHANGELOG][].
2727

2828
### Fixed
2929

30+
- Fixed secondary PythonEngine.Initialize call, all sensitive static variables now reseted.
31+
This is a hidden bug. Once python cleaning up enough memory, objects from previous engine run becomes corrupted. ([#534][p534])
3032
- Fixed Visual Studio 2017 compat ([#434][i434]) for setup.py
3133
- Fixed crashes when integrating pythonnet in Unity3d ([#714][i714]),
3234
related to unloading the Application Domain
@@ -695,3 +697,4 @@ This document follows the conventions laid out in [Keep a CHANGELOG][].
695697
[i131]: https://github.com/pythonnet/pythonnet/issues/131
696698
[p531]: https://github.com/pythonnet/pythonnet/pull/531
697699
[i755]: https://github.com/pythonnet/pythonnet/pull/755
700+
[p534]: https://github.com/pythonnet/pythonnet/pull/534

src/embed_tests/GlobalTestsSetup.cs

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
using NUnit.Framework;
2+
using Python.Runtime;
3+
4+
namespace Python.EmbeddingTest
5+
{
6+
7+
// As the SetUpFixture, the OneTimeTearDown of this class is executed after
8+
// all tests have run.
9+
[SetUpFixture]
10+
public class GlobalTestsSetup
11+
{
12+
[OneTimeTearDown]
13+
public void FinalCleanup()
14+
{
15+
if (PythonEngine.IsInitialized)
16+
{
17+
PythonEngine.Shutdown();
18+
}
19+
}
20+
}
21+
}

src/embed_tests/Python.EmbeddingTest.csproj

+1
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@
105105
<Compile Include="TestRuntime.cs" />
106106
<Compile Include="TestPyScope.cs" />
107107
<Compile Include="TestTypeManager.cs" />
108+
<Compile Include="GlobalTestsSetup.cs" />
108109
</ItemGroup>
109110
<ItemGroup>
110111
<ProjectReference Include="..\runtime\Python.Runtime.csproj">

src/embed_tests/TestPythonEngineProperties.cs

+36-3
Original file line numberDiff line numberDiff line change
@@ -109,18 +109,41 @@ public static void GetPythonHomeDefault()
109109
[Test]
110110
public void SetPythonHome()
111111
{
112+
// We needs to ensure that engine was started and shutdown at least once before setting dummy home.
113+
// Otherwise engine will not run with dummy path with random problem.
114+
if (!PythonEngine.IsInitialized)
115+
{
116+
PythonEngine.Initialize();
117+
}
118+
119+
PythonEngine.Shutdown();
120+
121+
var pythonHomeBackup = PythonEngine.PythonHome;
122+
112123
var pythonHome = "/dummypath/";
113124

114125
PythonEngine.PythonHome = pythonHome;
115126
PythonEngine.Initialize();
116127

117-
Assert.AreEqual(pythonHome, PythonEngine.PythonHome);
118128
PythonEngine.Shutdown();
129+
130+
// Restoring valid pythonhome.
131+
PythonEngine.PythonHome = pythonHomeBackup;
119132
}
120133

121134
[Test]
122135
public void SetPythonHomeTwice()
123136
{
137+
// We needs to ensure that engine was started and shutdown at least once before setting dummy home.
138+
// Otherwise engine will not run with dummy path with random problem.
139+
if (!PythonEngine.IsInitialized)
140+
{
141+
PythonEngine.Initialize();
142+
}
143+
PythonEngine.Shutdown();
144+
145+
var pythonHomeBackup = PythonEngine.PythonHome;
146+
124147
var pythonHome = "/dummypath/";
125148

126149
PythonEngine.PythonHome = "/dummypath2/";
@@ -129,18 +152,29 @@ public void SetPythonHomeTwice()
129152

130153
Assert.AreEqual(pythonHome, PythonEngine.PythonHome);
131154
PythonEngine.Shutdown();
155+
156+
PythonEngine.PythonHome = pythonHomeBackup;
132157
}
133158

134159
[Test]
135160
public void SetProgramName()
136161
{
162+
if (PythonEngine.IsInitialized)
163+
{
164+
PythonEngine.Shutdown();
165+
}
166+
167+
var programNameBackup = PythonEngine.ProgramName;
168+
137169
var programName = "FooBar";
138170

139171
PythonEngine.ProgramName = programName;
140172
PythonEngine.Initialize();
141173

142174
Assert.AreEqual(programName, PythonEngine.ProgramName);
143175
PythonEngine.Shutdown();
176+
177+
PythonEngine.ProgramName = programNameBackup;
144178
}
145179

146180
[Test]
@@ -156,7 +190,7 @@ public void SetPythonPath()
156190
string path = PythonEngine.PythonPath;
157191
PythonEngine.Shutdown();
158192

159-
PythonEngine.ProgramName = path;
193+
PythonEngine.PythonPath = path;
160194
PythonEngine.Initialize();
161195

162196
Assert.AreEqual(path, PythonEngine.PythonPath);
@@ -171,7 +205,6 @@ public void SetPythonPathExceptionOn27()
171205
Assert.Pass();
172206
}
173207

174-
// Get previous path to avoid crashing Python
175208
PythonEngine.Initialize();
176209
string path = PythonEngine.PythonPath;
177210
PythonEngine.Shutdown();

src/embed_tests/TestRuntime.cs

+13-3
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,21 @@
1-
using System;
1+
using System;
22
using NUnit.Framework;
33
using Python.Runtime;
44

55
namespace Python.EmbeddingTest
66
{
77
public class TestRuntime
88
{
9+
[OneTimeSetUp]
10+
public void SetUp()
11+
{
12+
// We needs to ensure that no any engines are running.
13+
if (PythonEngine.IsInitialized)
14+
{
15+
PythonEngine.Shutdown();
16+
}
17+
}
18+
919
/// <summary>
1020
/// Test the cache of the information from the platform module.
1121
///
@@ -24,12 +34,12 @@ public static void PlatformCache()
2434

2535
// Don't shut down the runtime: if the python engine was initialized
2636
// but not shut down by another test, we'd end up in a bad state.
27-
}
37+
}
2838

2939
[Test]
3040
public static void Py_IsInitializedValue()
3141
{
32-
Runtime.Runtime.Py_Finalize(); // In case another test left it on.
42+
Runtime.Runtime.Py_Finalize();
3343
Assert.AreEqual(0, Runtime.Runtime.Py_IsInitialized());
3444
Runtime.Runtime.Py_Initialize();
3545
Assert.AreEqual(1, Runtime.Runtime.Py_IsInitialized());

src/runtime/assemblymanager.cs

+3-6
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,14 @@ internal class AssemblyManager
2424
// than it can end up referring to assemblies that are already unloaded (default behavior after unload appDomain -
2525
// unless LoaderOptimization.MultiDomain is used);
2626
// So for multidomain support it is better to have the dict. recreated for each app-domain initialization
27-
private static ConcurrentDictionary<string, ConcurrentDictionary<Assembly, string>> namespaces;
28-
27+
private static ConcurrentDictionary<string, ConcurrentDictionary<Assembly, string>> namespaces =
28+
new ConcurrentDictionary<string, ConcurrentDictionary<Assembly, string>>();
2929
//private static Dictionary<string, Dictionary<string, string>> generics;
3030
private static AssemblyLoadEventHandler lhandler;
3131
private static ResolveEventHandler rhandler;
3232

3333
// updated only under GIL?
34-
private static Dictionary<string, int> probed;
34+
private static Dictionary<string, int> probed = new Dictionary<string, int>(32);
3535

3636
// modified from event handlers below, potentially triggered from different .NET threads
3737
private static ConcurrentQueue<Assembly> assemblies;
@@ -48,9 +48,6 @@ private AssemblyManager()
4848
/// </summary>
4949
internal static void Initialize()
5050
{
51-
namespaces = new ConcurrentDictionary<string, ConcurrentDictionary<Assembly, string>>();
52-
probed = new Dictionary<string, int>(32);
53-
//generics = new Dictionary<string, Dictionary<string, string>>();
5451
assemblies = new ConcurrentQueue<Assembly>();
5552
pypath = new List<string>(16);
5653

src/runtime/classderived.cs

+8-1
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1-
using System;
1+
using System;
22
using System.Collections.Generic;
33
using System.Linq;
44
using System.Reflection;
55
using System.Reflection.Emit;
6+
using System.Resources;
67
using System.Runtime.InteropServices;
78
using System.Threading.Tasks;
89

@@ -32,6 +33,12 @@ static ClassDerivedObject()
3233
moduleBuilders = new Dictionary<Tuple<string, string>, ModuleBuilder>();
3334
}
3435

36+
public static void Reset()
37+
{
38+
assemblyBuilders = new Dictionary<string, AssemblyBuilder>();
39+
moduleBuilders = new Dictionary<Tuple<string, string>, ModuleBuilder>();
40+
}
41+
3542
internal ClassDerivedObject(Type tp) : base(tp)
3643
{
3744
}

src/runtime/classmanager.cs

+5
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,11 @@ static ClassManager()
3434
dtype = typeof(MulticastDelegate);
3535
}
3636

37+
public static void Reset()
38+
{
39+
cache = new Dictionary<Type, ClassBase>(128);
40+
}
41+
3742
/// <summary>
3843
/// Return the ClassBase-derived instance that implements a particular
3944
/// reflected managed type, creating it if it doesn't yet exist.

src/runtime/genericutil.cs

+6
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.Collections.Generic;
3+
using System.Resources;
34

45
namespace Python.Runtime
56
{
@@ -20,6 +21,11 @@ static GenericUtil()
2021
mapping = new Dictionary<string, Dictionary<string, List<string>>>();
2122
}
2223

24+
public static void Reset()
25+
{
26+
mapping = new Dictionary<string, Dictionary<string, List<string>>>();
27+
}
28+
2329
/// <summary>
2430
/// Register a generic type that appears in a given namespace.
2531
/// </summary>

src/runtime/moduleobject.cs

+11
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,17 @@ public CLRModule() : base("clr")
335335
}
336336
}
337337

338+
public static void Reset()
339+
{
340+
hacked = false;
341+
interactive_preload = true;
342+
preload = false;
343+
344+
// XXX Test performance of new features //
345+
_SuppressDocs = false;
346+
_SuppressOverloads = false;
347+
}
348+
338349
/// <summary>
339350
/// The initializing of the preload hook has to happen as late as
340351
/// possible since sys.ps1 is created after the CLR module is

src/runtime/pyscope.cs

+6-1
Original file line numberDiff line numberDiff line change
@@ -537,10 +537,15 @@ public void Dispose()
537537

538538
public class PyScopeManager
539539
{
540-
public readonly static PyScopeManager Global = new PyScopeManager();
540+
public static PyScopeManager Global;
541541

542542
private Dictionary<string, PyScope> NamedScopes = new Dictionary<string, PyScope>();
543543

544+
internal static void Reset()
545+
{
546+
Global = new PyScopeManager();
547+
}
548+
544549
internal PyScope NewScope(string name)
545550
{
546551
if (name == null)

src/runtime/pythonengine.cs

+2-7
Original file line numberDiff line numberDiff line change
@@ -309,19 +309,14 @@ public static void Shutdown()
309309
{
310310
if (initialized)
311311
{
312+
PyScopeManager.Global.Clear();
313+
312314
// If the shutdown handlers trigger a domain unload,
313315
// don't call shutdown again.
314316
AppDomain.CurrentDomain.DomainUnload -= OnDomainUnload;
315317

316318
ExecuteShutdownHandlers();
317319

318-
Marshal.FreeHGlobal(_pythonHome);
319-
_pythonHome = IntPtr.Zero;
320-
Marshal.FreeHGlobal(_programName);
321-
_programName = IntPtr.Zero;
322-
Marshal.FreeHGlobal(_pythonPath);
323-
_pythonPath = IntPtr.Zero;
324-
325320
initialized = false;
326321
}
327322
}

src/runtime/runtime.cs

+9
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,15 @@ internal static void Initialize()
281281
PyEval_InitThreads();
282282
}
283283

284+
IsFinalizing = false;
285+
286+
CLRModule.Reset();
287+
GenericUtil.Reset();
288+
PyScopeManager.Reset();
289+
ClassManager.Reset();
290+
ClassDerivedObject.Reset();
291+
TypeManager.Reset();
292+
284293
IntPtr op;
285294
IntPtr dict;
286295
if (IsPython3)

src/runtime/typemanager.cs

+4
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ static TypeManager()
2121
cache = new Dictionary<Type, IntPtr>(128);
2222
}
2323

24+
public static void Reset()
25+
{
26+
cache = new Dictionary<Type, IntPtr>(128);
27+
}
2428

2529
/// <summary>
2630
/// Given a managed Type derived from ExtensionType, get the handle to

0 commit comments

Comments
 (0)