Skip to content

Commit cabeec8

Browse files
authored
Merge branch 'master' into 449_Py_Initialize_SignalConfiguration
2 parents 876480f + f1084b5 commit cabeec8

File tree

12 files changed

+198
-14
lines changed

12 files changed

+198
-14
lines changed

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
@@ -13,6 +13,7 @@ This document follows the conventions laid out in [Keep a CHANGELOG][].
1313
Currently there two side-by-side build systems that produces the same output (net40) from the same sources.
1414
After a some transition time, current (mono/ msbuild 14.0) build system will be removed.
1515
- NUnit upgraded to 3.7 (eliminates travis-ci random bug)
16+
- Added C# `PythonEngine.AddShutdownHandler` to help client code clean up on shutdown.
1617
- Added `clr.GetClrType` ([#432][i432])([#433][p433])
1718
- Allowed passing `None` for nullable args ([#460][p460])
1819
- Added keyword arguments based on C# syntax for calling CPython methods ([#461][p461])
@@ -41,6 +42,7 @@ This document follows the conventions laid out in [Keep a CHANGELOG][].
4142
- Fixed errors breaking .NET Remoting on method invoke ([#276][i276])
4243
- Fixed PyObject.GetHashCode ([#676][i676])
4344
- Fix memory leaks due to spurious handle incrementation ([#691][i691])
45+
- Fix inheritance of non-abstract base methods ([#755][i755])
4446

4547

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

599601
[1.0.0]: https://github.com/pythonnet/pythonnet/releases/tag/1.0
600602

603+
[i714]: https://github.com/pythonnet/pythonnet/issues/714
601604
[i608]: https://github.com/pythonnet/pythonnet/issues/608
602605
[i443]: https://github.com/pythonnet/pythonnet/issues/443
603606
[p690]: https://github.com/pythonnet/pythonnet/pull/690
@@ -691,3 +694,4 @@ This document follows the conventions laid out in [Keep a CHANGELOG][].
691694
[p625]: https://github.com/pythonnet/pythonnet/pull/625
692695
[i131]: https://github.com/pythonnet/pythonnet/issues/131
693696
[p531]: https://github.com/pythonnet/pythonnet/pull/531
697+
[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
@@ -169,6 +169,16 @@ public static void Initialize(IEnumerable<string> args, bool setSysArgv = true,
169169
initialized = true;
170170
Exceptions.Clear();
171171

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

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

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

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

322394
/// <summary>
323395
/// 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+
}

src/testing/Python.Test.csproj

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<?xml version="1.0" encoding="utf-8"?>
1+
<?xml version="1.0" encoding="utf-8"?>
22
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
33
<PropertyGroup>
44
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
@@ -83,6 +83,7 @@
8383
<Compile Include="generictest.cs" />
8484
<Compile Include="globaltest.cs" />
8585
<Compile Include="indexertest.cs" />
86+
<Compile Include="InheritanceTest.cs" />
8687
<Compile Include="interfacetest.cs" />
8788
<Compile Include="methodtest.cs" />
8889
<Compile Include="moduletest.cs" />
@@ -110,4 +111,4 @@
110111
<Copy SourceFiles="$(TargetAssembly)" DestinationFolder="$(SolutionDir)\src\tests\fixtures" />
111112
<!--Copy SourceFiles="$(TargetAssemblyPdb)" Condition="Exists('$(TargetAssemblyPdb)')" DestinationFolder="$(PythonBuildDir)" /-->
112113
</Target>
113-
</Project>
114+
</Project>

src/tests/test_class.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,3 +281,14 @@ def PyCallback(self, self2):
281281
testobj.DoCallback()
282282
assert testobj.PyCallbackWasCalled
283283
assert testobj.SameReference
284+
285+
286+
def test_method_inheritance():
287+
"""Ensure that we call the overridden method instead of the one provided in
288+
the base class."""
289+
290+
base = Test.BaseClass()
291+
derived = Test.DerivedClass()
292+
293+
assert base.IsBase() == True
294+
assert derived.IsBase() == False

0 commit comments

Comments
 (0)