This repository was archived by the owner on Jul 22, 2023. It is now read-only.
forked from pythonnet/pythonnet
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathTupleCodecs.cs
More file actions
134 lines (114 loc) · 4.74 KB
/
TupleCodecs.cs
File metadata and controls
134 lines (114 loc) · 4.74 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
namespace Python.Runtime.Codecs
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
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))
{
Exceptions.Clear();
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))
{
Exceptions.Clear();
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);
}
}
}