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

Commitaba6217

Browse files
committed
allow .NET classes to override __getattr__ and __setattr__
1 parenta8a9426 commitaba6217

File tree

6 files changed

+137
-14
lines changed

6 files changed

+137
-14
lines changed

‎CHANGELOG.md

Lines changed: 1 addition & 0 deletions
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
- Added automatic NuGet package generation in appveyor and local builds
13+
- Added IGetAttr and ISetAttr, so that .NET classes could override__getattr__ and__setattr__
1314

1415
###Changed
1516

‎src/embed_tests/Python.EmbeddingTest.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@
8989
<CompileInclude="TestDomainReload.cs" />
9090
<CompileInclude="TestExample.cs" />
9191
<CompileInclude="TestFinalizer.cs" />
92+
<CompileInclude="TestInstanceWrapping.cs" />
9293
<CompileInclude="TestPyAnsiString.cs" />
9394
<CompileInclude="TestPyFloat.cs" />
9495
<CompileInclude="TestPyInt.cs" />
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
usingNUnit.Framework;
2+
usingPython.Runtime;
3+
usingPython.Runtime.Slots;
4+
5+
namespacePython.EmbeddingTest{
6+
publicclassTestInstanceWrapping{
7+
[OneTimeSetUp]
8+
publicvoidSetUp(){
9+
PythonEngine.Initialize();
10+
}
11+
12+
[OneTimeTearDown]
13+
publicvoidDispose(){
14+
PythonEngine.Shutdown();
15+
}
16+
17+
[Test]
18+
publicvoidGetAttrCanBeOverriden(){
19+
varoverloaded=newOverloaded();
20+
using(Py.GIL()){
21+
varo=overloaded.ToPython();
22+
dynamicgetNonexistingAttr=PythonEngine.Eval("lambda o: o.non_existing_attr");
23+
stringnonexistentAttrValue=getNonexistingAttr(o);
24+
Assert.AreEqual(GetAttrFallbackValue,nonexistentAttrValue);
25+
}
26+
}
27+
28+
[Test]
29+
publicvoidSetAttrCanBeOverriden(){
30+
varoverloaded=newOverloaded();
31+
using(Py.GIL())
32+
using(varscope=Py.CreateScope()){
33+
varo=overloaded.ToPython();
34+
scope.Set(nameof(o),o);
35+
scope.Exec($"{nameof(o)}.non_existing_attr = 42");
36+
Assert.AreEqual(42,overloaded.Value);
37+
}
38+
}
39+
40+
conststringGetAttrFallbackValue="undefined";
41+
42+
classBase{}
43+
classDerived:Base{}
44+
45+
classOverloaded:Derived,IGetAttr,ISetAttr
46+
{
47+
publicintValue{get;privateset;}
48+
49+
publicboolTryGetAttr(stringname,outPyObjectvalue){
50+
value=GetAttrFallbackValue.ToPython();
51+
returntrue;
52+
}
53+
54+
publicboolTrySetAttr(stringname,PyObjectvalue){
55+
this.Value=value.As<int>();
56+
returntrue;
57+
}
58+
}
59+
}
60+
}

