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

Commitf77b131

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- flags enums support bitwise ops via EnumOps class
1 parent6f1219f commitf77b131

17 files changed

+316
-147
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/embed_tests/TestOperator.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,14 @@ public void SymmetricalOperatorOverloads()
335335
");
336336
}
337337

338+
[Test]
339+
publicvoidEnumOperator()
340+
{
341+
PythonEngine.Exec($@"
342+
from System.IO import FileAccess
343+
c = FileAccess.Read | FileAccess.Write");
344+
}
345+
338346
[Test]
339347
publicvoidOperatorOverloadMissingArgument()
340348
{

‎src/runtime/Codecs/EnumPyLongCodec.cs

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
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)||typeof(T).IsFlagsEnum())
41+
{
42+
value=(T)Enum.ToObject(typeof(T),result);
43+
returntrue;
44+
}
45+
46+
returnfalse;
47+
}
48+
49+
publicPyObjectTryEncode(objectvalue)
50+
{
51+
if(valueisnull)returnnull;
52+
53+
varenumType=value.GetType();
54+
if(!enumType.IsEnum)returnnull;
55+
56+
try
57+
{
58+
returnnewPyLong((long)value);
59+
}
60+
catch(InvalidCastException)
61+
{
62+
returnnewPyLong((ulong)value);
63+
}
64+
}
65+
66+
privateEnumPyLongCodec(){}
67+
}
68+
}

‎src/runtime/classmanager.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -403,6 +403,17 @@ private static ClassInfo GetClassInfo(Type type)
403403
}
404404
}
405405

406+
// only [Flags] enums support bitwise operations
407+
if(type.IsEnum&&type.IsFlagsEnum())
408+
{
409+
varopsImpl=typeof(EnumOps<>).MakeGenericType(type);
410+
foreach(varopinopsImpl.GetMethods(OpsHelper.BindingFlags))
411+
{
412+
local[op.Name]=1;
413+
}
414+
info=info.Concat(opsImpl.GetMethods(OpsHelper.BindingFlags)).ToArray();
415+
}
416+
406417
// Now again to filter w/o losing overloaded member info
407418
for(i=0;i<info.Length;i++)
408419
{

‎src/runtime/classobject.cs

Lines changed: 42 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,44 @@ 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)&&!type.IsFlagsEnum())
142+
{
143+
Exceptions.SetError(Exceptions.ValueError,"Invalid enumeration value. Pass True as the second argument if unchecked conversion is desired");
144+
returndefault;
145+
}
146+
147+
objectenumValue=Enum.ToObject(type,result);
148+
returnCLRObject.GetInstHandle(enumValue,tp);
112149
}
113150

114151

‎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/operatormethod.cs

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,6 @@ static OperatorMethod()
5151
["op_OnesComplement"]=newSlotDefinition("__invert__",TypeOffset.nb_invert),
5252
["op_UnaryNegation"]=newSlotDefinition("__neg__",TypeOffset.nb_negative),
5353
["op_UnaryPlus"]=newSlotDefinition("__pos__",TypeOffset.nb_positive),
54-
["op_OneComplement"]=newSlotDefinition("__invert__",TypeOffset.nb_invert),
5554
};
5655
ComparisonOpMap=newDictionary<string,string>
5756
{
@@ -80,7 +79,7 @@ public static void Shutdown()
8079

8180
publicstaticboolIsOperatorMethod(MethodBasemethod)
8281
{
83-
if(!method.IsSpecialName)
82+
if(!method.IsSpecialName&&!method.IsOpsHelper())
8483
{
8584
returnfalse;
8685
}
@@ -102,7 +101,12 @@ public static void FixupSlots(IntPtr pyType, Type clrType)
102101
{
103102
constBindingFlagsflags=BindingFlags.Public|BindingFlags.Static;
104103
Debug.Assert(_opType!=null);
105-
foreach(varmethodinclrType.GetMethods(flags))
104+
105+
varstaticMethods=
106+
clrType.IsEnum?typeof(EnumOps<>).MakeGenericType(clrType).GetMethods(flags)
107+
:clrType.GetMethods(flags);
108+
109+
foreach(varmethodinstaticMethods)
106110
{
107111
// We only want to override slots for operators excluding
108112
// comparison operators, which are handled by ClassBase.tp_richcompare.
@@ -170,9 +174,11 @@ public static string ReversePyMethodName(string pyName)
170174
/// <returns></returns>
171175
publicstaticboolIsReverse(MethodInfomethod)
172176
{
173-
TypedeclaringType=method.DeclaringType;
177+
TypeprimaryType=method.IsOpsHelper()
178+
?method.DeclaringType.GetGenericArguments()[0]
179+
:method.DeclaringType;
174180
TypeleftOperandType=method.GetParameters()[0].ParameterType;
175-
returnleftOperandType!=declaringType;
181+
returnleftOperandType!=primaryType;
176182
}
177183

178184
publicstaticvoidFilterMethods(MethodInfo[]methods,outMethodInfo[]forwardMethods,outMethodInfo[]reverseMethods)

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp