Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Codecs: customize set of Py<->C# conversions via PyObjectConversions#1022

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged
lostmsu merged 17 commits intopythonnet:masterfromlosttech:PR/Codecs
Feb 26, 2020
Merged
Show file tree
Hide file tree
Changes fromall commits
Commits
Show all changes
17 commits
Select commitHold shift + click to select a range
28143d5
enable expanding set of marshaling conversions via PyObjectConversions
lostmsuSep 12, 2019
6eca169
fixed ConversionsObject test failing due to sequence to array convers…
lostmsuJan 16, 2020
97e33c7
attempt to fix CI build issue with ValueTuple under Mono
lostmsuJan 16, 2020
449338f
added RefereneAssemblies package reference to fix CI build
lostmsuJan 30, 2020
39b2347
marked the new codecs API as unstable
lostmsuFeb 1, 2020
daa2901
attempt to fix PyScopeTest.TestThread() reading stale value from res …
lostmsuFeb 1, 2020
e8e3b4b
Merge branch 'master' into PR/Codecs
lostmsuFeb 13, 2020
8818610
Merge branch 'master' into PR/Codecs
filmorFeb 13, 2020
82f6b99
Merge branch 'master' into PR/Codecs
lostmsuFeb 21, 2020
ec98209
fixed bad IncRef after PyTuple_New
lostmsuFeb 21, 2020
50a3822
corrected reference counting in Codecs
lostmsuFeb 21, 2020
2e19f2c
don't dispose encoded object in case codec keeps a cache of them
lostmsuFeb 21, 2020
44bfec2
Merge branch 'master' into PR/Codecs
filmorFeb 23, 2020
399ae54
do not dispose object, that might have been just decoded succesfully,…
lostmsuFeb 23, 2020
e2d3333
remove incref for tuple fields, as Converter.ToPython is supposed to …
lostmsuFeb 23, 2020
5619fb9
Merge branch 'master' into PR/Codecs
filmorFeb 25, 2020
41de69d
Merge branch 'master' into PR/Codecs
lostmsuFeb 26, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 86 additions & 0 deletionssrc/embed_tests/Codecs.cs
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
namespace Python.EmbeddingTest {
using System;
using System.Collections.Generic;
using System.Text;
using NUnit.Framework;
using Python.Runtime;
using Python.Runtime.Codecs;

public class Codecs {
[SetUp]
public void SetUp() {
PythonEngine.Initialize();
}

[TearDown]
public void Dispose() {
PythonEngine.Shutdown();
}

[Test]
public void ConversionsGeneric() {
ConversionsGeneric<ValueTuple<int, string, object>, ValueTuple>();
}

static void ConversionsGeneric<T, TTuple>() {
TupleCodec<TTuple>.Register();
var tuple = Activator.CreateInstance(typeof(T), 42, "42", new object());
T restored = default;
using (Py.GIL())
using (var scope = Py.CreateScope()) {
void Accept(T value) => restored = value;
var accept = new Action<T>(Accept).ToPython();
scope.Set(nameof(tuple), tuple);
scope.Set(nameof(accept), accept);
scope.Exec($"{nameof(accept)}({nameof(tuple)})");
Assert.AreEqual(expected: tuple, actual: restored);
}
}

[Test]
public void ConversionsObject() {
ConversionsObject<ValueTuple<int, string, object>, ValueTuple>();
}
static void ConversionsObject<T, TTuple>() {
TupleCodec<TTuple>.Register();
var tuple = Activator.CreateInstance(typeof(T), 42, "42", new object());
T restored = default;
using (Py.GIL())
using (var scope = Py.CreateScope()) {
void Accept(object value) => restored = (T)value;
var accept = new Action<object>(Accept).ToPython();
scope.Set(nameof(tuple), tuple);
scope.Set(nameof(accept), accept);
scope.Exec($"{nameof(accept)}({nameof(tuple)})");
Assert.AreEqual(expected: tuple, actual: restored);
}
}

[Test]
public void TupleRoundtripObject() {
TupleRoundtripObject<ValueTuple<int, string, object>, ValueTuple>();
}
static void TupleRoundtripObject<T, TTuple>() {
var tuple = Activator.CreateInstance(typeof(T), 42, "42", new object());
using (Py.GIL()) {
var pyTuple = TupleCodec<TTuple>.Instance.TryEncode(tuple);
Assert.IsTrue(TupleCodec<TTuple>.Instance.TryDecode(pyTuple, out object restored));
Assert.AreEqual(expected: tuple, actual: restored);
}
}

[Test]
public void TupleRoundtripGeneric() {
TupleRoundtripGeneric<ValueTuple<int, string, object>, ValueTuple>();
}

static void TupleRoundtripGeneric<T, TTuple>() {
var tuple = Activator.CreateInstance(typeof(T), 42, "42", new object());
using (Py.GIL()) {
var pyTuple = TupleCodec<TTuple>.Instance.TryEncode(tuple);
Assert.IsTrue(TupleCodec<TTuple>.Instance.TryDecode(pyTuple, out T restored));
Assert.AreEqual(expected: tuple, actual: restored);
}
}
}
}
3 changes: 2 additions & 1 deletionsrc/embed_tests/Python.EmbeddingTest.15.csproj
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -23,7 +23,7 @@
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\</SolutionDir>
<PythonBuildDir Condition="'$(TargetFramework)'=='net40' AND '$(PythonBuildDir)' == ''">$(SolutionDir)\bin\</PythonBuildDir>
<PublishDir Condition="'$(TargetFramework)'!='net40'">$(OutputPath)\$(TargetFramework)_publish</PublishDir>
<LangVersion>6</LangVersion>
<LangVersion>7.3</LangVersion>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Is this intentional?@lostmsu

