Skip to content

Commit eec30c0

Browse files
committed
make methods of PyObject inherited from its base C# classes GIL-safe
fixes #1642
1 parent 303378a commit eec30c0

File tree

6 files changed

+28
-16
lines changed

6 files changed

+28
-16
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
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.
@@ -69,6 +70,7 @@ One must now either use enum members (e.g. `MyEnum.Option`), or use enum constru
6970
- BREAKING: Names of .NET types (e.g. `str(__class__)`) changed to better support generic types
7071
- BREAKING: overload resolution will no longer prefer basic types. Instead, first matching overload will
7172
be chosen.
73+
- BREAKING: acquiring GIL using `Py.GIL` no longer forces `PythonEngine` to initialize
7274
- BREAKING: `Exec` and `Eval` from `PythonEngine` no longer accept raw pointers.
7375
- BREAKING: .NET collections and arrays are no longer automatically converted to
7476
Python collections. Instead, they implement standard Python

src/embed_tests/Codecs.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -473,7 +473,7 @@ public DecoderReturningPredefinedValue(PyObject objectType, TTarget decodeResult
473473
}
474474

475475
public bool CanDecode(PyType objectType, Type targetType)
476-
=> objectType.Handle == TheOnlySupportedSourceType.Handle
476+
=> PythonReferenceComparer.Instance.Equals(objectType, TheOnlySupportedSourceType)
477477
&& targetType == typeof(TTarget);
478478
public bool TryDecode<T>(PyObject pyObj, out T value)
479479
{

src/embed_tests/TestPyObject.cs

Lines changed: 7 additions & 0 deletions
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

Lines changed: 1 addition & 1 deletion
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/Py.cs

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,7 @@ namespace Python.Runtime;
1010

1111
public static class Py
1212
{
13-
public static GILState GIL()
14-
{
15-
if (!PythonEngine.IsInitialized)
16-
{
17-
PythonEngine.Initialize();
18-
}
19-
20-
return PythonEngine.DebugGIL ? new DebugGILState() : new GILState();
21-
}
13+
public static GILState GIL() => PythonEngine.DebugGIL ? new DebugGILState() : new GILState();
2214

2315
public static PyModule CreateScope() => new();
2416
public static PyModule CreateScope(string name)

src/runtime/PythonTypes/PyObject.cs

Lines changed: 16 additions & 5 deletions
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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy