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

Commit429cc3b

Browse files
committed
reworked Enum marshaling
- enums are no longer converted to and from PyLong automatically#1220- one can construct an instance of MyEnum from Python using MyEnum(numeric_val), e.g. MyEnum(10)- in the above, if MyEnum does not have [Flags] and does not have value 10 defined, to create MyEnum with value 10 one must call MyEnum(10, True). Here True is an unnamed parameter, that allows unchecked conversion- legacy behavior has been moved to a codec (EnumPyLongCodec); enums can now be encoded by codecs
1 parent707ef36 commit429cc3b

File tree

9 files changed

+196
-104
lines changed

9 files changed

+196
-104
lines changed

‎CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ when .NET expects an integer [#1342][i1342]
3636
- BREAKING: Methods with`ref` or`out` parameters and void return type return a tuple of only the`ref` and`out` parameters.
3737
- BREAKING: to call Python from .NET`Runtime.PythonDLL` property must be set to Python DLL name
3838
or the DLL must be loaded in advance. This must be done before calling any other Python.NET functions.
39+
- BREAKING: disabled implicit conversion from C# enums to Python`int` and back.
40+
One must now either use enum members (e.g.`MyEnum.Option`), or use enum constructor
41+
(e.g.`MyEnum(42)` or`MyEnum(42, True)` when`MyEnum` does not have a member with value 42).
3942
- Sign Runtime DLL with a strong name
4043
- Implement loading through`clr_loader` instead of the included`ClrModule`, enables
4144
support for .NET Core

‎src/runtime/Codecs/EnumPyLongCodec.cs

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
usingSystem;
2+
3+
namespacePython.Runtime.Codecs
4+
{
5+
[Obsolete]
6+
publicsealedclassEnumPyLongCodec:IPyObjectEncoder,IPyObjectDecoder
7+
{
8+
publicstaticEnumPyLongCodecInstance{get;}=newEnumPyLongCodec();
9+
10+
publicboolCanDecode(PyObjectobjectType,TypetargetType)
11+
{
12+
returntargetType.IsEnum
13+
&&objectType.IsSubclass(newBorrowedReference(Runtime.PyLongType));
14+
}
15+
16+
publicboolCanEncode(Typetype)
17+
{
18+
returntype==typeof(object)||type==typeof(ValueType)||type.IsEnum;
19+
}
20+
21+
publicboolTryDecode<T>(PyObjectpyObj,outTvalue)
22+
{
23+
value=default;
24+
if(!typeof(T).IsEnum)returnfalse;
25+
26+
Typeetype=Enum.GetUnderlyingType(typeof(T));
27+
28+
if(!PyLong.IsLongType(pyObj))returnfalse;
29+
30+
objectresult;
31+
try
32+
{
33+
result=pyObj.AsManagedObject(etype);
34+
}
35+
catch(InvalidCastException)
36+
{
37+
returnfalse;
38+
}
39+
40+
if(Enum.IsDefined(typeof(T),result)
41+
||typeof(T).GetCustomAttributes(typeof(FlagsAttribute),true).Length>0)
42+
{
43+
value=(T)Enum.ToObject(typeof(T),result);
44+
returntrue;
45+
}
46+
47+
returnfalse;
48+
}
49+
50+
publicPyObjectTryEncode(objectvalue)
51+
{
52+
if(valueisnull)returnnull;
53+
54+
varenumType=value.GetType();
55+
if(!enumType.IsEnum)returnnull;
56+
57+
returnnewPyLong((long)value);
58+
}
59+
60+
privateEnumPyLongCodec(){}
61+
}
62+
}

‎src/runtime/classobject.cs

Lines changed: 43 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,9 @@ internal NewReference GetDocString()
5050
/// <summary>
5151
/// Implements __new__ for reflected classes and value types.
5252
/// </summary>
53-
publicstaticIntPtrtp_new(IntPtrtp,IntPtrargs,IntPtrkw)
53+
publicstaticIntPtrtp_new(IntPtrtpRaw,IntPtrargs,IntPtrkw)
5454
{
55+
vartp=newBorrowedReference(tpRaw);
5556
varself=GetManagedObject(tp)asClassObject;
5657

5758
// Sanity check: this ensures a graceful error if someone does
@@ -87,7 +88,7 @@ public static IntPtr tp_new(IntPtr tp, IntPtr args, IntPtr kw)
8788
returnIntPtr.Zero;
8889
}
8990

90-
returnCLRObject.GetInstHandle(result,tp);
91+
returnCLRObject.GetInstHandle(result,tp).DangerousMoveToPointerOrNull();
9192
}
9293

9394
if(type.IsAbstract)
@@ -98,8 +99,7 @@ public static IntPtr tp_new(IntPtr tp, IntPtr args, IntPtr kw)
9899