Copy link
MemberAuthor

@lostmsulostmsuDec 22, 2019
edited
Loading

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Yes, I think I am using a few features. Is there a C# compiler, that does not support 7.3 yet?
It is 7.3 in Runtime for a few months now.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

@lostmsu just wanted to make sure it wasn't added by mistake.

<ErrorReport>prompt</ErrorReport>
<CustomDefineConstants Condition="'$(CustomDefineConstants)' == ''">$(PYTHONNET_DEFINE_CONSTANTS)</CustomDefineConstants>
<BaseDefineConstants>XPLAT</BaseDefineConstants>
Expand DownExpand Up@@ -77,6 +77,7 @@


<ItemGroup>
<PackageReference Include="System.ValueTuple" Version="4.5.0" />
<PackageReference Include="NUnit" Version="3.12.0" />
<PackageReference Include="NUnit.ConsoleRunner" Version="3.11.1" />
<PackageReference Include="NUnit3TestAdapter" Version="3.16.1">
Expand Down
6 changes: 5 additions & 1 deletionsrc/embed_tests/Python.EmbeddingTest.csproj
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -14,7 +14,7 @@
<NoWarn>1591</NoWarn>
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\</SolutionDir>
<PythonBuildDir Condition=" '$(PythonBuildDir)' == '' ">$(SolutionDir)\bin\</PythonBuildDir>
<LangVersion>6</LangVersion>
<LangVersion>7.3</LangVersion>
<RestorePackages>true</RestorePackages>
<ErrorReport>prompt</ErrorReport>
</PropertyGroup>
Expand DownExpand Up@@ -73,13 +73,17 @@
<Reference Include="nunit.framework, Version=3.12.0.0, Culture=neutral, PublicKeyToken=2638cd05610744eb, processorArchitecture=MSIL">
<HintPath>..\..\packages\NUnit.3.12.0\lib\net40\nunit.framework.dll</HintPath>
</Reference>
<Reference Include="System.ValueTuple, Version=4.0.3.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\..\packages\System.ValueTuple.4.5.0\lib\portable-net40+sl4+win8+wp8\System.ValueTuple.dll</HintPath>
</Reference>
<Reference Include="System" />
</ItemGroup>
<ItemGroup>
<None Include="..\pythonnet.snk" />
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<Compile Include="Codecs.cs" />
<Compile Include="dynamic.cs" />
<Compile Include="pyimport.cs" />
<Compile Include="pyinitialize.cs" />
Expand Down
7 changes: 6 additions & 1 deletionsrc/embed_tests/TestPyScope.cs
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
using System;
using System.Threading;
using NUnit.Framework;
using Python.Runtime;