‎src/runtime/Python.Runtime.csproj

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -22,19 +22,19 @@
2222
</PropertyGroup>
2323
<!--We can relax binding to platform because code references no any platform dependent assemblies-->
2424
<!--This will allows to use any build of this assebly as a compile ref assebly-->
25-
<!--<PropertyGroup Condition=" '$(Platform)' == 'x86'">
26-
<PlatformTarget>x86</PlatformTarget>
27-
</PropertyGroup>
28-
<PropertyGroup Condition=" '$(Platform)' == 'x64'">
29-
<PlatformTarget>x64</PlatformTarget>
25+
<!--<PropertyGroup Condition=" '$(Platform)' == 'x86'">
26+
<PlatformTarget>x86</PlatformTarget>
27+
</PropertyGroup>
28+
<PropertyGroup Condition=" '$(Platform)' == 'x64'">
29+
<PlatformTarget>x64</PlatformTarget>
3030
</PropertyGroup>-->
3131
<PropertyGroupCondition=" '$(Configuration)' == 'ReleaseMono'">
3232
<DefineConstantsCondition="'$(DefineConstants)' == ''">PYTHON2;PYTHON27;UCS4</DefineConstants>
3333
<Optimize>true</Optimize>
3434
<DebugType>pdbonly</DebugType>
3535
</PropertyGroup>
3636
<PropertyGroupCondition=" '$(Configuration)' == 'ReleaseMonoPY3'">
37-
<DefineConstantsCondition="'$(DefineConstants)' == ''">PYTHON3;PYTHON37;UCS4</DefineConstants>
37+
<DefineConstantsCondition="'$(DefineConstants)' == ''">PYTHON3;PYTHON37;UCS4</DefineConstants>
3838
<Optimize>true</Optimize>
3939
<DebugType>pdbonly</DebugType>
4040
</PropertyGroup>
@@ -46,7 +46,7 @@
4646
</PropertyGroup>
4747
<PropertyGroupCondition=" '$(Configuration)' == 'DebugMonoPY3'">
4848
<DebugSymbols>true</DebugSymbols>
49-
<DefineConstantsCondition="'$(DefineConstants)' == ''">PYTHON3;PYTHON37;UCS4;TRACE;DEBUG</DefineConstants>
49+
<DefineConstantsCondition="'$(DefineConstants)' == ''">PYTHON3;PYTHON37;UCS4;TRACE;DEBUG</DefineConstants>
5050
<Optimize>false</Optimize>
5151
<DebugType>full</DebugType>
5252
</PropertyGroup>
@@ -56,7 +56,7 @@
5656
<DebugType>pdbonly</DebugType>
5757
</PropertyGroup>
5858
<PropertyGroupCondition=" '$(Configuration)' == 'ReleaseWinPY3'">
59-
<DefineConstantsCondition="'$(DefineConstants)' == ''">PYTHON3;PYTHON37;UCS2</DefineConstants>
59+
<DefineConstantsCondition="'$(DefineConstants)' == ''">PYTHON3;PYTHON37;UCS2</DefineConstants>
6060
<Optimize>true</Optimize>
6161
<DebugType>pdbonly</DebugType>
6262
</PropertyGroup>
@@ -68,7 +68,7 @@
6868
</PropertyGroup>
6969
<PropertyGroupCondition=" '$(Configuration)' == 'DebugWinPY3'">
7070
<DebugSymbols>true</DebugSymbols>
71-
<DefineConstantsCondition="'$(DefineConstants)' == ''">PYTHON3;PYTHON37;UCS2;TRACE;DEBUG</DefineConstants>
71+
<DefineConstantsCondition="'$(DefineConstants)' == ''">PYTHON3;PYTHON37;UCS2;TRACE;DEBUG</DefineConstants>
7272
<Optimize>false</Optimize>
7373
<DebugType>full</DebugType>
7474
</PropertyGroup>
@@ -137,11 +137,12 @@
137137
<CompileInclude="pythonexception.cs" />
138138
<CompileInclude="pytuple.cs" />
139139
<CompileInclude="runtime.cs" />
140+
<CompileInclude="slots.cs" />
140141
<CompileInclude="typemanager.cs" />
141142
<CompileInclude="typemethod.cs" />
142143
<CompileInclude="Util.cs" />
143-
<CompileInclude="platform\Types.cs" />
144-
<CompileInclude="platform\LibraryLoader.cs" />
144+
<CompileInclude="platform\Types.cs" />
145+
<CompileInclude="platform\LibraryLoader.cs" />
145146
</ItemGroup>
146147
<ItemGroupCondition=" '$(PythonInteropFile)' != ''">
147148
<CompileInclude="$(PythonInteropFile)" />
@@ -151,7 +152,7 @@
151152
<CompileInclude="interop34.cs" />
152153
<CompileInclude="interop35.cs" />
153154
<CompileInclude="interop36.cs" />
154-
<CompileInclude="interop37.cs" />
155+
<CompileInclude="interop37.cs" />
155156
</ItemGroup>
156157
<ItemGroup>
157158
<NoneInclude="..\pythonnet.snk" />
@@ -170,4 +171,4 @@
170171
<CopySourceFiles="$(TargetAssembly)"DestinationFolder="$(PythonBuildDir)" />
171172
<!--Copy SourceFiles="$(TargetAssemblyPdb)" Condition="Exists('$(TargetAssemblyPdb)')" DestinationFolder="$(PythonBuildDir)" /-->
172173
</Target>
173-
</Project>
174+
</Project>