99100
if(type.IsEnum)
100101
{
101-
Exceptions.SetError(Exceptions.TypeError,"cannot instantiate enumeration");
102-
returnIntPtr.Zero;
102+
returnNewEnum(type,newBorrowedReference(args),tp).DangerousMoveToPointerOrNull();
103103
}
104104

105105
objectobj=self.binder.InvokeRaw(IntPtr.Zero,args,kw);
@@ -108,7 +108,45 @@ public static IntPtr tp_new(IntPtr tp, IntPtr args, IntPtr kw)
108108
returnIntPtr.Zero;
109109
}
110110

111-
returnCLRObject.GetInstHandle(obj,tp);
111+
returnCLRObject.GetInstHandle(obj,tp).DangerousMoveToPointerOrNull();
112+
}
113+
114+
privatestaticNewReferenceNewEnum(Typetype,BorrowedReferenceargs,BorrowedReferencetp)
115+
{
116+
nintargCount=Runtime.PyTuple_Size(args);
117+
boolallowUnchecked=false;
118+
if(argCount==2)
119+
{
120+
varallow=Runtime.PyTuple_GetItem(args,1);
121+
if(!Converter.ToManaged(allow,typeof(bool),outvarallowObj,true)||allowObjisnull)
122+
{
123+
Exceptions.RaiseTypeError("second argument to enum constructor must be a boolean");
124+
returndefault;
125+
}
126+
allowUnchecked|=(bool)allowObj;
127+
}
128+
129+
if(argCount<1||argCount>2)
130+
{
131+
Exceptions.SetError(Exceptions.TypeError,"no constructors match given arguments");
132+
returndefault;
133+
}
134+
135+
varop=Runtime.PyTuple_GetItem(args,0);
136+
if(!Converter.ToManaged(op,type.GetEnumUnderlyingType(),outobjectresult,true))
137+
{
138+
returndefault;
139+
}
140+
141+
if(!allowUnchecked&&!Enum.IsDefined(type,result)
142+
&&type.GetCustomAttributes(typeof(FlagsAttribute),true).Length==0)
143+
{
144+
Exceptions.SetError(Exceptions.ValueError,"Invalid enumeration value. Pass True as the second argument if unchecked conversion is desired");
145+
returndefault;
146+
}
147+
148+
objectenumValue=Enum.ToObject(type,result);
149+
returnCLRObject.GetInstHandle(enumValue,tp);
112150
}
113151

114152

‎src/runtime/converter.cs

Lines changed: 30 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ private Converter()
2727
privatestaticTypeint16Type;
2828
privatestaticTypeint32Type;
2929
privatestaticTypeint64Type;
30-
privatestaticTypeflagsType;
3130
privatestaticTypeboolType;
3231
privatestaticTypetypeType;
3332

@@ -42,7 +41,6 @@ static Converter()
4241
singleType=typeof(Single);
4342
doubleType=typeof(Double);
4443
decimalType=typeof(Decimal);
45-
flagsType=typeof(FlagsAttribute);
4644
boolType=typeof(Boolean);
4745
typeType=typeof(Type);
4846
}
@@ -148,7 +146,8 @@ internal static IntPtr ToPython(object value, Type type)
148146
returnresult;
149147
}
150148

