Skip to content

Commit 5f45c70

Browse files
authored
Merge branch 'master' into feat/operator-overloads
2 parents 5682e0c + 96cc739 commit 5f45c70

File tree

14 files changed

+235
-39
lines changed

14 files changed

+235
-39
lines changed

.github/workflows/main.yml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,13 @@ jobs:
4949
- name: Python Tests
5050
run: pytest
5151

52-
- name: Run Embedding tests
52+
- name: Embedding tests
5353
run: dotnet test --runtime any-${{ matrix.platform }} src/embed_tests/
5454
if: ${{ matrix.os != 'macos' }} # Not working right now, doesn't find libpython
5555

56+
- name: Python tests run from .NET
57+
run: dotnet test --runtime any-${{ matrix.platform }} src/python_tests_runner/
58+
if: ${{ matrix.os == 'windows' }} # Not working for others right now
59+
5660
# TODO: Run perf tests
5761
# TODO: Run mono tests on Windows?

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ This document follows the conventions laid out in [Keep a CHANGELOG][].
1010
### Added
1111

1212
- Ability to instantiate new .NET arrays using `Array[T](dim1, dim2, ...)` syntax
13+
- Python operator method will call C# operator method for supported binary and unary operators ([#1324][p1324]).
1314

1415
### Changed
1516
- Drop support for Python 2, 3.4, and 3.5
@@ -44,7 +45,7 @@ details about the cause of the failure
4445
- Made it possible to call `ToString`, `GetHashCode`, and `GetType` on inteface objects
4546
- Fixed objects returned by enumerating `PyObject` being disposed too soon
4647
- Incorrectly using a non-generic type with type parameters now produces a helpful Python error instead of throwing NullReferenceException
47-
- Python operator method will call C# operator method for supported binary and unary operators ([#1324][p1324]).
48+
- `import` may now raise errors with more detail than "No module named X"
4849

4950
### Removed
5051

