Skip to content

Commit 9bea24d

Browse files
authored
Merge branch 'master' into master
2 parents 0670bde + f544adc commit 9bea24d

14 files changed

+50
-118
lines changed

CHANGELOG.md

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

1010
### Added
1111

12+
### Changed
13+
14+
### Fixed
15+
16+
## [2.4.0][]
17+
18+
### Added
19+
1220
- Added support for embedding python into dotnet core 2.0 (NetStandard 2.0)
1321
- Added new build system (pythonnet.15.sln) based on dotnetcore-sdk/xplat(crossplatform msbuild).
1422
Currently there two side-by-side build systems that produces the same output (net40) from the same sources.

README.rst

+1-1
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ Embedding Python in .NET
4848
- All python objects should be declared as ``dynamic`` type.
4949
- Mathematical operations involving python and literal/managed types
5050
must have the python object first, eg. ``np.pi * 2`` works,
51-
``2 * np.pi`` doesnt.
51+
``2 * np.pi`` doesn't.
5252

5353
Example
5454
~~~~~~~

setup.py

+1-2
Original file line numberDiff line numberDiff line change
@@ -637,7 +637,7 @@ def run(self):
637637

638638
setup(
639639
name="pythonnet",
640-
version="2.4.0-rc2",
640+
version="2.4.1-dev",
641641
description=".Net and Mono integration for Python",
642642
url="https://pythonnet.github.io/",
643643
license="MIT",
@@ -662,7 +662,6 @@ def run(self):
662662
"Programming Language :: Python :: 2",
663663
"Programming Language :: Python :: 2.7",
664664
"Programming Language :: Python :: 3",
665-
"Programming Language :: Python :: 3.4",
666665
"Programming Language :: Python :: 3.5",
667666
"Programming Language :: Python :: 3.6",
668667
"Programming Language :: Python :: 3.7",

src/SharedAssemblyInfo.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,4 @@
2525
// Version Information. Keeping it simple. May need to revisit for Nuget
2626
// See: https://codingforsmarties.wordpress.com/2016/01/21/how-to-version-assemblies-destined-for-nuget/
2727
// AssemblyVersion can only be numeric
28-
[assembly: AssemblyVersion("2.4.0")]
28+
[assembly: AssemblyVersion("2.4.1")]

src/clrmodule/ClrModule.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ public static void initclr()
5353
{
5454
#if USE_PYTHON_RUNTIME_VERSION
5555
// Has no effect until SNK works. Keep updated anyways.
56-
Version = new Version("2.4.0"),
56+
Version = new Version("2.4.1"),
5757
#endif
5858
CultureInfo = CultureInfo.InvariantCulture
5959
};

src/clrmodule/clrmodule.15.csproj

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
<RootNamespace>clrmodule</RootNamespace>
1010
<AssemblyName>clrmodule</AssemblyName>
1111
<PackageId>clrmodule</PackageId>
12-
<VersionPrefix>2.4.0</VersionPrefix>
12+
<VersionPrefix>2.4.1</VersionPrefix>
1313
<GenerateAssemblyVersionAttribute>false</GenerateAssemblyVersionAttribute>
1414
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
1515
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>

src/console/Console.15.csproj

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
<AssemblyName>nPython</AssemblyName>
99
<RootNamespace>Python.Runtime</RootNamespace>
1010
<PackageId>nPython</PackageId>
11-
<VersionPrefix>2.4.0</VersionPrefix>
11+
<VersionPrefix>2.4.1</VersionPrefix>
1212
<GenerateAssemblyVersionAttribute>false</GenerateAssemblyVersionAttribute>
1313
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
1414
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>

src/embed_tests/Python.EmbeddingTest.15.csproj

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
<AssemblyName>Python.EmbeddingTest</AssemblyName>
1111
<RootNamespace>Python.EmbeddingTest</RootNamespace>
1212
<PackageId>Python.EmbeddingTest</PackageId>
13-
<VersionPrefix>2.4.0</VersionPrefix>
13+
<VersionPrefix>2.4.1</VersionPrefix>
1414
<GenerateAssemblyVersionAttribute>false</GenerateAssemblyVersionAttribute>
1515
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
1616
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>

src/embed_tests/TestFinalizer.cs

+11-9
Original file line numberDiff line numberDiff line change
@@ -36,16 +36,18 @@ public void CollectBasicObject()
3636
{
3737
Assert.IsTrue(Finalizer.Instance.Enable);
3838

39-
int thId = Thread.CurrentThread.ManagedThreadId;
4039
Finalizer.Instance.Threshold = 1;
4140
bool called = false;
41+
var objectCount = 0;
4242
EventHandler<Finalizer.CollectArgs> handler = (s, e) =>
4343
{
44-
Assert.AreEqual(thId, Thread.CurrentThread.ManagedThreadId);
45-
Assert.GreaterOrEqual(e.ObjectCount, 1);
44+
objectCount = e.ObjectCount;
4645
called = true;
4746
};
4847

48+
Assert.IsFalse(called);
49+
Finalizer.Instance.CollectOnce += handler;
50+
4951
WeakReference shortWeak;
5052
WeakReference longWeak;
5153
{
@@ -61,18 +63,16 @@ public void CollectBasicObject()
6163
Assert.NotZero(garbage.Count);
6264
Assert.IsTrue(garbage.Any(T => ReferenceEquals(T.Target, longWeak.Target)));
6365
}
64-
65-
Assert.IsFalse(called);
66-
Finalizer.Instance.CollectOnce += handler;
6766
try
6867
{
69-
Finalizer.Instance.CallPendingFinalizers();
68+
Finalizer.Instance.Collect(forceDispose: false);
7069
}
7170
finally
7271
{
7372
Finalizer.Instance.CollectOnce -= handler;
7473
}
7574
Assert.IsTrue(called);
75+
Assert.GreaterOrEqual(objectCount, 1);
7676
}
7777

7878
private static void MakeAGarbage(out WeakReference shortWeak, out WeakReference longWeak)
@@ -85,7 +85,7 @@ private static void MakeAGarbage(out WeakReference shortWeak, out WeakReference
8585

8686
private static long CompareWithFinalizerOn(PyObject pyCollect, bool enbale)
8787
{
88-
// Must larger than 512 bytes make sure Python use
88+
// Must larger than 512 bytes make sure Python use
8989
string str = new string('1', 1024);
9090
Finalizer.Instance.Enable = true;
9191
FullGCCollect();
@@ -164,10 +164,11 @@ internal static void CreateMyPyObject(IntPtr op)
164164
public void ErrorHandling()
165165
{
166166
bool called = false;
167+
var errorMessage = "";
167168
EventHandler<Finalizer.ErrorArgs> handleFunc = (sender, args) =>
168169
{
169170
called = true;
170-
Assert.AreEqual(args.Error.Message, "MyPyObject");
171+
errorMessage = args.Error.Message;
171172
};
172173
Finalizer.Instance.Threshold = 1;
173174
Finalizer.Instance.ErrorHandler += handleFunc;
@@ -193,6 +194,7 @@ public void ErrorHandling()
193194
{
194195
Finalizer.Instance.ErrorHandler -= handleFunc;
195196
}
197+
Assert.AreEqual(errorMessage, "MyPyObject");
196198
}
197199

198200
[Test]

src/runtime/Python.Runtime.15.csproj

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
<RootNamespace>Python.Runtime</RootNamespace>
99
<AssemblyName>Python.Runtime</AssemblyName>
1010
<PackageId>Python.Runtime</PackageId>
11-
<VersionPrefix>2.4.0</VersionPrefix>
11+
<VersionPrefix>2.4.1</VersionPrefix>
1212
<GenerateAssemblyVersionAttribute>false</GenerateAssemblyVersionAttribute>
1313
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
1414
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>

src/runtime/finalizer.cs

+21-92
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
using System;
22
using System.Collections.Concurrent;
33
using System.Collections.Generic;
4-
using System.Diagnostics;
54
using System.Linq;
6-
using System.Runtime.InteropServices;
75
using System.Threading;
6+
using System.Threading.Tasks;
87

98
namespace Python.Runtime
109
{
@@ -28,20 +27,10 @@ public class ErrorArgs : EventArgs
2827
public int Threshold { get; set; }
2928
public bool Enable { get; set; }
3029

31-
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
32-
struct PendingArgs
33-
{
34-
public bool cancelled;
35-
}
36-
37-
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
38-
private delegate int PendingCall(IntPtr arg);
39-
private readonly PendingCall _collectAction;
40-
4130
private ConcurrentQueue<IPyDisposable> _objQueue = new ConcurrentQueue<IPyDisposable>();
4231
private bool _pending = false;
4332
private readonly object _collectingLock = new object();
44-
private IntPtr _pendingArgs = IntPtr.Zero;
33+
private Task _finalizerTask;
4534

4635
#region FINALIZER_CHECK
4736

@@ -84,23 +73,20 @@ private Finalizer()
8473
{
8574
Enable = true;
8675
Threshold = 200;
87-
_collectAction = OnPendingCollect;
8876
}
8977

90-
public void CallPendingFinalizers()
78+
public void Collect(bool forceDispose = true)
9179
{
92-
if (Thread.CurrentThread.ManagedThreadId != Runtime.MainManagedThreadId)
80+
if (Instance._finalizerTask != null
81+
&& !Instance._finalizerTask.IsCompleted)
9382
{
94-
throw new Exception("PendingCall should execute in main Python thread");
83+
var ts = PythonEngine.BeginAllowThreads();
84+
Instance._finalizerTask.Wait();
85+
PythonEngine.EndAllowThreads(ts);
9586
}
96-
Runtime.Py_MakePendingCalls();
97-
}
98-
99-
public void Collect()
100-
{
101-
using (var gilState = new Py.GILState())
87+
else if (forceDispose)
10288
{
103-
DisposeAll();
89+
Instance.DisposeAll();
10490
}
10591
}
10692

@@ -141,25 +127,7 @@ internal static void Shutdown()
141127
Instance._objQueue = new ConcurrentQueue<IPyDisposable>();
142128
return;
143129
}
144-
Instance.DisposeAll();
145-
if (Thread.CurrentThread.ManagedThreadId != Runtime.MainManagedThreadId)
146-
{
147-
if (Instance._pendingArgs == IntPtr.Zero)
148-
{
149-
Instance.ResetPending();
150-
return;
151-
}
152-
// Not in main thread just cancel the pending operation to avoid error in different domain
153-
// It will make a memory leak
154-
unsafe
155-
{
156-
PendingArgs* args = (PendingArgs*)Instance._pendingArgs;
157-
args->cancelled = true;
158-
}
159-
Instance.ResetPending();
160-
return;
161-
}
162-
Instance.CallPendingFinalizers();
130+
Instance.Collect(forceDispose: true);
163131
}
164132

165133
private void AddPendingCollect()
@@ -171,16 +139,17 @@ private void AddPendingCollect()
171139
if (!_pending)
172140
{
173141
_pending = true;
174-
var args = new PendingArgs { cancelled = false };
175-
_pendingArgs = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(PendingArgs)));
176-
Marshal.StructureToPtr(args, _pendingArgs, false);
177-
IntPtr func = Marshal.GetFunctionPointerForDelegate(_collectAction);
178-
if (Runtime.Py_AddPendingCall(func, _pendingArgs) != 0)
142+
// should already be complete but just in case
143+
_finalizerTask?.Wait();
144+
145+
_finalizerTask = Task.Factory.StartNew(() =>
179146
{
180-
// Full queue, append next time
181-
FreePendingArgs();
182-
_pending = false;
183-
}
147+
using (Py.GIL())
148+
{
149+
Instance.DisposeAll();
150+
_pending = false;
151+
}
152+
});
184153
}
185154
}
186155
finally
@@ -190,29 +159,6 @@ private void AddPendingCollect()
190159
}
191160
}
192161