151-
if(Type.GetTypeCode(type)==TypeCode.Object&&value.GetType()!=typeof(object)){
149+
if(Type.GetTypeCode(type)==TypeCode.Object&&value.GetType()!=typeof(object)
150+
||type.IsEnum){
152151
varencoded=PyObjectConversions.TryEncode(value,type);
153152
if(encoded!=null){
154153
result=encoded.Handle;
@@ -203,6 +202,11 @@ internal static IntPtr ToPython(object value, Type type)
203202

204203
type=value.GetType();
205204

205+
if(type.IsEnum)
206+
{
207+
returnCLRObject.GetInstHandle(value,type);
208+
}
209+
206210
TypeCodetc=Type.GetTypeCode(type);
207211

208212
switch(tc)
@@ -317,6 +321,18 @@ internal static bool ToManaged(IntPtr value, Type type,
317321
}
318322
returnConverter.ToManagedValue(value,type,outresult,setError);
319323
}
324+
/// <summary>
325+
/// Return a managed object for the given Python object, taking funny
326+
/// byref types into account.
327+
/// </summary>
328+
/// <param name="value">A Python object</param>
329+
/// <param name="type">The desired managed type</param>
330+
/// <param name="result">Receives the managed object</param>
331+
/// <param name="setError">If true, call <c>Exceptions.SetError</c> with the reason for failure.</param>
332+
/// <returns>True on success</returns>
333+
internalstaticboolToManaged(BorrowedReferencevalue,Typetype,
334+
outobjectresult,boolsetError)
335+
=>ToManaged(value.DangerousGetAddress(),type,outresult,setError);
320336

321337
internalstaticboolToManagedValue(BorrowedReferencevalue,TypeobType,
322338
outobjectresult,boolsetError)
@@ -398,11 +414,6 @@ internal static bool ToManagedValue(IntPtr value, Type obType,
398414
returnToArray(value,obType,outresult,setError);
399415
}
400416

401-
if(obType.IsEnum)
402-
{
403-
returnToEnum(value,obType,outresult,setError);
404-
}
405-
406417
// Conversion to 'Object' is done based on some reasonable default
407418
// conversions (Python string -> managed string, Python int -> Int32 etc.).
408419
if(obType==objectType)
@@ -497,7 +508,7 @@ internal static bool ToManagedValue(IntPtr value, Type obType,
497508
}
498509

499510
TypeCodetypeCode=Type.GetTypeCode(obType);
500-
if(typeCode==TypeCode.Object)
511+
if(typeCode==TypeCode.Object||obType.IsEnum)
501512
{
502513
IntPtrpyType=Runtime.PyObject_TYPE(value);
503514
if(PyObjectConversions.TryDecode(value,pyType,obType,outresult))
@@ -516,8 +527,17 @@ internal static bool ToManagedValue(IntPtr value, Type obType,
516527
/// </summary>
517528
privatestaticboolToPrimitive(IntPtrvalue,TypeobType,outobjectresult,boolsetError)
518529
{
519-
TypeCodetc=Type.GetTypeCode(obType);
520530
result=null;
531+
if(obType.IsEnum)
532+
{
533+
if(setError)
534+
{
535+
Exceptions.SetError(Exceptions.TypeError,"since Python.NET 3.0 int can not be converted to Enum implicitly. Use Enum(int_value)");
536+
}
537+
returnfalse;
538+
}
539+
540+
TypeCodetc=Type.GetTypeCode(obType);
521541
IntPtrop=IntPtr.Zero;
522542

523543
switch(tc)
@@ -876,40 +896,6 @@ private static bool ToArray(IntPtr value, Type obType, out object result, bool s
876896
result=items;
877897
returntrue;
878898
}
879-
880-
881-
/// <summary>
882-
/// Convert a Python value to a correctly typed managed enum instance.
883-
/// </summary>
884-
privatestaticboolToEnum(IntPtrvalue,TypeobType,outobjectresult,boolsetError)
885-
{
886-
Typeetype=Enum.GetUnderlyingType(obType);
887-
result=null;
888-
889-
if(!ToPrimitive(value,etype,outresult,setError))
890-
{
891-
returnfalse;
892-
}
893-
894-
if(Enum.IsDefined(obType,result))
895-
{
896-
result=Enum.ToObject(obType,result);
897-
returntrue;
898-
}
899-
900-
if(obType.GetCustomAttributes(flagsType,true).Length>0)
901-
{
902-
result=Enum.ToObject(obType,result);
903-
returntrue;
904-
}
905-
906-
if(setError)
907-
{
908-
Exceptions.SetError(Exceptions.ValueError,"invalid enumeration value");
909-
}
910-
911-
returnfalse;
912-
}
913899
}
914900

915901
publicstaticclassConverterExtension

‎src/runtime/exceptions.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -343,7 +343,7 @@ public static void Clear()
343343
publicstaticvoidwarn(stringmessage,IntPtrexception,intstacklevel)
344344
{
345345
if(exception==IntPtr.Zero||
346-
(Runtime.PyObject_IsSubclass(exception,Exceptions.Warning)!=1))
346+
(Runtime.PyObject_IsSubclass(newBorrowedReference(exception),newBorrowedReference(Exceptions.Warning))!=1))
347347
{
348348
Exceptions.RaiseTypeError("Invalid exception");
349349
}

‎src/runtime/pylong.cs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -188,11 +188,8 @@ public PyLong(string value) : base(FromString(value))
188188

189189

190190
/// <summary>
191-
/// IsLongType Method
192-
/// </summary>
193-
/// <remarks>
194191
/// Returns true if the given object is a Python long.
195-
/// </remarks>
192+
/// </summary>
196193
publicstaticboolIsLongType(PyObjectvalue)
197194
{
198195
returnRuntime.PyLong_Check(value.obj);

‎src/runtime/pyobject.cs

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -934,17 +934,21 @@ public bool IsInstance(PyObject typeOrClass)
934934

935935

936936
/// <summary>
937-
/// IsSubclass Method
938-
/// </summary>
939-
/// <remarks>
940-
/// Return true if the object is identical to or derived from the
937+
/// Return <c>true</c> if the object is identical to or derived from the
941938
/// given Python type or class. This method always succeeds.
942-
/// </remarks>
939+
/// </summary>
943940
publicboolIsSubclass(PyObjecttypeOrClass)
944941
{
945942
if(typeOrClass==null)thrownewArgumentNullException(nameof(typeOrClass));
946943

947-
intr=Runtime.PyObject_IsSubclass(obj,typeOrClass.obj);
944+
returnIsSubclass(typeOrClass.Reference);
945+
}
946+
947+
internalboolIsSubclass(BorrowedReferencetypeOrClass)
948+
{
949+
if(typeOrClass.IsNull)thrownewArgumentNullException(nameof(typeOrClass));
950+
951+
intr=Runtime.PyObject_IsSubclass(Reference,typeOrClass);
948952
if(r<0)
949953
{
950954
Runtime.PyErr_Clear();

‎src/runtime/runtime.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1103,7 +1103,7 @@ internal static int PyObject_Compare(IntPtr value1, IntPtr value2)
11031103
internalstaticintPyObject_IsInstance(IntPtrob,IntPtrtype)=>Delegates.PyObject_IsInstance(ob,type);
11041104

11051105

1106-
internalstaticintPyObject_IsSubclass(IntPtrob,IntPtrtype)=>Delegates.PyObject_IsSubclass(ob,type);
1106+
internalstaticintPyObject_IsSubclass(BorrowedReferenceob,BorrowedReferencetype)=>Delegates.PyObject_IsSubclass(ob,type);
11071107

11081108

11091109
internalstaticintPyCallable_Check(IntPtrpointer)=>Delegates.PyCallable_Check(pointer);
@@ -2313,7 +2313,7 @@ static Delegates()
23132313
PyObject_CallObject=(delegate* unmanaged[Cdecl]<IntPtr,IntPtr,IntPtr>)GetFunctionByName(nameof(PyObject_CallObject),GetUnmanagedDll(_PythonDll));
23142314
PyObject_RichCompareBool=(delegate* unmanaged[Cdecl]<IntPtr,IntPtr,int,int>)GetFunctionByName(nameof(PyObject_RichCompareBool),GetUnmanagedDll(_PythonDll));
23152315
PyObject_IsInstance=(delegate* unmanaged[Cdecl]<IntPtr,IntPtr,int>)GetFunctionByName(nameof(PyObject_IsInstance),GetUnmanagedDll(_PythonDll));
2316-
PyObject_IsSubclass=(delegate* unmanaged[Cdecl]<IntPtr,IntPtr,int>)GetFunctionByName(nameof(PyObject_IsSubclass),GetUnmanagedDll(_PythonDll));
2316+
PyObject_IsSubclass=(delegate* unmanaged[Cdecl]<BorrowedReference,BorrowedReference,int>)GetFunctionByName(nameof(PyObject_IsSubclass),GetUnmanagedDll(_PythonDll));
23172317
PyCallable_Check=(delegate* unmanaged[Cdecl]<IntPtr,int>)GetFunctionByName(nameof(PyCallable_Check),GetUnmanagedDll(_PythonDll));
23182318
PyObject_IsTrue=(delegate* unmanaged[Cdecl]<BorrowedReference,int>)GetFunctionByName(nameof(PyObject_IsTrue),GetUnmanagedDll(_PythonDll));
23192319
PyObject_Not=(delegate* unmanaged[Cdecl]<IntPtr,int>)GetFunctionByName(nameof(PyObject_Not),GetUnmanagedDll(_PythonDll));
@@ -2585,7 +2585,7 @@ static Delegates()
25852585
internalstaticdelegate* unmanaged[Cdecl]<IntPtr,IntPtr,IntPtr>PyObject_CallObject{get;}
25862586
internalstaticdelegate* unmanaged[Cdecl]<IntPtr,IntPtr,int,int>PyObject_RichCompareBool{get;}
25872587
internalstaticdelegate* unmanaged[Cdecl]<IntPtr,IntPtr,int>PyObject_IsInstance{get;}
2588-
internalstaticdelegate* unmanaged[Cdecl]<IntPtr,IntPtr,int>PyObject_IsSubclass{get;}
2588+
internalstaticdelegate* unmanaged[Cdecl]<BorrowedReference,BorrowedReference,int>PyObject_IsSubclass{get;}
25892589
internalstaticdelegate* unmanaged[Cdecl]<IntPtr,int>PyCallable_Check{get;}
25902590
internalstaticdelegate* unmanaged[Cdecl]<BorrowedReference,int>PyObject_IsTrue{get;}
25912591
internalstaticdelegate* unmanaged[Cdecl]<IntPtr,int>PyObject_Not{get;}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp