Skip to content

Commit f4bb77a

Browse files
committed
* API for getting the default shutdown mode
* Make the reload test won't bother other tests * Clear obsolete code
1 parent 8dabed7 commit f4bb77a

4 files changed

Lines changed: 67 additions & 83 deletions

File tree

src/embed_tests/Python.EmbeddingTest.15.csproj

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -65,13 +65,9 @@
6565
<PropertyGroup Condition="$(Configuration.Contains('Release'))">
6666
<DefineConstants Condition="'$(CustomDefineConstants)' == ''">$(DefineConstants)</DefineConstants>
6767
</PropertyGroup>
68-
<ItemGroup>
69-
<Compile Remove="DomainCode.cs" />
70-
</ItemGroup>
7168

7269
<ItemGroup>
7370
<None Include="..\pythonnet.snk" />
74-
<None Include="DomainCode.cs" />
7571
<None Remove="packages.config" />
7672
</ItemGroup>
7773

src/embed_tests/TestDomainReload.cs

Lines changed: 46 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,5 @@
11
using System;
2-
using System.CodeDom.Compiler;
3-
using System.Collections.Generic;
42
using System.Diagnostics;
5-
using System.IO;
6-
using System.Linq;
73
using System.Reflection;
84
using NUnit.Framework;
95
using Python.Runtime;
@@ -54,6 +50,7 @@ class TestDomainReload
5450
[Test]
5551
public static void DomainReloadAndGC()
5652
{
53+
Assert.IsFalse(PythonEngine.IsInitialized);
5754
RunAssemblyAndUnload("test1");
5855

5956
Assert.That(Runtime.Runtime.Py_IsInitialized() != 0,
@@ -65,72 +62,47 @@ public static void DomainReloadAndGC()
6562
[Test]
6663
public static void CrossDomainObject()
6764
{
68-
IntPtr handle;
65+
IntPtr handle = IntPtr.Zero;
6966
Type type = typeof(Proxy);
7067
{
7168
AppDomain domain = CreateDomain("test_domain_reload");
72-
var theProxy = (Proxy)domain.CreateInstanceAndUnwrap(
73-
type.Assembly.FullName,
74-
type.FullName);
75-
theProxy.Call("InitPython", ShutdownMode.Reload);
76-
handle = (IntPtr)theProxy.Call("GetTestObject");
77-
theProxy.Call("ShutdownPython");
78-
AppDomain.Unload(domain);
69+
try
70+
{
71+
var theProxy = (Proxy)domain.CreateInstanceAndUnwrap(
72+
type.Assembly.FullName,
73+
type.FullName);
74+
theProxy.Call("InitPython", ShutdownMode.Reload);
75+
handle = (IntPtr)theProxy.Call("GetTestObject");
76+
theProxy.Call("ShutdownPython");
77+
}
78+
finally
79+
{
80+
AppDomain.Unload(domain);
81+
}
7982
}
8083

8184
{
8285
AppDomain domain = CreateDomain("test_domain_reload");
83-
var theProxy = (Proxy)domain.CreateInstanceAndUnwrap(
84-
type.Assembly.FullName,
85-
type.FullName);
86-
theProxy.Call("InitPython", ShutdownMode.Reload);
87-
88-
// handle refering a clr object created in previous domain,
89-
// it should had been deserialized and became callable agian.
90-
theProxy.Call("RunTestObject", handle);
91-
theProxy.Call("ShutdownPythonCompletely");
92-
AppDomain.Unload(domain);
93-
}
94-
Assert.IsTrue(Runtime.Runtime.Py_IsInitialized() == 0);
95-
}
96-
97-
/// <summary>
98-
/// Build an assembly out of the source code above.
99-
///
100-
/// This creates a file <paramref name="assemblyName"/>.dll in order
101-
/// to support the statement "proxy.theAssembly = assembly" below.
102-
/// That statement needs a file, can't run via memory.
103-
/// </summary>
104-
static Assembly BuildAssembly(string assemblyName)
105-
{
106-
var provider = CodeDomProvider.CreateProvider("CSharp");
107-
var compilerparams = new CompilerParameters();
108-
var assemblies = from assembly in AppDomain.CurrentDomain.GetAssemblies()
109-
where !assembly.IsDynamic && !string.IsNullOrEmpty(assembly.Location)
110-
select assembly.Location;
111-
compilerparams.ReferencedAssemblies.AddRange(assemblies.ToArray());
112-
113-
compilerparams.GenerateExecutable = false;
114-
compilerparams.GenerateInMemory = false;
115-
compilerparams.IncludeDebugInformation = false;
116-
compilerparams.OutputAssembly = assemblyName;
117-
118-
var dir = Path.GetDirectoryName(new StackTrace(true).GetFrame(0).GetFileName());
119-
string DomainCodePath = Path.Combine(dir, "DomainCode.cs");
120-
var results = provider.CompileAssemblyFromFile(compilerparams, DomainCodePath);
121-
if (results.Errors.HasErrors)
122-
{
123-
var errors = new System.Text.StringBuilder("Compiler Errors:\n");
124-
foreach (CompilerError error in results.Errors)
86+
try
87+
{
88+
var theProxy = (Proxy)domain.CreateInstanceAndUnwrap(
89+
type.Assembly.FullName,
90+
type.FullName);
91+
theProxy.Call("InitPython", ShutdownMode.Reload);
92+
93+
// handle refering a clr object created in previous domain,
94+
// it should had been deserialized and became callable agian.
95+
theProxy.Call("RunTestObject", handle);
96+
theProxy.Call("ShutdownPythonCompletely");
97+
}
98+
finally
12599
{
126-
errors.AppendFormat("Line {0},{1}\t: {2}\n",
127-
error.Line, error.Column, error.ErrorText);
100+
AppDomain.Unload(domain);
128101
}
129-
throw new Exception(errors.ToString());
130102
}
131-
else
103+
if (PythonEngine.DefaultShutdownMode == ShutdownMode.Normal)
132104
{
133-
return results.CompiledAssembly;
105+
Assert.IsTrue(Runtime.Runtime.Py_IsInitialized() == 0);
134106
}
135107
}
136108

@@ -246,7 +218,7 @@ public static void RunPython()
246218
AppDomain.CurrentDomain.DomainUnload += OnDomainUnload;
247219
string name = AppDomain.CurrentDomain.FriendlyName;
248220
Console.WriteLine(string.Format("[{0} in .NET] In PythonRunner.RunPython", name));
249-
PythonEngine.Initialize(mode: ShutdownMode.Reload);
221+
PythonEngine.Initialize();
250222
using (Py.GIL())
251223
{
252224
try
@@ -287,8 +259,21 @@ public static void ShutdownPython()
287259
public static void ShutdownPythonCompletely()
288260
{
289261
PythonEngine.EndAllowThreads(_state);
290-
PythonEngine.ShutdownMode = ShutdownMode.Normal;
262+
// XXX: Reload mode will reserve clr objects after `Runtime.Shutdown`,
263+
// if it used a another mode(the default mode) in other tests,
264+
// when other tests trying to access these reserved objects, it may cause Domain exception,
265+
// thus it needs to reduct to Soft mode to make sure all clr objects remove from Python.
266+
if (PythonEngine.DefaultShutdownMode != ShutdownMode.Reload)
267+
{
268+
PythonEngine.ShutdownMode = ShutdownMode.Soft;
269+
}
291270
PythonEngine.Shutdown();
271+
if (PythonEngine.DefaultShutdownMode == ShutdownMode.Normal)
272+
{
273+
// Normal mode will shutdown the VM, so it needs to be shutdown
274+
// for avoiding influence with other tests.
275+
Runtime.Runtime.Shutdown();
276+
}
292277
}
293278

294279
public static IntPtr GetTestObject()

src/runtime/pythonengine.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ public static ShutdownMode ShutdownMode
1717
get => Runtime.ShutdownMode;
1818
set => Runtime.ShutdownMode = value;
1919
}
20-
20+
21+
public static ShutdownMode DefaultShutdownMode => Runtime.GetDefaultShutdownMode();
2122

2223
private static DelegateManager delegateManager;
2324
private static bool initialized;
@@ -157,7 +158,7 @@ public static void Initialize()
157158
Initialize(setSysArgv: true);
158159
}
159160

160-
public static void Initialize(bool setSysArgv = true, bool initSigs = false, ShutdownMode mode = ShutdownMode.Normal)
161+
public static void Initialize(bool setSysArgv = true, bool initSigs = false, ShutdownMode mode = ShutdownMode.Default)
161162
{
162163
Initialize(Enumerable.Empty<string>(), setSysArgv: setSysArgv, initSigs: initSigs, mode);
163164
}

src/runtime/runtime.cs

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -142,23 +142,10 @@ internal static void Initialize(bool initSigs = false, ShutdownMode mode = Shutd
142142

143143
if (mode == ShutdownMode.Default)
144144
{
145-
if (Environment.GetEnvironmentVariable("PYTHONNET_SOFT_SHUTDOWN") == "1")
146-
{
147-
mode = ShutdownMode.Soft;
148-
}
149-
#if !NETSTANDARD
150-
else if (Environment.GetEnvironmentVariable("PYTHONNET_RELOAD_SHUTDOWN") == "1")
151-
{
152-
mode = ShutdownMode.Reload;
153-
}
154-
#endif
155-
else
156-
{
157-
mode = ShutdownMode.Normal;
158-
}
145+
mode = GetDefaultShutdownMode();
159146
}
160-
161147
ShutdownMode = mode;
148+
162149
if (Py_IsInitialized() == 0)
163150
{
164151
Py_InitializeEx(initSigs ? 1 : 0);
@@ -408,6 +395,21 @@ internal static void Shutdown()
408395
Py_Finalize();
409396
}
410397

398+
internal static ShutdownMode GetDefaultShutdownMode()
399+
{
400+
if (Environment.GetEnvironmentVariable("PYTHONNET_SOFT_SHUTDOWN") == "1")
401+
{
402+
return ShutdownMode.Soft;
403+
}
404+
#if !NETSTANDARD
405+
else if (Environment.GetEnvironmentVariable("PYTHONNET_RELOAD_SHUTDOWN") == "1")
406+
{
407+
return ShutdownMode.Reload;
408+
}
409+
#endif
410+
return ShutdownMode.Normal;
411+
}
412+
411413
// called *without* the GIL acquired by clr._AtExit
412414
internal static int AtExit()
413415
{
@@ -1984,7 +1986,7 @@ internal static IntPtr PyType_GenericAlloc(IntPtr type, long n)
19841986
private static extern IntPtr PyType_GenericAlloc(IntPtr type, IntPtr n);
19851987

19861988
/// <summary>
1987-
/// Finalize a type object. This should be called on all type objects to finish their initialization. This function is responsible for adding inherited slots from a type¡¯s base class. Return 0 on success, or return -1 and sets an exception on error.
1989+
/// Finalize a type object. This should be called on all type objects to finish their initialization. This function is responsible for adding inherited slots from a types base class. Return 0 on success, or return -1 and sets an exception on error.
19881990
/// </summary>
19891991
[DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)]
19901992
internal static extern int PyType_Ready(IntPtr type);

0 commit comments

Comments
 (0)