193-
private static int OnPendingCollect(IntPtr arg)
194-
{
195-
Debug.Assert(arg == Instance._pendingArgs);
196-
try
197-
{
198-
unsafe
199-
{
200-
PendingArgs* pendingArgs = (PendingArgs*)arg;
201-
if (pendingArgs->cancelled)
202-
{
203-
return 0;
204-
}
205-
}
206-
Instance.DisposeAll();
207-
}
208-
finally
209-
{
210-
Instance.FreePendingArgs();
211-
Instance.ResetPending();
212-
}
213-
return 0;
214-
}
215-
216162
private void DisposeAll()
217163
{
218164
CollectOnce?.Invoke(this, new CollectArgs()
@@ -246,23 +192,6 @@ private void DisposeAll()
246192
}
247193
}
248194

249-
private void FreePendingArgs()
250-
{
251-
if (_pendingArgs != IntPtr.Zero)
252-
{
253-
Marshal.FreeHGlobal(_pendingArgs);
254-
_pendingArgs = IntPtr.Zero;
255-
}
256-
}
257-
258-
private void ResetPending()
259-
{
260-
lock (_collectingLock)
261-
{
262-
_pending = false;
263-
}
264-
}
265-
266195
#if FINALIZER_CHECK
267196
private void ValidateRefCount()
268197
{

src/runtime/resources/clr.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
Code in this module gets loaded into the main clr module.
33
"""
44

5-
__version__ = "2.4.0-rc2"
5+
__version__ = "2.4.1"
66

77

88
class clrproperty(object):

src/testing/Python.Test.15.csproj

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
<AssemblyName>Python.Test</AssemblyName>
88
<RootNamespace>Python.Test</RootNamespace>
99
<PackageId>Python.Test</PackageId>
10-
<VersionPrefix>2.4.0</VersionPrefix>
10+
<VersionPrefix>2.4.1</VersionPrefix>
1111
<OutputPath>bin\</OutputPath>
1212
<AppendTargetFrameworkToOutputPath Condition="'$(TargetFramework)'=='net40'">false</AppendTargetFrameworkToOutputPath>
1313
<DocumentationFile Condition="'$(TargetFramework)'=='net40'">$(OutputPath)\$(AssemblyName).xml</DocumentationFile>

src/tests/test_exceptions.py

-6
Original file line numberDiff line numberDiff line change
@@ -270,12 +270,6 @@ def test_str_of_exception():
270270
with pytest.raises(FormatException) as cm:
271271
Convert.ToDateTime('this will fail')
272272

273-
e = cm.value
274-
# fix for international installation
275-
msg = text_type(e).encode("utf8")
276-
fnd = text_type('System.Convert.ToDateTime').encode("utf8")
277-
assert msg.find(fnd) > -1, msg
278-
279273

280274
def test_python_compat_of_managed_exceptions():
281275
"""Test managed exceptions compatible with Python's implementation"""

0 commit comments

Comments
 (0)