pythonnet.sln

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tools", "Tools", "{BC426F42
4747
tools\geninterop\geninterop.py = tools\geninterop\geninterop.py
4848
EndProjectSection
4949
EndProject
50+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Python.PythonTestsRunner", "src\python_tests_runner\Python.PythonTestsRunner.csproj", "{35CBBDEB-FC07-4D04-9D3E-F88FC180110B}"
51+
EndProject
5052
Global
5153
GlobalSection(SolutionConfigurationPlatforms) = preSolution
5254
Debug|Any CPU = Debug|Any CPU
@@ -129,6 +131,30 @@ Global
129131
{4F2EA4A1-7ECA-48B5-8077-7A3C366F9931}.Release|x64.Build.0 = Release|x64
130132
{4F2EA4A1-7ECA-48B5-8077-7A3C366F9931}.Release|x86.ActiveCfg = Release|x86
131133
{4F2EA4A1-7ECA-48B5-8077-7A3C366F9931}.Release|x86.Build.0 = Release|x86
134+
{6CF9EEA0-F865-4536-AABA-739AE3DA971E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
135+
{6CF9EEA0-F865-4536-AABA-739AE3DA971E}.Debug|Any CPU.Build.0 = Debug|Any CPU
136+
{6CF9EEA0-F865-4536-AABA-739AE3DA971E}.Debug|x64.ActiveCfg = Debug|Any CPU
137+
{6CF9EEA0-F865-4536-AABA-739AE3DA971E}.Debug|x64.Build.0 = Debug|Any CPU
138+
{6CF9EEA0-F865-4536-AABA-739AE3DA971E}.Debug|x86.ActiveCfg = Debug|Any CPU
139+
{6CF9EEA0-F865-4536-AABA-739AE3DA971E}.Debug|x86.Build.0 = Debug|Any CPU
140+
{6CF9EEA0-F865-4536-AABA-739AE3DA971E}.Release|Any CPU.ActiveCfg = Release|Any CPU
141+
{6CF9EEA0-F865-4536-AABA-739AE3DA971E}.Release|Any CPU.Build.0 = Release|Any CPU
142+
{6CF9EEA0-F865-4536-AABA-739AE3DA971E}.Release|x64.ActiveCfg = Release|Any CPU
143+
{6CF9EEA0-F865-4536-AABA-739AE3DA971E}.Release|x64.Build.0 = Release|Any CPU
144+
{6CF9EEA0-F865-4536-AABA-739AE3DA971E}.Release|x86.ActiveCfg = Release|Any CPU
145+
{6CF9EEA0-F865-4536-AABA-739AE3DA971E}.Release|x86.Build.0 = Release|Any CPU
146+
{35CBBDEB-FC07-4D04-9D3E-F88FC180110B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
147+
{35CBBDEB-FC07-4D04-9D3E-F88FC180110B}.Debug|Any CPU.Build.0 = Debug|Any CPU
148+
{35CBBDEB-FC07-4D04-9D3E-F88FC180110B}.Debug|x64.ActiveCfg = Debug|Any CPU
149+
{35CBBDEB-FC07-4D04-9D3E-F88FC180110B}.Debug|x64.Build.0 = Debug|Any CPU
150+
{35CBBDEB-FC07-4D04-9D3E-F88FC180110B}.Debug|x86.ActiveCfg = Debug|Any CPU
151+
{35CBBDEB-FC07-4D04-9D3E-F88FC180110B}.Debug|x86.Build.0 = Debug|Any CPU
152+
{35CBBDEB-FC07-4D04-9D3E-F88FC180110B}.Release|Any CPU.ActiveCfg = Release|Any CPU
153+
{35CBBDEB-FC07-4D04-9D3E-F88FC180110B}.Release|Any CPU.Build.0 = Release|Any CPU
154+
{35CBBDEB-FC07-4D04-9D3E-F88FC180110B}.Release|x64.ActiveCfg = Release|Any CPU
155+
{35CBBDEB-FC07-4D04-9D3E-F88FC180110B}.Release|x64.Build.0 = Release|Any CPU
156+
{35CBBDEB-FC07-4D04-9D3E-F88FC180110B}.Release|x86.ActiveCfg = Release|Any CPU
157+
{35CBBDEB-FC07-4D04-9D3E-F88FC180110B}.Release|x86.Build.0 = Release|Any CPU
132158
EndGlobalSection
133159
GlobalSection(SolutionProperties) = preSolution
134160
HideSolutionNode = FALSE

src/embed_tests/Python.EmbeddingTest.csproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@
1212
<None Include="fixtures/**/*.py" CopyToOutputDirectory="PreserveNewest" />
1313
</ItemGroup>
1414

15+
<PropertyGroup>
16+
<DefineConstants>$(DefineConstants);$(ConfiguredConstants)</DefineConstants>
17+
</PropertyGroup>
18+
1519
<ItemGroup>
1620
<PackageReference Include="NUnit" Version="3.*" />
1721
<PackageReference Include="NUnit3TestAdapter" Version="3.*">
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Reflection;
5+
using System.Text;
6+
using System.Threading.Tasks;
7+
8+
using NUnit.Framework;
9+
10+
using Python.Runtime;
11+
12+
namespace Python.EmbeddingPythonTest
13+
{
14+
public class TestNativeTypeOffset
15+
{
16+
private Py.GILState _gs;
17+
18+
[SetUp]
19+
public void SetUp()
20+
{
21+
_gs = Py.GIL();
22+
}
23+
24+
[TearDown]
25+
public void Dispose()
26+
{
27+
_gs.Dispose();
28+
}
29+
30+
/// <summary>
31+
/// Tests that installation has generated code for NativeTypeOffset and that it can be loaded.
32+
/// </summary>
33+
[Test]
34+
public void LoadNativeTypeOffsetClass()
35+
{
36+
PyObject sys = Py.Import("sys");
37+
string attributeName = "abiflags";
38+
if (sys.HasAttr(attributeName) && !string.IsNullOrEmpty(sys.GetAttr(attributeName).ToString()))
39+
{
40+
string typeName = "Python.Runtime.NativeTypeOffset, Python.Runtime";
41+
Assert.NotNull(Type.GetType(typeName), $"{typeName} does not exist and sys.{attributeName} is not empty");
42+
}
43+
}
44+
}
45+
}

src/embed_tests/TestPyObject.cs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,9 +59,17 @@ def add(self, x, y):
5959
}
6060

6161
[Test]
62-
public void InvokeNull() {
62+
public void InvokeNull()
63+
{
6364
var list = PythonEngine.Eval("list");
6465
Assert.Throws<ArgumentNullException>(() => list.Invoke(new PyObject[] {null}));
6566
}
67+
68+
[Test]
69+
public void AsManagedObjectInvalidCast()
70+
{
71+
var list = PythonEngine.Eval("list");
72+
Assert.Throws<InvalidCastException>(() => list.AsManagedObject(typeof(int)));
73+
}
6674
}
6775
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFrameworks>net472;netcoreapp3.1</TargetFrameworks>
5+
</PropertyGroup>
6+
7+
<ItemGroup>
8+
<ProjectReference Include="..\runtime\Python.Runtime.csproj" />
9+
</ItemGroup>
10+
11+
<ItemGroup>
12+
<PackageReference Include="NUnit" Version="3.*" />
13+
<PackageReference Include="NUnit3TestAdapter" Version="3.*">
14+
<PrivateAssets>all</PrivateAssets>
15+
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
16+
</PackageReference>
17+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.*" />
18+
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Condition="$(MSBuildRuntimeType) == 'Core'">
19+
<Version>1.0.0</Version>
20+
<PrivateAssets>all</PrivateAssets>
21+
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
22+
</PackageReference>
23+
</ItemGroup>
24+
25+
</Project>
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.IO;
4+
using System.Linq;
5+
using System.Reflection;
6+
using System.Text;
7+
8+
using NUnit.Framework;
9+
10+
using Python.Runtime;
11+
12+
namespace Python.PythonTestsRunner
13+
{
14+
public class PythonTestRunner
15+
{
16+
[OneTimeSetUp]
17+
public void SetUp()
18+
{
19+
PythonEngine.Initialize();
20+
}
21+
22+
[OneTimeTearDown]
23+
public void Dispose()
24+
{
25+
PythonEngine.Shutdown();
26+
}
27+
28+
/// <summary>
29+
/// Selects the Python tests to be run as embedded tests.
30+
/// </summary>
31+
/// <returns></returns>
32+
static IEnumerable<string[]> PythonTestCases()
33+
{
34+
// Add the test that you want to debug here.
35+
yield return new[] { "test_enum", "test_enum_standard_attrs" };
36+
yield return new[] { "test_generic", "test_missing_generic_type" };
37+
}
38+
39+
/// <summary>
40+
/// Runs a test in src/tests/*.py as an embedded test. This facilitates debugging.
41+
/// </summary>
42+
/// <param name="testFile">The file name without extension</param>
43+
/// <param name="testName">The name of the test method</param>
44+
[TestCaseSource(nameof(PythonTestCases))]
45+
public void RunPythonTest(string testFile, string testName)
46+
{
47+
// Find the tests directory
48+
string folder = typeof(PythonTestRunner).Assembly.Location;
49+
while (Path.GetFileName(folder) != "src")
50+
{
51+
folder = Path.GetDirectoryName(folder);
52+
}
53+
folder = Path.Combine(folder, "tests");
54+
string path = Path.Combine(folder, testFile + ".py");
55+
if (!File.Exists(path)) throw new FileNotFoundException("Cannot find test file", path);
56+
57+
// We could use 'import' below, but importlib gives more helpful error messages than 'import'
58+
// https://docs.python.org/3/library/importlib.html#importing-a-source-file-directly
59+
// Because the Python tests sometimes have relative imports, the module name must be inside the tests package
60+
PythonEngine.Exec($@"
61+
import sys
62+
import os
63+
sys.path.append(os.path.dirname(r'{folder}'))
64+
sys.path.append(os.path.join(r'{folder}', 'fixtures'))
65+
import clr
66+
clr.AddReference('Python.Test')
67+
import tests
68+
module_name = 'tests.{testFile}'
69+
file_path = r'{path}'
70+
import importlib.util
71+
spec = importlib.util.spec_from_file_location(module_name, file_path)
72+
module = importlib.util.module_from_spec(spec)
73+
sys.modules[module_name] = module
74+
try:
75+
spec.loader.exec_module(module)
76+
except ImportError as error:
77+
raise ImportError(str(error) + ' when sys.path=' + os.pathsep.join(sys.path))
78+
module.{testName}()
79+
");
80+
}
81+
}
82+
}

src/runtime/Python.Runtime.csproj

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,6 @@
1919
<DefineConstants>$(DefineConstants);$(ConfiguredConstants)</DefineConstants>
2020
</PropertyGroup>
2121

22-
<ItemGroup Condition=" '$(PythonInteropFile)' != '' ">
23-
<Compile Remove="interop*.cs" />
24-
<Compile Include="interop.cs" />
25-
<Compile Include="$(PythonInteropFile)" />
26-
</ItemGroup>
27-
2822
<ItemGroup>
2923
<None Remove="resources\clr.py" />
3024
<EmbeddedResource Include="resources\clr.py">

src/runtime/delegatemanager.cs

Lines changed: 5 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -221,19 +221,17 @@ public void Dispose()
221221
public object Dispatch(ArrayList args)
222222
{
223223
IntPtr gs = PythonEngine.AcquireLock();
224-
object ob = null;
224+
object ob;
225225

226226
try
227227
{
228228
ob = TrueDispatch(args);
229229
}
230-
catch (Exception e)
230+
finally
231231
{
232232
PythonEngine.ReleaseLock(gs);
233-
throw e;
234233
}
235234

236-
PythonEngine.ReleaseLock(gs);
237235
return ob;
238236
}
239237

@@ -266,27 +264,15 @@ public object TrueDispatch(ArrayList args)
266264
return null;
267265
}
268266

269-
object result = null;
270-
if (!Converter.ToManaged(op, rtype, out result, false))
267+
object result;
268+
if (!Converter.ToManaged(op, rtype, out result, true))
271269
{
272270
Runtime.XDecref(op);
273-
throw new ConversionException($"could not convert Python result to {rtype}");
271+
throw new PythonException();
274272
}
275273

276274
Runtime.XDecref(op);
277275
return result;
278276
}
279277
}
280-
281-
282-
public class ConversionException : Exception
283-
{
284-
public ConversionException()
285-
{
286-
}
287-
288-
public ConversionException(string msg) : base(msg)
289-
{
290-
}
291-
}
292278
}

0 commit comments

Comments
 (0)