Skip to content

Commit 2b1d33f

Browse files
authored
Merge branch 'master' into implicit-assembly-loading
2 parents 3b30ff6 + f1084b5 commit 2b1d33f

12 files changed

Lines changed: 198 additions & 14 deletions

AUTHORS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
- Sam Winstanley ([@swinstanley](https://github.com/swinstanley))
4343
- Sean Freitag ([@cowboygneox](https://github.com/cowboygneox))
4444
- Serge Weinstock ([@sweinst](https://github.com/sweinst))
45+
- Simon Mourier ([@smourier](https://github.com/smourier))
4546
- Viktoria Kovescses ([@vkovec](https://github.com/vkovec))
4647
- Ville M. Vainio ([@vivainio](https://github.com/vivainio))
4748
- Virgil Dupras ([@hsoft](https://github.com/hsoft))

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ This document follows the conventions laid out in [Keep a CHANGELOG][].
1414
Currently there two side-by-side build systems that produces the same output (net40) from the same sources.
1515
After a some transition time, current (mono/ msbuild 14.0) build system will be removed.
1616
- NUnit upgraded to 3.7 (eliminates travis-ci random bug)
17+
- Added C# `PythonEngine.AddShutdownHandler` to help client code clean up on shutdown.
1718
- Added `clr.GetClrType` ([#432][i432])([#433][p433])
1819
- Allowed passing `None` for nullable args ([#460][p460])
1920
- Added keyword arguments based on C# syntax for calling CPython methods ([#461][p461])
@@ -43,6 +44,7 @@ This document follows the conventions laid out in [Keep a CHANGELOG][].
4344
- Fixed PyObject.GetHashCode ([#676][i676])
4445
- Fix memory leaks due to spurious handle incrementation ([#691][i691])
4546
- Fix spurious assembly loading exceptions from private types ([#703][i703])
47+
- Fix inheritance of non-abstract base methods ([#755][i755])
4648

4749

4850
## [2.3.0][] - 2017-03-11
@@ -600,6 +602,7 @@ This document follows the conventions laid out in [Keep a CHANGELOG][].
600602

601603
[1.0.0]: https://github.com/pythonnet/pythonnet/releases/tag/1.0
602604

605+
[i714]: https://github.com/pythonnet/pythonnet/issues/714
603606
[i608]: https://github.com/pythonnet/pythonnet/issues/608
604607
[i443]: https://github.com/pythonnet/pythonnet/issues/443
605608
[p690]: https://github.com/pythonnet/pythonnet/pull/690
@@ -693,3 +696,4 @@ This document follows the conventions laid out in [Keep a CHANGELOG][].
693696
[p625]: https://github.com/pythonnet/pythonnet/pull/625
694697
[i131]: https://github.com/pythonnet/pythonnet/issues/131
695698
[p531]: https://github.com/pythonnet/pythonnet/pull/531
699+
[i755]: https://github.com/pythonnet/pythonnet/pull/755

setup.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -329,9 +329,16 @@ def _install_packages(self):
329329
self.debug_print("Updating NuGet: {0}".format(cmd))
330330
subprocess.check_call(cmd, shell=use_shell)
331331

332-
cmd = "{0} restore pythonnet.sln -MSBuildVersion 14 -o packages".format(nuget)
333-
self.debug_print("Installing packages: {0}".format(cmd))
334-
subprocess.check_call(cmd, shell=use_shell)
332+
try:
333+
# msbuild=14 is mainly for Mono issues
334+
cmd = "{0} restore pythonnet.sln -MSBuildVersion 14 -o packages".format(nuget)
335+
self.debug_print("Installing packages: {0}".format(cmd))
336+
subprocess.check_call(cmd, shell=use_shell)
337+
except:
338+
# when only VS 2017 is installed do not specify msbuild version
339+
cmd = "{0} restore pythonnet.sln -o packages".format(nuget)
340+
self.debug_print("Installing packages: {0}".format(cmd))
341+
subprocess.check_call(cmd, shell=use_shell)
335342

336343
def _find_msbuild_tool(self, tool="msbuild.exe", use_windows_sdk=False):
337344
"""Return full path to one of the Microsoft build tools"""

src/embed_tests/pyinitialize.cs

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,5 +74,65 @@ public void ReInitialize()
7474
}
7575
PythonEngine.Shutdown();
7676
}
77+
78+
[Test]
79+
public void TestScopeIsShutdown()
80+
{
81+
PythonEngine.Initialize();
82+
var scope = PyScopeManager.Global.Create("test");
83+
PythonEngine.Shutdown();
84+
Assert.That(PyScopeManager.Global.Contains("test"), Is.False);
85+
}
86+
87+
/// <summary>
88+
/// Helper for testing the shutdown handlers.
89+
/// </summary>
90+
int shutdown_count = 0;
91+
void OnShutdownIncrement()
92+
{
93+
shutdown_count++;
94+
}
95+
void OnShutdownDouble()
96+
{
97+
shutdown_count *= 2;
98+
}
99+
100+
/// <summary>
101+
/// Test the shutdown handlers.
102+
/// </summary>
103+
[Test]
104+
public void ShutdownHandlers()
105+
{
106+
// Test we can run one shutdown handler.
107+
shutdown_count = 0;
108+
PythonEngine.Initialize();
109+
PythonEngine.AddShutdownHandler(OnShutdownIncrement);
110+
PythonEngine.Shutdown();
111+
Assert.That(shutdown_count, Is.EqualTo(1));
112+
113+
// Test we can run multiple shutdown handlers in the right order.
114+
shutdown_count = 4;
115+
PythonEngine.Initialize();
116+
PythonEngine.AddShutdownHandler(OnShutdownIncrement);
117+
PythonEngine.AddShutdownHandler(OnShutdownDouble);
118+
PythonEngine.Shutdown();
119+
// Correct: 4 * 2 + 1 = 9
120+
// Wrong: (4 + 1) * 2 = 10
121+
Assert.That(shutdown_count, Is.EqualTo(9));
122+
123+
// Test we can remove shutdown handlers, handling duplicates.
124+
shutdown_count = 4;
125+
PythonEngine.Initialize();
126+
PythonEngine.AddShutdownHandler(OnShutdownIncrement);
127+
PythonEngine.AddShutdownHandler(OnShutdownIncrement);
128+
PythonEngine.AddShutdownHandler(OnShutdownDouble);
129+
PythonEngine.AddShutdownHandler(OnShutdownIncrement);
130+
PythonEngine.AddShutdownHandler(OnShutdownDouble);
131+
PythonEngine.RemoveShutdownHandler(OnShutdownDouble);
132+
PythonEngine.Shutdown();
133+
// Correct: (4 + 1) * 2 + 1 + 1 = 12
134+
// Wrong: (4 * 2) + 1 + 1 + 1 = 11
135+
Assert.That(shutdown_count, Is.EqualTo(12));
136+
}
77137
}
78138
}

src/runtime/Python.Runtime.15.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@
131131

132132
<Target Name="BeforeBuild" Condition="'$(TargetFramework)'=='net40' AND $(Configuration.Contains('Mono')) AND '$(OS)' != 'Windows_NT'">
133133
<!--Endless war!-->
134-
<Exec Command="cp $(NuGetPackageRoot)/microsoft.targetingpack.netframework.v4.5/1.0.1/lib/net45/System.XML.dll $(NuGetPackageRoot)/microsoft.targetingpack.netframework.v4.5/1.0.1/lib/net45/System.Xml.dll" />
134+
<Exec Command="[[ -e $(NuGetPackageRoot)/microsoft.targetingpack.netframework.v4.5/1.0.1/lib/net45/System.Xml.dll ]] || cp $(NuGetPackageRoot)/microsoft.targetingpack.netframework.v4.5/1.0.1/lib/net45/System.XML.dll $(NuGetPackageRoot)/microsoft.targetingpack.netframework.v4.5/1.0.1/lib/net45/System.Xml.dll" />
135135
</Target>
136136
<Target Name="AfterBuild">
137137
<Copy Condition="'$(TargetFramework)'=='net40'" SourceFiles="$(TargetAssembly)" DestinationFolder="$(PythonBuildDir)" />

src/runtime/Python.Runtime.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,7 @@
148148
<Compile Include="interop34.cs" />
149149
<Compile Include="interop35.cs" />
150150
<Compile Include="interop36.cs" />
151+
<Compile Include="interop37.cs" />
151152
</ItemGroup>
152153
<ItemGroup>
153154
<None Include="..\pythonnet.snk" />

src/runtime/methodbinder.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -601,6 +601,19 @@ internal class MethodSorter : IComparer
601601
{
602602
int IComparer.Compare(object m1, object m2)
603603
{
604+
var me1 = (MethodBase)m1;
605+
var me2 = (MethodBase)m2;
606+
if (me1.DeclaringType != me2.DeclaringType)
607+
{
608+
// m2's type derives from m1's type, favor m2
609+
if (me1.DeclaringType.IsAssignableFrom(me2.DeclaringType))
610+
return 1;
611+
612+
// m1's type derives from m2's type, favor m1
613+
if (me2.DeclaringType.IsAssignableFrom(me1.DeclaringType))
614+
return -1;
615+
}
616+
604617
int p1 = MethodBinder.GetPrecedence((MethodBase)m1);
605618
int p2 = MethodBinder.GetPrecedence((MethodBase)m2);
606619
if (p1 < p2)

src/runtime/pythonengine.cs

Lines changed: 79 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,16 @@ public static void Initialize(IEnumerable<string> args, bool setSysArgv = true)
168168
initialized = true;
169169
Exceptions.Clear();
170170

171+
// Make sure we clean up properly on app domain unload.
172+
AppDomain.CurrentDomain.DomainUnload += OnDomainUnload;
173+
174+
// Remember to shut down the runtime.
175+
AddShutdownHandler(Runtime.Shutdown);
176+
177+
// The global scope gets used implicitly quite early on, remember
178+
// to clear it out when we shut down.
179+
AddShutdownHandler(PyScopeManager.Global.Clear);
180+
171181
if (setSysArgv)
172182
{
173183
Py.SetArgv(args);
@@ -220,9 +230,6 @@ public static void Initialize(IEnumerable<string> args, bool setSysArgv = true)
220230
{
221231
locals.Dispose();
222232
}
223-
224-
// Make sure we clean up properly on app domain unload.
225-
AppDomain.CurrentDomain.DomainUnload += OnDomainUnload;
226233
}
227234
}
228235

@@ -302,21 +309,86 @@ public static void Shutdown()
302309
{
303310
if (initialized)
304311
{
305-
PyScopeManager.Global.Clear();
312+
// If the shutdown handlers trigger a domain unload,
313+
// don't call shutdown again.
314+
AppDomain.CurrentDomain.DomainUnload -= OnDomainUnload;
315+
316+
ExecuteShutdownHandlers();
317+
306318
Marshal.FreeHGlobal(_pythonHome);
307319
_pythonHome = IntPtr.Zero;
308320
Marshal.FreeHGlobal(_programName);
309321
_programName = IntPtr.Zero;
310322
Marshal.FreeHGlobal(_pythonPath);
311323
_pythonPath = IntPtr.Zero;
312324

313-
Runtime.Shutdown();
314-
315-
AppDomain.CurrentDomain.DomainUnload -= OnDomainUnload;
316325
initialized = false;
317326
}
318327
}
319328

329+
/// <summary>
330+
/// Called when the engine is shut down.
331+
///
332+
/// Shutdown handlers are run in reverse order they were added, so that
333+
/// resources available when running a shutdown handler are the same as
334+
/// what was available when it was added.
335+
/// </summary>
336+
public delegate void ShutdownHandler();
337+
338+
static List<ShutdownHandler> ShutdownHandlers = new List<ShutdownHandler>();
339+
340+
/// <summary>
341+
/// Add a function to be called when the engine is shut down.
342+
///
343+
/// Shutdown handlers are executed in the opposite order they were
344+
/// added, so that you can be sure that everything that was initialized
345+
/// when you added the handler is still initialized when you need to shut
346+
/// down.
347+
///
348+
/// If the same shutdown handler is added several times, it will be run
349+
/// several times.
350+
///
351+
/// Don't add shutdown handlers while running a shutdown handler.
352+
/// </summary>
353+
public static void AddShutdownHandler(ShutdownHandler handler)
354+
{
355+
ShutdownHandlers.Add(handler);
356+
}
357+
358+
/// <summary>
359+
/// Remove a shutdown handler.
360+
///
361+
/// If the same shutdown handler is added several times, only the last
362+
/// one is removed.
363+
///
364+
/// Don't remove shutdown handlers while running a shutdown handler.
365+
/// </summary>
366+
public static void RemoveShutdownHandler(ShutdownHandler handler)
367+
{
368+
for (int index = ShutdownHandlers.Count - 1; index >= 0; --index)
369+
{
370+
if (ShutdownHandlers[index] == handler)
371+
{
372+
ShutdownHandlers.RemoveAt(index);
373+
break;
374+
}
375+
}
376+
}
377+
378+
/// <summary>
379+
/// Run all the shutdown handlers.
380+
///
381+
/// They're run in opposite order they were added.
382+
/// </summary>
383+
static void ExecuteShutdownHandlers()
384+
{
385+
while(ShutdownHandlers.Count > 0)
386+
{
387+
var handler = ShutdownHandlers[ShutdownHandlers.Count - 1];
388+
ShutdownHandlers.RemoveAt(ShutdownHandlers.Count - 1);
389+
handler();
390+
}
391+
}
320392

321393
/// <summary>
322394
/// AcquireLock Method

src/runtime/runtime.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ public class Runtime
149149
#elif PYTHON36
150150
internal const string _pyversion = "3.6";
151151
internal const string _pyver = "36";
152-
#elif PYTHON37 // TODO: Add `interop37.cs` after PY37 is released
152+
#elif PYTHON37
153153
internal const string _pyversion = "3.7";
154154
internal const string _pyver = "37";
155155
#else

src/testing/InheritanceTest.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
using System;
2+
3+
namespace Python.Test
4+
{
5+
public class BaseClass
6+
{
7+
public bool IsBase() => true;
8+
}
9+
10+
public class DerivedClass : BaseClass
11+
{
12+
public new bool IsBase() => false;
13+
}
14+
}

0 commit comments

Comments
 (0)