Skip to content

Commit 12501f8

Browse files
committed
make methods of PyObject inherited from its base C# classes GIL-safe
fixes #1642
1 parent 7637884 commit 12501f8

File tree

4 files changed

+25
-6
lines changed

4 files changed

+25
-6
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ details about the cause of the failure
4747
- floating point values passed from Python are no longer silently truncated
4848
when .NET expects an integer [#1342][i1342]
4949
- More specific error messages for method argument mismatch
50+
- members of `PyObject` inherited from `System.Object and `DynamicObject` now autoacquire GIL
5051
- BREAKING: when inheriting from .NET types in Python if you override `__init__` you
5152
must explicitly call base constructor using `super().__init__(.....)`. Not doing so will lead
5253
to undefined behavior.

src/embed_tests/TestPyObject.cs

+7
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,13 @@ public void GetAttrDefault_IgnoresAttributeErrorOnly()
9494
);
9595
Assert.AreEqual(Exceptions.TypeError, typeErrResult.Type);
9696
}
97+
98+
// regression test from https://github.com/pythonnet/pythonnet/issues/1642
99+
[Test]
100+
public void InheritedMethodsAutoacquireGIL()
101+
{
102+
PythonEngine.Exec("from System import String\nString.Format('{0},{1}', 1, 2)");
103+
}
97104
}
98105

99106
public class PyObjectTestMethods

src/runtime/InternString.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ public static void Initialize()
3939
{
4040
NewReference pyStr = Runtime.PyUnicode_InternFromString(name);
4141
var op = new PyString(pyStr.StealOrThrow());
42-
Debug.Assert(name == op.ToString());
42+
Debug.Assert(name == op.As<string>());
4343
SetIntern(name, op);
4444
var field = type.GetField("f" + name, PyIdentifierFieldFlags)!;
4545
field.SetValue(null, op.rawPtr);

src/runtime/PythonTypes/PyObject.cs

+16-5
Original file line numberDiff line numberDiff line change
@@ -1056,6 +1056,7 @@ public PyList Dir()
10561056
/// </remarks>
10571057
public override string? ToString()
10581058
{
1059+
using var _ = Py.GIL();
10591060
using var strval = Runtime.PyObject_Str(obj);
10601061
return Runtime.GetManagedString(strval.BorrowOrThrow());
10611062
}
@@ -1072,7 +1073,11 @@ public PyList Dir()
10721073
/// Return true if this object is equal to the given object. This
10731074
/// method is based on Python equality semantics.
10741075
/// </remarks>
1075-
public override bool Equals(object o) => Equals(o as PyObject);
1076+
public override bool Equals(object o)
1077+
{
1078+
using var _ = Py.GIL();
1079+
return Equals(o as PyObject);
1080+
}
10761081

10771082
public virtual bool Equals(PyObject? other)
10781083
{
@@ -1101,6 +1106,7 @@ public virtual bool Equals(PyObject? other)
11011106
/// </remarks>
11021107
public override int GetHashCode()
11031108
{
1109+
using var _ = Py.GIL();
11041110
nint pyHash = Runtime.PyObject_Hash(obj);
11051111
if (pyHash == -1 && Exceptions.ErrorOccurred())
11061112
{
@@ -1135,12 +1141,14 @@ public long Refcount
11351141

11361142
public override bool TryGetMember(GetMemberBinder binder, out object? result)
11371143
{
1144+
using var _ = Py.GIL();
11381145
result = CheckNone(this.GetAttr(binder.Name));
11391146
return true;
11401147
}
11411148

11421149
public override bool TrySetMember(SetMemberBinder binder, object? value)
11431150
{
1151+
using var _ = Py.GIL();
11441152
using var newVal = Converter.ToPythonDetectType(value);
11451153
int r = Runtime.PyObject_SetAttrString(obj, binder.Name, newVal.Borrow());
11461154
if (r < 0)
@@ -1234,6 +1242,7 @@ private static NewReference GetPythonObject(object? target)
12341242

12351243
public override bool TryInvokeMember(InvokeMemberBinder binder, object?[] args, out object? result)
12361244
{
1245+
using var _ = Py.GIL();
12371246
if (this.HasAttr(binder.Name) && this.GetAttr(binder.Name).IsCallable())
12381247
{
12391248
PyTuple? pyargs = null;
@@ -1258,6 +1267,7 @@ public override bool TryInvokeMember(InvokeMemberBinder binder, object?[] args,
12581267

12591268
public override bool TryInvoke(InvokeBinder binder, object?[] args, out object? result)
12601269
{
1270+
using var _ = Py.GIL();
12611271
if (this.IsCallable())
12621272
{
12631273
PyTuple? pyargs = null;
@@ -1282,6 +1292,7 @@ public override bool TryInvoke(InvokeBinder binder, object?[] args, out object?
12821292

12831293
public override bool TryConvert(ConvertBinder binder, out object? result)
12841294
{
1295+
using var _ = Py.GIL();
12851296
// always try implicit conversion first
12861297
if (Converter.ToManaged(this.obj, binder.Type, out result, false))
12871298
{
@@ -1307,6 +1318,7 @@ public override bool TryConvert(ConvertBinder binder, out object? result)
13071318

13081319
public override bool TryBinaryOperation(BinaryOperationBinder binder, object arg, out object? result)
13091320
{
1321+
using var _ = Py.GIL();
13101322
NewReference res;
13111323
if (!(arg is PyObject))
13121324
{
@@ -1419,6 +1431,7 @@ public override bool TryBinaryOperation(BinaryOperationBinder binder, object arg
14191431

14201432
public override bool TryUnaryOperation(UnaryOperationBinder binder, out object? result)
14211433
{
1434+
using var _ = Py.GIL();
14221435
int r;
14231436
NewReference res;
14241437
switch (binder.Operation)
@@ -1463,10 +1476,8 @@ public override bool TryUnaryOperation(UnaryOperationBinder binder, out object?
14631476
/// <returns>A sequence that contains dynamic member names.</returns>
14641477
public override IEnumerable<string> GetDynamicMemberNames()
14651478
{
1466-
foreach (PyObject pyObj in Dir())
1467-
{
1468-
yield return pyObj.ToString()!;
1469-
}
1479+
using var _ = Py.GIL();
1480+
return Dir().Select(pyObj => pyObj.ToString()!).ToArray();
14701481
}
14711482

14721483
void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)

0 commit comments

Comments
 (0)