‎src/runtime/slots.cs

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
usingSystem;
2+
3+
namespacePython.Runtime.Slots
4+
{
5+
/// <summary>
6+
/// Implement this interface to override Python's __getattr__ for your class
7+
/// </summary>
8+
publicinterfaceIGetAttr{
9+
boolTryGetAttr(stringname,outPyObjectvalue);
10+
}
11+
12+
/// <summary>
13+
/// Implement this interface to override Python's __setattr__ for your class
14+
/// </summary>
15+
publicinterfaceISetAttr{
16+
boolTrySetAttr(stringname,PyObjectvalue);
17+
}
18+
19+
staticclassSlotOverrides{
20+
publicstaticIntPtrtp_getattro(IntPtrob,IntPtrkey){
21+
IntPtrgenericResult=Runtime.PyObject_GenericGetAttr(ob,key);
22+
if(genericResult!=IntPtr.Zero||!Runtime.PyString_Check(key)){
23+
returngenericResult;
24+
}
25+
26+
Exceptions.Clear();
27+
28+
varself=(IGetAttr)((CLRObject)ManagedType.GetManagedObject(ob)).inst;
29+
stringattr=Runtime.GetManagedString(key);
30+
returnself.TryGetAttr(attr,outvarvalue)
31+
?value.Handle
32+
:Runtime.PyObject_GenericGetAttr(ob,key);
33+
}
34+
35+
publicstaticinttp_setattro(IntPtrob,IntPtrkey,IntPtrval){
36+
if(!Runtime.PyString_Check(key)){
37+
returnRuntime.PyObject_GenericSetAttr(ob,key,val);
38+
}
39+
40+
varself=(ISetAttr)((CLRObject)ManagedType.GetManagedObject(ob)).inst;
41+
stringattr=Runtime.GetManagedString(key);
42+
returnself.TrySetAttr(attr,newPyObject(val))
43+
?0
44+
:Runtime.PyObject_GenericSetAttr(ob,key,val);
45+
}
46+
}
47+
}

‎src/runtime/typemanager.cs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@
44
usingSystem.Reflection;
55
usingSystem.Runtime.InteropServices;
66
usingPython.Runtime.Platform;
7+
usingPython.Runtime.Slots;
78

89
namespacePython.Runtime
910
{
10-
1111
/// <summary>
1212
/// The TypeManager class is responsible for building binary-compatible
1313
/// Python type objects that are implemented in managed code.
@@ -155,6 +155,14 @@ internal static IntPtr CreateType(ManagedType impl, Type clrType)
155155

156156
InitializeSlots(type,impl.GetType());
157157

158+
if(typeof(IGetAttr).IsAssignableFrom(clrType)){
159+
InitializeSlot(type,TypeOffset.tp_getattro,typeof(SlotOverrides).GetMethod(nameof(SlotOverrides.tp_getattro)));
160+
}
161+
162+
if(typeof(ISetAttr).IsAssignableFrom(clrType)){
163+
InitializeSlot(type,TypeOffset.tp_setattro,typeof(SlotOverrides).GetMethod(nameof(SlotOverrides.tp_setattro)));
164+
}
165+
158166
if(base_!=IntPtr.Zero)
159167
{
160168
Marshal.WriteIntPtr(type,TypeOffset.tp_base,base_);
@@ -779,6 +787,11 @@ static void InitializeSlot(IntPtr type, IntPtr slot, string name)
779787
Marshal.WriteIntPtr(type,offset,slot);
780788
}
781789

790+
staticvoidInitializeSlot(IntPtrtype,intslotOffset,MethodInfomethod){
791+
IntPtrthunk=Interop.GetThunk(method);
792+
Marshal.WriteIntPtr(type,slotOffset,thunk);
793+
}
794+
782795
/// <summary>
783796
/// Given a newly allocated Python type object and a managed Type that
784797
/// implements it, initialize any methods defined by the Type that need

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp