Skip to content

Commit ea2469f

Browse files
committed
Simplify the setattr and getattr functions a bit
1 parent 522573e commit ea2469f

2 files changed

Lines changed: 50 additions & 84 deletions

File tree

src/runtime/TypeManager.cs

Lines changed: 49 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using System.Reflection;
66
using System.Runtime.InteropServices;
77
using System.Diagnostics;
8+
89
using Python.Runtime.Native;
910
using Python.Runtime.StateSerialization;
1011

@@ -76,74 +77,48 @@ static bool TryGetDynamicInstance(BorrowedReference ob, out object instance, out
7677
return false;
7778
}
7879

79-
static bool TryGetManagedDynamicInstance(BorrowedReference ob, out object instance, out IDynamicMetaObjectProvider dynamicObject)
80+
public static NewReference tp_getattro_dlr_proxy(BorrowedReference ob, BorrowedReference key)
8081
{
81-
if (ManagedType.GetManagedObject(ob) is CLRObject co && co.inst is IDynamicMetaObjectProvider coDynamic)
82+
var isDynamic = TryGetDynamicInstance(ob, out object instance, out IDynamicMetaObjectProvider dynamicObject);
83+
84+
// The whole DLR machinery only makes sense with string keys and dynamic objects
85+
if (!isDynamic || !Runtime.PyString_Check(key))
8286
{
83-
instance = co.inst;
84-
dynamicObject = coDynamic;
85-
return true;
87+
return Runtime.PyObject_GenericGetAttr(ob, key);
8688
}
8789

88-
instance = null!;
89-
dynamicObject = null!;
90-
return false;
91-
}
92-
93-
public static NewReference tp_getattro_dlr_proxy(BorrowedReference ob, BorrowedReference key)
94-
{
95-
bool hasStringKey = Runtime.PyString_Check(key);
96-
string? memberName = hasStringKey ? Runtime.GetManagedString(key) : null;
90+
string memberName = Runtime.GetManagedString(key)!;
9791

98-
if (memberName is not null && TryGetManagedDynamicInstance(ob, out object preInstance, out var preDynamicObject))
92+
// Forward requests to GetDynamicMemberNames to the mixin implementation
93+
if (memberName == nameof(DynamicObjectMemberAccessor.GetDynamicMemberNames)
94+
&& !HasClrMember(instance, memberName))
9995
{
100-
if (memberName == nameof(DynamicObjectMemberAccessor.GetDynamicMemberNames)
101-
&& !HasClrMember(preInstance, memberName))
102-
{
103-
using var pyMemberNames = new Func<IReadOnlyCollection<string>>(
104-
() => dynamicMemberAccessor.GetDynamicMemberNames(preDynamicObject)).ToPython();
105-
return pyMemberNames.NewReferenceOrNull();
106-
}
96+
using var pyMemberNames = new Func<IReadOnlyCollection<string>>(
97+
() => dynamicMemberAccessor.GetDynamicMemberNames(dynamicObject)
98+
).ToPython();
99+
return pyMemberNames.NewReferenceOrNull();
107100
}
108101

102+
// Now, first try to access the Python attribute
109103
var attr = Runtime.PyObject_GenericGetAttr(ob, key);
110104
if (!attr.IsNull())
111-
{
112105
return attr;
113-
}
114-
115-
if (memberName == null)
116-
{
117-
return default;
118-
}
119106

107+
// attr is null, so an exception must be set. If that exception is not an AttributeError,
108+
// we return from this function immediately without clearing. All later returns until the
109+
// very end will lead to the AttributeError getting raised.
120110
if (Runtime.PyErr_ExceptionMatches(Exceptions.AttributeError) == 0)
121111
{
122112
return default;
123113
}
124114

125-
if (!TryGetManagedDynamicInstance(ob, out object instance, out var dynamicObject))
115+
if (HasClrMember(instance, memberName) || IsPythonSpecialAttributeName(memberName))
126116
{
127117
return default;
128118
}
129119

130-
if (memberName is null)
131-
{
132-
return default;
133-
}
134-
135-
if (HasClrMember(instance, memberName))
136-
{
137-
return default;
138-
}
139-
140-
if (IsPythonSpecialAttributeName(memberName))
141-
{
142-
return default;
143-
}
144-
145-
bool resolved;
146-
object? value;
120+
bool resolved = false;
121+
object? value = null;
147122
try
148123
{
149124
resolved = dynamicMemberAccessor.TryGetMember(dynamicObject, memberName, out value);
@@ -166,64 +141,55 @@ public static NewReference tp_getattro_dlr_proxy(BorrowedReference ob, BorrowedR
166141

167142
public static int tp_setattro_dlr_proxy(BorrowedReference ob, BorrowedReference key, BorrowedReference val)
168143
{
169-
string? memberName = Runtime.PyString_Check(key) ? Runtime.GetManagedString(key) : null;
170-
bool hasDynamicInstance = TryGetDynamicInstance(ob, out object instance, out var dynamicObject);
171-
bool canUseDynamic = hasDynamicInstance
172-
&& memberName is not null
173-
&& !HasClrMember(instance, memberName);
144+
var isDynamic = TryGetDynamicInstance(ob, out object instance, out IDynamicMetaObjectProvider dynamicObject);
145+
146+
// The whole DLR machinery only makes sense with string keys and dynamic objects
147+
if (!isDynamic || !Runtime.PyString_Check(key))
148+
{
149+
return Runtime.PyObject_GenericSetAttr(ob, key, val);
150+
}
151+
152+
string memberName = Runtime.GetManagedString(key)!;
174153

175154
// For Python-derived types (IPythonDerivedType), the Python descriptor protocol
176-
// (e.g. @property setters) takes priority over DLR member storage. Try
177-
// PyObject_GenericSetAttr first; only fall back to DLR on AttributeError.
178-
if (canUseDynamic && instance is IPythonDerivedType)
155+
// (e.g. @property setters) takes priority over DLR member storage.
156+
if (instance is IPythonDerivedType)
179157
{
180158
int pyResult = Runtime.PyObject_GenericSetAttr(ob, key, val);
181-
if (pyResult == 0) return 0;
182-
if (Runtime.PyErr_ExceptionMatches(Exceptions.AttributeError) == 0) return pyResult;
159+
if (pyResult == 0)
160+
return 0;
161+
162+
if (Runtime.PyErr_ExceptionMatches(Exceptions.AttributeError) == 0)
163+
return pyResult;
164+
183165
Runtime.PyErr_Clear();
184-
// fall through to DLR path below
166+
// Fall through to DLR fallback below
185167
}
186168

187-
if (canUseDynamic)
169+
if (!HasClrMember(instance, memberName) && !IsPythonSpecialAttributeName(memberName))
188170
{
171+
// Try DLR member storage first
172+
bool handled = false;
173+
189174
if (val == null)
190175
{
191-
if (dynamicMemberAccessor.TryDeleteMember(dynamicObject, memberName!))
192-
{
193-
Runtime.PyErr_Clear();
194-
return 0;
195-
}
176+
handled = dynamicMemberAccessor.TryDeleteMember(dynamicObject, memberName);
196177
}
197178
else
198179
{
199180
object? managedValue = null;
200181
if (val != Runtime.PyNone && !Converter.ToManaged(val, typeof(object), out managedValue, true))
201-
{
202182
return -1;
203-
}
204183

205-
if (dynamicMemberAccessor.TrySetMember(dynamicObject, memberName!, managedValue))
206-
{
207-
Runtime.PyErr_Clear();
208-
return 0;
209-
}
184+
handled = dynamicMemberAccessor.TrySetMember(dynamicObject, memberName, managedValue);
210185
}
211-
}
212186

213-
int result = Runtime.PyObject_GenericSetAttr(ob, key, val);
214-
if (result == 0 && canUseDynamic)
215-
{
216-
object? managedValue = null;
217-
if (val != null && val != Runtime.PyNone && !Converter.ToManaged(val, typeof(object), out managedValue, true))
218-
{
187+
if (handled)
219188
return 0;
220-
}
221-
222-
dynamicMemberAccessor.TrySetMember(dynamicObject, memberName!, managedValue);
223-
Runtime.PyErr_Clear();
224189
}
225190

226-
return result;
191+
// Fall back to Python attribute setting
192+
return Runtime.PyObject_GenericSetAttr(ob, key, val);
227193
}
228194

229195
internal static void Initialize()

src/runtime/Types/ClassDerived.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ static ClassDerivedObject()
4141
moduleBuilders = new Dictionary<Tuple<string, string>, ModuleBuilder>();
4242
}
4343

44-
public static new void Reset()
44+
public static void Reset()
4545
{
4646
assemblyBuilders = new Dictionary<string, AssemblyBuilder>();
4747
moduleBuilders = new Dictionary<Tuple<string, string>, ModuleBuilder>();

0 commit comments

Comments
 (0)