Expand DownExpand Up@@ -337,9 +338,12 @@ public void TestThread()
//add function to the scope
//can be call many times, more efficient than ast
ps.Exec(
"import clr\n" +
"from System.Threading import Thread\n" +
"def update():\n" +
" global res, th_cnt\n" +
" res += bb + 1\n" +
" Thread.MemoryBarrier()\n" +
" th_cnt += 1\n"
);
}
Expand All@@ -364,8 +368,9 @@ public void TestThread()
{
cnt = ps.Get<int>("th_cnt");
}
System.Threading.Thread.Sleep(10);
Thread.Sleep(10);
}
Thread.MemoryBarrier();
using (Py.GIL())
{
var result = ps.Get<int>("res");
Expand Down
3 changes: 2 additions & 1 deletionsrc/embed_tests/packages.config
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="NUnit" version="3.12.0" targetFramework="net40" />
<package id="NUnit.ConsoleRunner" version="3.11.1" targetFramework="net40" />
<package id="System.ValueTuple" version="4.5.0" targetFramework="net40" />
</packages>
133 changes: 133 additions & 0 deletionssrc/runtime/Codecs/TupleCodecs.cs
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
namespace Python.Runtime.Codecs
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

[Obsolete(Util.UnstableApiMessage)]
public sealed class TupleCodec<TTuple> : IPyObjectEncoder, IPyObjectDecoder
{
TupleCodec() { }
public static TupleCodec<TTuple> Instance { get; } = new TupleCodec<TTuple>();

public bool CanEncode(Type type)
{
if (type == typeof(object) || type == typeof(TTuple)) return true;
return type.Namespace == typeof(TTuple).Namespace
// generic versions of tuples are named Tuple`TYPE_ARG_COUNT
&& type.Name.StartsWith(typeof(TTuple).Name + '`');
}

public PyObject TryEncode(object value)
{
if (value == null) return null;

var tupleType = value.GetType();
if (tupleType == typeof(object)) return null;
if (!this.CanEncode(tupleType)) return null;
if (tupleType == typeof(TTuple)) return new PyTuple();

long fieldCount = tupleType.GetGenericArguments().Length;
var tuple = Runtime.PyTuple_New(fieldCount);
Exceptions.ErrorCheck(tuple);
int fieldIndex = 0;
foreach (FieldInfo field in tupleType.GetFields())
{
var item = field.GetValue(value);
IntPtr pyItem = Converter.ToPython(item);
Runtime.PyTuple_SetItem(tuple, fieldIndex, pyItem);
fieldIndex++;
}
return new PyTuple(tuple);
}

public bool CanDecode(PyObject objectType, Type targetType)
=> objectType.Handle == Runtime.PyTupleType && this.CanEncode(targetType);

public bool TryDecode<T>(PyObject pyObj, out T value)
{
if (pyObj == null) throw new ArgumentNullException(nameof(pyObj));

value = default;

if (!Runtime.PyTuple_Check(pyObj.Handle)) return false;

if (typeof(T) == typeof(object))
{
bool converted = Decode(pyObj, out object result);
if (converted)
{
value = (T)result;
return true;
}

return false;
}

var itemTypes = typeof(T).GetGenericArguments();
long itemCount = Runtime.PyTuple_Size(pyObj.Handle);
if (itemTypes.Length != itemCount) return false;

if (itemCount == 0)
{
value = (T)EmptyTuple;
return true;
}

var elements = new object[itemCount];
for (int itemIndex = 0; itemIndex < itemTypes.Length; itemIndex++)
{
IntPtr pyItem = Runtime.PyTuple_GetItem(pyObj.Handle, itemIndex);
if (!Converter.ToManaged(pyItem, itemTypes[itemIndex], out elements[itemIndex], setError: false))
{
return false;
}
}
var factory = tupleCreate[itemCount].MakeGenericMethod(itemTypes);
value = (T)factory.Invoke(null, elements);
return true;
}

static bool Decode(PyObject tuple, out object value)
{
long itemCount = Runtime.PyTuple_Size(tuple.Handle);
if (itemCount == 0)
{
value = EmptyTuple;
return true;
}
var elements = new object[itemCount];
var itemTypes = new Type[itemCount];
value = null;
for (int itemIndex = 0; itemIndex < elements.Length; itemIndex++)
{
var pyItem = Runtime.PyTuple_GetItem(tuple.Handle, itemIndex);
if (!Converter.ToManaged(pyItem, typeof(object), out elements[itemIndex], setError: false))
{
return false;
}

itemTypes[itemIndex] = elements[itemIndex]?.GetType() ?? typeof(object);
}

var factory = tupleCreate[itemCount].MakeGenericMethod(itemTypes);
value = factory.Invoke(null, elements);
return true;
}

static readonly MethodInfo[] tupleCreate =
typeof(TTuple).GetMethods(BindingFlags.Public | BindingFlags.Static)
.Where(m => m.Name == nameof(Tuple.Create))
.OrderBy(m => m.GetParameters().Length)
.ToArray();

static readonly object EmptyTuple = tupleCreate[0].Invoke(null, parameters: new object[0]);

public static void Register()
{
PyObjectConversions.RegisterEncoder(Instance);
PyObjectConversions.RegisterDecoder(Instance);
}
}
}
6 changes: 4 additions & 2 deletionssrc/runtime/Python.Runtime.csproj
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
Expand DownExpand Up@@ -76,6 +76,8 @@
<Reference Include="System" />
</ItemGroup>
<ItemGroup>
<Compile Include="Codecs\TupleCodecs.cs" />
<Compile Include="converterextensions.cs" />
<Compile Include="finalizer.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="..\SharedAssemblyInfo.cs">
Expand DownExpand Up@@ -175,4 +177,4 @@
<Copy SourceFiles="$(TargetAssembly)" DestinationFolder="$(PythonBuildDir)" />
<!--Copy SourceFiles="$(TargetAssemblyPdb)" Condition="Exists('$(TargetAssemblyPdb)')" DestinationFolder="$(PythonBuildDir)" /-->
</Target>
</Project>
</Project>
3 changes: 3 additions & 0 deletionssrc/runtime/Util.cs
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -5,6 +5,9 @@ namespace Python.Runtime
{
internal static class Util
{
internal const string UnstableApiMessage =
"This API is unstable, and might be changed or removed in the next minor release";

internal static Int64 ReadCLong(IntPtr tp, int offset)
{
// On Windows, a C long is always 32 bits.
Expand Down
9 changes: 9 additions & 0 deletionssrc/runtime/assemblymanager.cs
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -452,6 +452,7 @@ public static List<string> GetNames(string nsname)
/// looking in the currently loaded assemblies for the named
/// type. Returns null if the named type cannot be found.
/// </summary>
[Obsolete("Use LookupTypes and handle name conflicts")]
public static Type LookupType(string qname)
{
foreach (Assembly assembly in assemblies)
Expand All@@ -465,6 +466,14 @@ public static Type LookupType(string qname)
return null;
}

/// <summary>
/// Returns the <see cref="Type"/> objects for the given qualified name,
/// looking in the currently loaded assemblies for the named
/// type.
/// </summary>
public static IEnumerable<Type> LookupTypes(string qualifiedName)
=> assemblies.Select(assembly => assembly.GetType(qualifiedName)).Where(type => type != null);

internal static Type[] GetTypes(Assembly a)
{
if (a.IsDynamic)
Expand Down
Loading

[8]ページ先頭

©2009-2025 Movatter.jp