From 640e97b41c0cddacc28199560817fae1083f7b39 Mon Sep 17 00:00:00 2001 From: Benedikt Reinartz Date: Sun, 19 Dec 2021 15:01:17 +0100 Subject: [PATCH 01/75] Add Python 3.10 to the list of supported versions --- setup.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/setup.py b/setup.py index 196cb3734..77211e442 100644 --- a/setup.py +++ b/setup.py @@ -8,9 +8,6 @@ import sys, os -PY_MAJOR = sys.version_info[0] -PY_MINOR = sys.version_info[1] - # Disable SourceLink during the build until it can read repo-format v1, #1613 os.environ["EnableSourceControlManagerQueries"] = "false" @@ -169,6 +166,7 @@ def finalize_options(self): "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", "Operating System :: Microsoft :: Windows", "Operating System :: POSIX :: Linux", "Operating System :: MacOS :: MacOS X", From 332cae9ed2ec9b173aa219bf7840d41def429566 Mon Sep 17 00:00:00 2001 From: Victor Nova Date: Sat, 18 Dec 2021 12:28:27 -0800 Subject: [PATCH 02/75] Match generic and private methods upon runtime reload --- CHANGELOG.md | 1 + .../StateSerialization/MethodSerialization.cs | 35 +++++ src/runtime/Reflection/ParameterHelper.cs | 51 ++++++- .../StateSerialization/MaybeMethodBase.cs | 134 ++++++++++-------- src/runtime/runtime_data.cs | 2 +- 5 files changed, 160 insertions(+), 63 deletions(-) create mode 100644 src/embed_tests/StateSerialization/MethodSerialization.cs diff --git a/CHANGELOG.md b/CHANGELOG.md index bce1ec557..0a01e69fc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -103,6 +103,7 @@ Instead, `PyIterable` does that. - Providing an invalid type parameter to a generic type or method produces a helpful Python error - Empty parameter names (as can be generated from F#) do not cause crashes - Unicode strings with surrogates were truncated when converting from Python +- `Reload` mode now supports generic methods (previously Python would stop seeing them after reload) ### Removed diff --git a/src/embed_tests/StateSerialization/MethodSerialization.cs b/src/embed_tests/StateSerialization/MethodSerialization.cs new file mode 100644 index 000000000..0e584fc37 --- /dev/null +++ b/src/embed_tests/StateSerialization/MethodSerialization.cs @@ -0,0 +1,35 @@ +using System.IO; +using System.Reflection; + +using NUnit.Framework; + +using Python.Runtime; + +namespace Python.EmbeddingTest.StateSerialization; + +public class MethodSerialization +{ + [Test] + public void GenericRoundtrip() + { + var method = typeof(MethodTestHost).GetMethod(nameof(MethodTestHost.Generic)); + var maybeMethod = new MaybeMethodBase(method); + var restored = SerializationRoundtrip(maybeMethod); + Assert.IsTrue(restored.Valid); + Assert.AreEqual(method, restored.Value); + } + + static T SerializationRoundtrip(T item) + { + using var buf = new MemoryStream(); + var formatter = RuntimeData.CreateFormatter(); + formatter.Serialize(buf, item); + buf.Position = 0; + return (T)formatter.Deserialize(buf); + } +} + +public class MethodTestHost +{ + public void Generic(T item, T[] array, ref T @ref) { } +} diff --git a/src/runtime/Reflection/ParameterHelper.cs b/src/runtime/Reflection/ParameterHelper.cs index 24fce63b1..bff9f7430 100644 --- a/src/runtime/Reflection/ParameterHelper.cs +++ b/src/runtime/Reflection/ParameterHelper.cs @@ -1,18 +1,20 @@ using System; using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; using System.Reflection; namespace Python.Runtime.Reflection; [Serializable] -struct ParameterHelper : IEquatable +class ParameterHelper : IEquatable { public readonly string TypeName; public readonly ParameterModifier Modifier; + public readonly ParameterHelper[]? GenericArguments; - public ParameterHelper(ParameterInfo tp) + public ParameterHelper(ParameterInfo tp) : this(tp.ParameterType) { - TypeName = tp.ParameterType.AssemblyQualifiedName; Modifier = ParameterModifier.None; if (tp.IsIn && tp.ParameterType.IsByRef) @@ -29,12 +31,55 @@ public ParameterHelper(ParameterInfo tp) } } + public ParameterHelper(Type type) + { + TypeName = type.AssemblyQualifiedName; + if (TypeName is null) + { + if (type.IsByRef || type.IsArray) + { + TypeName = type.IsArray ? "[]" : "&"; + GenericArguments = new[] { new ParameterHelper(type.GetElementType()) }; + } + else + { + Debug.Assert(type.ContainsGenericParameters); + TypeName = $"{type.Assembly}::{type.Namespace}/{type.Name}"; + GenericArguments = type.GenericTypeArguments.Select(t => new ParameterHelper(t)).ToArray(); + } + } + } + + public bool IsSpecialType => TypeName == "&" || TypeName == "[]"; + public bool Equals(ParameterInfo other) { return this.Equals(new ParameterHelper(other)); } public bool Matches(ParameterInfo other) => this.Equals(other); + + public bool Equals(ParameterHelper other) + { + if (other is null) return false; + + if (!(other.TypeName == TypeName && other.Modifier == Modifier)) + return false; + + if (GenericArguments == other.GenericArguments) return true; + + if (GenericArguments is not null && other.GenericArguments is not null) + { + if (GenericArguments.Length != other.GenericArguments.Length) return false; + for (int arg = 0; arg < GenericArguments.Length; arg++) + { + if (!GenericArguments[arg].Equals(other.GenericArguments[arg])) return false; + } + return true; + } + + return false; + } } enum ParameterModifier diff --git a/src/runtime/StateSerialization/MaybeMethodBase.cs b/src/runtime/StateSerialization/MaybeMethodBase.cs index a278df2cf..9fb8ae047 100644 --- a/src/runtime/StateSerialization/MaybeMethodBase.cs +++ b/src/runtime/StateSerialization/MaybeMethodBase.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Reflection; using System.Runtime.Serialization; @@ -17,8 +18,9 @@ internal struct MaybeMethodBase : ISerializable where T: MethodBase const string SerializationType = "t"; // Fhe parameters of the MethodBase const string SerializationParameters = "p"; - const string SerializationIsCtor = "c"; const string SerializationMethodName = "n"; + const string SerializationGenericParamCount = "G"; + const string SerializationFlags = "V"; public static implicit operator MaybeMethodBase (T? ob) => new (ob); @@ -62,6 +64,7 @@ public MaybeMethodBase(T? mi) { info = mi; name = mi?.ToString(); + Debug.Assert(name != null || info == null); deserializationException = null; } @@ -82,46 +85,15 @@ internal MaybeMethodBase(SerializationInfo serializationInfo, StreamingContext c { throw new SerializationException($"The underlying type {typeName} can't be found"); } + + var flags = (MaybeMethodFlags)serializationInfo.GetInt32(SerializationFlags); + int genericCount = serializationInfo.GetInt32(SerializationGenericParamCount); + // Get the method's parameters types var field_name = serializationInfo.GetString(SerializationMethodName); var param = (ParameterHelper[])serializationInfo.GetValue(SerializationParameters, typeof(ParameterHelper[])); - Type[] types = new Type[param.Length]; - bool hasRefType = false; - for (int i = 0; i < param.Length; i++) - { - var paramTypeName = param[i].TypeName; - types[i] = Type.GetType(paramTypeName); - if (types[i] == null) - { - throw new SerializationException($"The parameter of type {paramTypeName} can't be found"); - } - else if (types[i].IsByRef) - { - hasRefType = true; - } - } - MethodBase? mb = null; - if (serializationInfo.GetBoolean(SerializationIsCtor)) - { - // We never want the static constructor. - mb = tp.GetConstructor(ClassManager.BindingFlags&(~BindingFlags.Static), binder:null, types:types, modifiers:null); - } - else - { - mb = tp.GetMethod(field_name, ClassManager.BindingFlags, binder:null, types:types, modifiers:null); - } - - if (mb != null && hasRefType) - { - mb = CheckRefTypes(mb, param); - } - - // Do like in ClassManager.GetClassInfo - if(mb != null && ClassManager.ShouldBindMethod(mb)) - { - info = mb; - } + info = ScanForMethod(tp, field_name, genericCount, flags, param); } catch (Exception e) { @@ -129,28 +101,44 @@ internal MaybeMethodBase(SerializationInfo serializationInfo, StreamingContext c } } - MethodBase? CheckRefTypes(MethodBase mb, ParameterHelper[] ph) + static MethodBase ScanForMethod(Type declaringType, string name, int genericCount, MaybeMethodFlags flags, ParameterHelper[] parameters) { - // One more step: Changing: - // void MyFn (ref int a) - // to: - // void MyFn (out int a) - // will still find the function correctly as, `in`, `out` and `ref` - // are all represented as a reference type. Query the method we got - // and validate the parameters - if (ph.Length != 0) - { - foreach (var item in Enumerable.Zip(ph, mb.GetParameters(), (orig, current) => new {orig, current})) - { - if (!item.current.Equals(item.orig)) - { - // False positive - return null; - } - } - } + var bindingFlags = ClassManager.BindingFlags; + if (flags.HasFlag(MaybeMethodFlags.Constructor)) bindingFlags &= ~BindingFlags.Static; - return mb; + var alternatives = declaringType.GetMember(name, + flags.HasFlag(MaybeMethodFlags.Constructor) + ? MemberTypes.Constructor + : MemberTypes.Method, + bindingFlags); + + if (alternatives.Length == 0) + throw new MissingMethodException($"{declaringType}.{name}"); + + var visibility = flags & MaybeMethodFlags.Visibility; + + var result = alternatives.Cast().FirstOrDefault(m + => MatchesGenericCount(m, genericCount) && MatchesSignature(m, parameters) + && (Visibility(m) == visibility || ClassManager.ShouldBindMethod(m))); + + if (result is null) + throw new MissingMethodException($"Matching overload not found for {declaringType}.{name}"); + + return result; + } + + static bool MatchesGenericCount(MethodBase method, int genericCount) + => method.ContainsGenericParameters + ? method.GetGenericArguments().Length == genericCount + : genericCount == 0; + + static bool MatchesSignature(MethodBase method, ParameterHelper[] parameters) + { + var curr = method.GetParameters(); + if (curr.Length != parameters.Length) return false; + for (int i = 0; i < curr.Length; i++) + if (!parameters[i].Matches(curr[i])) return false; + return true; } public void GetObjectData(SerializationInfo serializationInfo, StreamingContext context) @@ -159,11 +147,39 @@ public void GetObjectData(SerializationInfo serializationInfo, StreamingContext if (Valid) { serializationInfo.AddValue(SerializationMethodName, info.Name); - serializationInfo.AddValue(SerializationType, info.ReflectedType.AssemblyQualifiedName); + serializationInfo.AddValue(SerializationGenericParamCount, + info.ContainsGenericParameters ? info.GetGenericArguments().Length : 0); + serializationInfo.AddValue(SerializationFlags, (int)Flags(info)); + string? typeName = info.ReflectedType.AssemblyQualifiedName; + Debug.Assert(typeName != null); + serializationInfo.AddValue(SerializationType, typeName); ParameterHelper[] parameters = (from p in info.GetParameters() select new ParameterHelper(p)).ToArray(); serializationInfo.AddValue(SerializationParameters, parameters, typeof(ParameterHelper[])); - serializationInfo.AddValue(SerializationIsCtor, info.IsConstructor); } } + + static MaybeMethodFlags Flags(MethodBase method) + { + var flags = MaybeMethodFlags.Default; + if (method.IsConstructor) flags |= MaybeMethodFlags.Constructor; + if (method.IsStatic) flags |= MaybeMethodFlags.Static; + if (method.IsPublic) flags |= MaybeMethodFlags.Public; + return flags; + } + + static MaybeMethodFlags Visibility(MethodBase method) + => Flags(method) & MaybeMethodFlags.Visibility; + } + + [Flags] + internal enum MaybeMethodFlags + { + Default = 0, + Constructor = 1, + Static = 2, + + // TODO: other kinds of visibility + Public = 32, + Visibility = Public, } } diff --git a/src/runtime/runtime_data.cs b/src/runtime/runtime_data.cs index f30a54dbe..a4726b479 100644 --- a/src/runtime/runtime_data.cs +++ b/src/runtime/runtime_data.cs @@ -247,7 +247,7 @@ private static void RestoreRuntimeDataObjects(SharedObjectsState storage) } } - private static IFormatter CreateFormatter() + internal static IFormatter CreateFormatter() { return FormatterType != null ? (IFormatter)Activator.CreateInstance(FormatterType) From 48c0ca62bcd8a4168343dce12d1842a57e056745 Mon Sep 17 00:00:00 2001 From: Victor Nova Date: Tue, 21 Dec 2021 11:20:37 -0800 Subject: [PATCH 03/75] "No method matches given arguments" message now includes type name --- src/runtime/methodbinder.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/runtime/methodbinder.cs b/src/runtime/methodbinder.cs index 0569afe3e..1c5da07c5 100644 --- a/src/runtime/methodbinder.cs +++ b/src/runtime/methodbinder.cs @@ -895,11 +895,11 @@ internal virtual NewReference Invoke(BorrowedReference inst, BorrowedReference a var value = new StringBuilder("No method matches given arguments"); if (methodinfo != null && methodinfo.Length > 0) { - value.Append($" for {methodinfo[0].Name}"); + value.Append($" for {methodinfo[0].DeclaringType?.Name}.{methodinfo[0].Name}"); } else if (list.Count > 0 && list[0].Valid) { - value.Append($" for {list[0].Value.Name}"); + value.Append($" for {list[0].Value.DeclaringType?.Name}.{list[0].Value.Name}"); } value.Append(": "); From 9fb7e6584f7dc928e6d9496a1a9b2352aa330e3e Mon Sep 17 00:00:00 2001 From: Victor Nova Date: Thu, 23 Dec 2021 10:44:45 -0800 Subject: [PATCH 04/75] bring NuGet preview CI job in line with main --- .github/workflows/nuget-preview.yml | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/.github/workflows/nuget-preview.yml b/.github/workflows/nuget-preview.yml index 025210bec..1dfa17d5a 100644 --- a/.github/workflows/nuget-preview.yml +++ b/.github/workflows/nuget-preview.yml @@ -37,20 +37,22 @@ jobs: - name: Install dependencies run: | pip install --upgrade -r requirements.txt + pip install numpy # for tests - name: Build and Install run: | pip install -v . + - name: Set Python DLL path (non Windows) + if: ${{ matrix.os != 'windows' }} + run: | + python -m pythonnet.find_libpython --export >> $GITHUB_ENV + - name: Python Tests run: pytest - env: - PYTHONNET_PYDLL: libpython3.8.so - name: Embedding tests run: dotnet test --runtime any-ubuntu src/embed_tests/ - env: - PYTHONNET_PYDLL: libpython3.8.so - name: Pack run: dotnet pack --configuration Release --version-suffix preview${{env.DATE_VER}} --output "Release-Preview" @@ -59,6 +61,3 @@ jobs: run: | dotnet nuget push --source https://api.nuget.org/v3/index.json --api-key ${{ secrets.NUGET_MONTHLY }} Release-Preview/*.nupkg dotnet nuget push --skip-duplicate --source https://api.nuget.org/v3/index.json --api-key ${{ secrets.NUGET_MONTHLY }} Release-Preview/*.snupkg - - # TODO: Run perf tests - # TODO: Run mono tests on Windows? From 806f79e955cb4126306e6a38a73f1be87abaa353 Mon Sep 17 00:00:00 2001 From: Benedikt Reinartz Date: Fri, 24 Dec 2021 00:14:40 +0100 Subject: [PATCH 05/75] Require newest available clr-loader (#1643) Before `pythonnet` 3.0 is finally released, we should bump clr-loader to 1.0. --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 77211e442..58e177262 100644 --- a/setup.py +++ b/setup.py @@ -151,7 +151,7 @@ def finalize_options(self): author="The Contributors of the Python.NET Project", author_email="pythonnet@python.org", packages=["pythonnet", "pythonnet.find_libpython"], - install_requires=["clr_loader"], + install_requires=["clr_loader >= 0.1.7"], long_description=long_description, long_description_content_type="text/x-rst", py_modules=["clr"], From 216c705ecc344d9f386c811483f0da8b598ca7fe Mon Sep 17 00:00:00 2001 From: Benedikt Reinartz Date: Thu, 23 Dec 2021 23:42:04 +0100 Subject: [PATCH 06/75] Fix the PyGILState_STATE type CPython uses a bare `enum` here, both of .NET and C default to `int` enums, so this should be closer to the truth if no special compile options are used. --- src/runtime/native/PyGILState.cs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/runtime/native/PyGILState.cs b/src/runtime/native/PyGILState.cs index 35fe6c983..2e7f61029 100644 --- a/src/runtime/native/PyGILState.cs +++ b/src/runtime/native/PyGILState.cs @@ -1,11 +1,8 @@ -using System; -using System.Runtime.InteropServices; - namespace Python.Runtime.Native; /// PyGILState_STATE -[StructLayout(LayoutKind.Sequential)] -struct PyGILState +enum PyGILState { - IntPtr handle; + PyGILState_LOCKED, + PyGILState_UNLOCKED } From dfeb354de6c396afe3b44d4905c10fbc6445531e Mon Sep 17 00:00:00 2001 From: Benedikt Reinartz Date: Thu, 23 Dec 2021 23:59:12 +0100 Subject: [PATCH 07/75] Fix warning regarding undefined __module__ on GC Offset Base Also makes the type names a bit more Python-esque. The `clr._internal` module doesn't exist, but that is no difference to the previous setup. --- src/runtime/typemanager.cs | 6 +++--- tests/utils.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/runtime/typemanager.cs b/src/runtime/typemanager.cs index c0a835943..b6fea0ca1 100644 --- a/src/runtime/typemanager.cs +++ b/src/runtime/typemanager.cs @@ -456,7 +456,7 @@ internal static PyType CreateMetatypeWithGCHandleOffset() int size = Util.ReadInt32(Runtime.PyTypeType, TypeOffset.tp_basicsize) + IntPtr.Size // tp_clr_inst_offset ; - var result = new PyType(new TypeSpec("GC Offset Base", basicSize: size, + var result = new PyType(new TypeSpec("clr._internal.GCOffsetBase", basicSize: size, new TypeSpec.Slot[] { @@ -480,7 +480,7 @@ internal static PyType CreateMetaType(Type impl, out SlotsHolder slotsHolder) PyType gcOffsetBase = CreateMetatypeWithGCHandleOffset(); - PyType type = AllocateTypeObject("CLR Metatype", metatype: gcOffsetBase); + PyType type = AllocateTypeObject("CLRMetatype", metatype: gcOffsetBase); Util.WriteRef(type, TypeOffset.tp_base, new NewReference(gcOffsetBase).Steal()); @@ -509,7 +509,7 @@ internal static PyType CreateMetaType(Type impl, out SlotsHolder slotsHolder) } BorrowedReference dict = Util.ReadRef(type, TypeOffset.tp_dict); - using (var mod = Runtime.PyString_FromString("CLR")) + using (var mod = Runtime.PyString_FromString("clr._internal")) Runtime.PyDict_SetItemString(dict, "__module__", mod.Borrow()); // The type has been modified after PyType_Ready has been called diff --git a/tests/utils.py b/tests/utils.py index b467cae97..6246d12ac 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -24,7 +24,7 @@ def is_clr_root_module(ob): def is_clr_class(ob): - return type(ob).__name__ == 'CLR Metatype' # for now + return type(ob).__name__ == 'CLRMetatype' and type(ob).__module__ == 'clr._internal' # for now class ClassicClass: From 53836defd715d30e7aec95a4c1d724e843988c17 Mon Sep 17 00:00:00 2001 From: Victor Nova Date: Sun, 19 Dec 2021 20:26:13 -0800 Subject: [PATCH 08/75] `ShutdownMode` has been removed. The only shutdown mode supported now is an equivalent of `ShutdownMode.Reload` also in this change: - fixed Python derived types not being decrefed when an instance is deallocated - reduced time and amount of storage needed for runtime reload - removed circular reference loop between Type <-> ConstructorBinding(s) + exposed Runtime.TryCollectingGarbage --- .github/workflows/main.yml | 4 - CHANGELOG.md | 2 + src/embed_tests/Codecs.cs | 20 +-- src/embed_tests/TestCallbacks.cs | 8 +- src/embed_tests/TestDomainReload.cs | 34 +---- src/embed_tests/pyinitialize.cs | 45 ------ src/runtime/ReflectedClrType.cs | 5 +- .../StateSerialization/ClassManagerState.cs | 2 +- .../StateSerialization/ICLRObjectStorer.cs | 4 +- .../StateSerialization/SharedObjectsState.cs | 4 +- src/runtime/Util.cs | 2 +- src/runtime/classbase.cs | 20 +-- src/runtime/classderived.cs | 8 +- src/runtime/classmanager.cs | 11 +- src/runtime/clrobject.cs | 4 +- src/runtime/constructorbinding.cs | 32 +--- src/runtime/extensiontype.cs | 2 +- src/runtime/finalizer.cs | 2 +- src/runtime/importhook.cs | 2 +- src/runtime/interop.cs | 2 +- src/runtime/managedtype.cs | 11 +- src/runtime/methodobject.cs | 8 +- src/runtime/moduleobject.cs | 34 ++++- src/runtime/pythonengine.cs | 50 +++---- src/runtime/runtime.cs | 139 +++++------------- src/runtime/runtime_data.cs | 52 ++----- src/runtime/runtime_state.cs | 2 +- src/runtime/typemanager.cs | 8 +- tests/domain_tests/TestRunner.cs | 13 +- tests/domain_tests/test_domain_reload.py | 1 - 30 files changed, 174 insertions(+), 357 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 53a0f3701..8276be16b 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -14,10 +14,6 @@ jobs: os: [windows, ubuntu, macos] python: ["3.6", "3.7", "3.8", "3.9", "3.10"] platform: [x64] - shutdown_mode: [Normal, Soft] - - env: - PYTHONNET_SHUTDOWN_MODE: ${{ matrix.SHUTDOWN_MODE }} steps: - name: Set Environment on macOS diff --git a/CHANGELOG.md b/CHANGELOG.md index 0a01e69fc..86c2a808a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -107,6 +107,8 @@ Instead, `PyIterable` does that. ### Removed +- `ShutdownMode` has been removed. The only shutdown mode supported now is an equivalent of `ShutdownMode.Reload`. +There is no need to specify it. - implicit assembly loading (you have to explicitly `clr.AddReference` before doing import) - messages in `PythonException` no longer start with exception type - `PyScopeManager`, `PyScopeException`, `PyScope` (use `PyModule` instead) diff --git a/src/embed_tests/Codecs.cs b/src/embed_tests/Codecs.cs index 157e60803..a87b287bc 100644 --- a/src/embed_tests/Codecs.cs +++ b/src/embed_tests/Codecs.cs @@ -34,7 +34,7 @@ static void TupleConversionsGeneric() using (var scope = Py.CreateScope()) { void Accept(T value) => restored = value; - var accept = new Action(Accept).ToPython(); + using var accept = new Action(Accept).ToPython(); scope.Set(nameof(tuple), tuple); scope.Set(nameof(accept), accept); scope.Exec($"{nameof(accept)}({nameof(tuple)})"); @@ -55,7 +55,7 @@ static void TupleConversionsObject() using (var scope = Py.CreateScope()) { void Accept(object value) => restored = (T)value; - var accept = new Action(Accept).ToPython(); + using var accept = new Action(Accept).ToPython(); scope.Set(nameof(tuple), tuple); scope.Set(nameof(accept), accept); scope.Exec($"{nameof(accept)}({nameof(tuple)})"); @@ -71,7 +71,7 @@ public void TupleRoundtripObject() static void TupleRoundtripObject() { var tuple = Activator.CreateInstance(typeof(T), 42.0, "42", new object()); - var pyTuple = TupleCodec.Instance.TryEncode(tuple); + using var pyTuple = TupleCodec.Instance.TryEncode(tuple); Assert.IsTrue(TupleCodec.Instance.TryDecode(pyTuple, out object restored)); Assert.AreEqual(expected: tuple, actual: restored); } @@ -85,7 +85,7 @@ public void TupleRoundtripGeneric() static void TupleRoundtripGeneric() { var tuple = Activator.CreateInstance(typeof(T), 42, "42", new object()); - var pyTuple = TupleCodec.Instance.TryEncode(tuple); + using var pyTuple = TupleCodec.Instance.TryEncode(tuple); Assert.IsTrue(TupleCodec.Instance.TryDecode(pyTuple, out T restored)); Assert.AreEqual(expected: tuple, actual: restored); } @@ -98,9 +98,9 @@ public void ListDecoderTest() var codec = ListDecoder.Instance; var items = new List() { new PyInt(1), new PyInt(2), new PyInt(3) }; - var pyList = new PyList(items.ToArray()); + using var pyList = new PyList(items.ToArray()); - var pyListType = pyList.GetPythonType(); + using var pyListType = pyList.GetPythonType(); Assert.IsTrue(codec.CanDecode(pyListType, typeof(IList))); Assert.IsTrue(codec.CanDecode(pyListType, typeof(IList))); Assert.IsFalse(codec.CanDecode(pyListType, typeof(System.Collections.IEnumerable))); @@ -128,8 +128,8 @@ public void ListDecoderTest() Assert.Throws(typeof(InvalidCastException), () => { var x = stringList[0]; }); //can't convert python iterable to list (this will require a copy which isn't lossless) - var foo = GetPythonIterable(); - var fooType = foo.GetPythonType(); + using var foo = GetPythonIterable(); + using var fooType = foo.GetPythonType(); Assert.IsFalse(codec.CanDecode(fooType, typeof(IList))); } @@ -140,8 +140,8 @@ public void SequenceDecoderTest() var items = new List() { new PyInt(1), new PyInt(2), new PyInt(3) }; //SequenceConverter can only convert to any ICollection - var pyList = new PyList(items.ToArray()); - var listType = pyList.GetPythonType(); + using var pyList = new PyList(items.ToArray()); + using var listType = pyList.GetPythonType(); //it can convert a PyList, since PyList satisfies the python sequence protocol Assert.IsFalse(codec.CanDecode(listType, typeof(bool))); diff --git a/src/embed_tests/TestCallbacks.cs b/src/embed_tests/TestCallbacks.cs index 6875fde01..88b84d0c3 100644 --- a/src/embed_tests/TestCallbacks.cs +++ b/src/embed_tests/TestCallbacks.cs @@ -4,8 +4,6 @@ using Python.Runtime; namespace Python.EmbeddingTest { - using Runtime = Python.Runtime.Runtime; - public class TestCallbacks { [OneTimeSetUp] public void SetUp() { @@ -22,11 +20,13 @@ public void TestNoOverloadException() { int passed = 0; var aFunctionThatCallsIntoPython = new Action(value => passed = value); using (Py.GIL()) { - dynamic callWith42 = PythonEngine.Eval("lambda f: f([42])"); - var error = Assert.Throws(() => callWith42(aFunctionThatCallsIntoPython.ToPython())); + using dynamic callWith42 = PythonEngine.Eval("lambda f: f([42])"); + using var pyFunc = aFunctionThatCallsIntoPython.ToPython(); + var error = Assert.Throws(() => callWith42(pyFunc)); Assert.AreEqual("TypeError", error.Type.Name); string expectedArgTypes = "()"; StringAssert.EndsWith(expectedArgTypes, error.Message); + error.Traceback.Dispose(); } } } diff --git a/src/embed_tests/TestDomainReload.cs b/src/embed_tests/TestDomainReload.cs index a0619a93f..498119d1e 100644 --- a/src/embed_tests/TestDomainReload.cs +++ b/src/embed_tests/TestDomainReload.cs @@ -67,14 +67,6 @@ public static void DomainReloadAndGC() RunAssemblyAndUnload("test2"); Assert.That(PyRuntime.Py_IsInitialized() != 0, "On soft-shutdown mode, Python runtime should still running"); - - if (PythonEngine.DefaultShutdownMode == ShutdownMode.Normal) - { - // The default mode is a normal mode, - // it should shutdown the Python VM avoiding influence other tests. - PyRuntime.PyGILState_Ensure(); - PyRuntime.Py_Finalize(); - } } #region CrossDomainObject @@ -222,7 +214,7 @@ static void RunAssemblyAndUnload(string domainName) // assembly (and Python .NET) to reside var theProxy = CreateInstanceInstanceAndUnwrap(domain); - theProxy.Call(nameof(PythonRunner.InitPython), ShutdownMode.Soft, PyRuntime.PythonDLL); + theProxy.Call(nameof(PythonRunner.InitPython), PyRuntime.PythonDLL); // From now on use the Proxy to call into the new assembly theProxy.RunPython(); @@ -290,7 +282,7 @@ static void RunDomainReloadSteps() where T1 : CrossCaller where T2 : Cro try { var theProxy = CreateInstanceInstanceAndUnwrap(domain); - theProxy.Call(nameof(PythonRunner.InitPython), ShutdownMode.Reload, PyRuntime.PythonDLL); + theProxy.Call(nameof(PythonRunner.InitPython), PyRuntime.PythonDLL); var caller = CreateInstanceInstanceAndUnwrap(domain); arg = caller.Execute(arg); @@ -308,7 +300,7 @@ static void RunDomainReloadSteps() where T1 : CrossCaller where T2 : Cro try { var theProxy = CreateInstanceInstanceAndUnwrap(domain); - theProxy.Call(nameof(PythonRunner.InitPython), ShutdownMode.Reload, PyRuntime.PythonDLL); + theProxy.Call(nameof(PythonRunner.InitPython), PyRuntime.PythonDLL); var caller = CreateInstanceInstanceAndUnwrap(domain); caller.Execute(arg); @@ -319,10 +311,8 @@ static void RunDomainReloadSteps() where T1 : CrossCaller where T2 : Cro AppDomain.Unload(domain); } } - if (PythonEngine.DefaultShutdownMode == ShutdownMode.Normal) - { - Assert.IsTrue(PyRuntime.Py_IsInitialized() == 0); - } + + Assert.IsTrue(PyRuntime.Py_IsInitialized() != 0); } } @@ -368,10 +358,10 @@ public static void RunPython() private static IntPtr _state; - public static void InitPython(ShutdownMode mode, string dllName) + public static void InitPython(string dllName) { PyRuntime.PythonDLL = dllName; - PythonEngine.Initialize(mode: mode); + PythonEngine.Initialize(); _state = PythonEngine.BeginAllowThreads(); } @@ -384,15 +374,7 @@ public static void ShutdownPython() public static void ShutdownPythonCompletely() { PythonEngine.EndAllowThreads(_state); - // XXX: Reload mode will reserve clr objects after `Runtime.Shutdown`, - // if it used a another mode(the default mode) in other tests, - // when other tests trying to access these reserved objects, it may cause Domain exception, - // thus it needs to reduct to Soft mode to make sure all clr objects remove from Python. - var defaultMode = PythonEngine.DefaultShutdownMode; - if (defaultMode != ShutdownMode.Reload) - { - PythonEngine.ShutdownMode = defaultMode; - } + PythonEngine.Shutdown(); } diff --git a/src/embed_tests/pyinitialize.cs b/src/embed_tests/pyinitialize.cs index 8c9d6d251..25dafb686 100644 --- a/src/embed_tests/pyinitialize.cs +++ b/src/embed_tests/pyinitialize.cs @@ -151,51 +151,6 @@ public void ShutdownHandlers() // Wrong: (4 * 2) + 1 + 1 + 1 = 11 Assert.That(shutdown_count, Is.EqualTo(12)); } - - [Test] - public static void TestRunExitFuncs() - { - if (Runtime.Runtime.GetDefaultShutdownMode() == ShutdownMode.Normal) - { - // If the runtime using the normal mode, - // callback registered by atexit will be called after we release the clr information, - // thus there's no chance we can check it here. - Assert.Ignore("Skip on normal mode"); - } - Runtime.Runtime.Initialize(); - PyObject atexit; - try - { - atexit = Py.Import("atexit"); - } - catch (PythonException e) - { - string msg = e.ToString(); - bool isImportError = e.Is(Exceptions.ImportError); - Runtime.Runtime.Shutdown(); - - if (isImportError) - { - Assert.Ignore("no atexit module"); - } - else - { - Assert.Fail(msg); - } - PythonEngine.InteropConfiguration = InteropConfiguration.MakeDefault(); - return; - } - bool called = false; - Action callback = () => - { - called = true; - }; - atexit.InvokeMethod("register", callback.ToPython()).Dispose(); - atexit.Dispose(); - Runtime.Runtime.Shutdown(); - Assert.True(called); - PythonEngine.InteropConfiguration = InteropConfiguration.MakeDefault(); - } } public class ImportClassShutdownRefcountClass { } diff --git a/src/runtime/ReflectedClrType.cs b/src/runtime/ReflectedClrType.cs index 93c28fd87..15ea5c2b2 100644 --- a/src/runtime/ReflectedClrType.cs +++ b/src/runtime/ReflectedClrType.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Diagnostics; using System.Runtime.Serialization; @@ -49,9 +50,9 @@ public static ReflectedClrType GetOrCreate(Type type) return pyType; } - internal void Restore(InterDomainContext context) + internal void Restore(Dictionary context) { - var cb = context.Storage.GetValue("impl"); + var cb = (ClassBase)context["impl"]!; Debug.Assert(cb is not null); diff --git a/src/runtime/StateSerialization/ClassManagerState.cs b/src/runtime/StateSerialization/ClassManagerState.cs index 093e5d41c..01c9c472c 100644 --- a/src/runtime/StateSerialization/ClassManagerState.cs +++ b/src/runtime/StateSerialization/ClassManagerState.cs @@ -10,6 +10,6 @@ namespace Python.Runtime.StateSerialization; [Serializable] internal class ClassManagerState { - public Dictionary Contexts { get; set; } + public Dictionary> Contexts { get; set; } public Dictionary Cache { get; set; } } diff --git a/src/runtime/StateSerialization/ICLRObjectStorer.cs b/src/runtime/StateSerialization/ICLRObjectStorer.cs index b87339cd5..547115284 100644 --- a/src/runtime/StateSerialization/ICLRObjectStorer.cs +++ b/src/runtime/StateSerialization/ICLRObjectStorer.cs @@ -4,6 +4,6 @@ namespace Python.Runtime; public interface ICLRObjectStorer { - ICollection Store(CLRWrapperCollection wrappers, RuntimeDataStorage storage); - CLRWrapperCollection Restore(RuntimeDataStorage storage); + ICollection Store(CLRWrapperCollection wrappers, Dictionary storage); + CLRWrapperCollection Restore(Dictionary storage); } diff --git a/src/runtime/StateSerialization/SharedObjectsState.cs b/src/runtime/StateSerialization/SharedObjectsState.cs index 0375007d6..6c7516623 100644 --- a/src/runtime/StateSerialization/SharedObjectsState.cs +++ b/src/runtime/StateSerialization/SharedObjectsState.cs @@ -12,6 +12,6 @@ internal class SharedObjectsState { public Dictionary InternalStores { get; init; } public Dictionary Extensions { get; init; } - public RuntimeDataStorage Wrappers { get; init; } - public Dictionary Contexts { get; init; } + public Dictionary Wrappers { get; init; } + public Dictionary> Contexts { get; init; } } diff --git a/src/runtime/Util.cs b/src/runtime/Util.cs index bbed4ad0a..f5f0d2957 100644 --- a/src/runtime/Util.cs +++ b/src/runtime/Util.cs @@ -13,7 +13,7 @@ internal static class Util internal const string UnstableApiMessage = "This API is unstable, and might be changed or removed in the next minor release"; internal const string MinimalPythonVersionRequired = - "Only Python 3.5 or newer is supported"; + "Only Python 3.6 or newer is supported"; internal const string InternalUseOnly = "This API is for internal use only"; diff --git a/src/runtime/classbase.cs b/src/runtime/classbase.cs index 9ef7c626c..349d20b6b 100644 --- a/src/runtime/classbase.cs +++ b/src/runtime/classbase.cs @@ -341,16 +341,17 @@ public static void tp_dealloc(NewReference lastRef) CallClear(lastRef.Borrow()); - IntPtr addr = lastRef.DangerousGetAddress(); - bool deleted = CLRObject.reflectedObjects.Remove(addr); - Debug.Assert(deleted); - DecrefTypeAndFree(lastRef.Steal()); } public static int tp_clear(BorrowedReference ob) { - TryFreeGCHandle(ob); + if (TryFreeGCHandle(ob)) + { + IntPtr addr = ob.DangerousGetAddress(); + bool deleted = CLRObject.reflectedObjects.Remove(addr); + Debug.Assert(deleted); + } int baseClearResult = BaseUnmanagedClear(ob); if (baseClearResult != 0) @@ -390,13 +391,14 @@ internal static unsafe int BaseUnmanagedClear(BorrowedReference ob) return clear(ob); } - protected override void OnSave(BorrowedReference ob, InterDomainContext context) + protected override Dictionary OnSave(BorrowedReference ob) { - base.OnSave(ob, context); - context.Storage.AddValue("impl", this); + var context = base.OnSave(ob) ?? new(); + context["impl"] = this; + return context; } - protected override void OnLoad(BorrowedReference ob, InterDomainContext? context) + protected override void OnLoad(BorrowedReference ob, Dictionary? context) { base.OnLoad(ob, context); var gcHandle = GCHandle.Alloc(this); diff --git a/src/runtime/classderived.cs b/src/runtime/classderived.cs index a39f23bef..5b9e630ca 100644 --- a/src/runtime/classderived.cs +++ b/src/runtime/classderived.cs @@ -867,17 +867,19 @@ public static void PyFinalize(IPythonDerivedType obj) internal static void Finalize(IntPtr derived) { - bool deleted = CLRObject.reflectedObjects.Remove(derived); - Debug.Assert(deleted); - var @ref = NewReference.DangerousFromPointer(derived); ClassBase.tp_clear(@ref.Borrow()); + var type = Runtime.PyObject_TYPE(@ref.Borrow()); + // rare case when it's needed // matches correspdonging PyObject_GC_UnTrack // in ClassDerivedObject.tp_dealloc Runtime.PyObject_GC_Del(@ref.Steal()); + + // must decref our type + Runtime.XDecref(StolenReference.DangerousFromPointer(type.DangerousGetAddress())); } internal static FieldInfo? GetPyObjField(Type type) => type.GetField(PyObjName, PyObjFlags); diff --git a/src/runtime/classmanager.cs b/src/runtime/classmanager.cs index 9e15b2bd1..bfc07874f 100644 --- a/src/runtime/classmanager.cs +++ b/src/runtime/classmanager.cs @@ -65,12 +65,15 @@ internal static void RemoveClasses() internal static ClassManagerState SaveRuntimeData() { - var contexts = new Dictionary(); + var contexts = new Dictionary>(); foreach (var cls in cache) { - var context = contexts[cls.Value] = new InterDomainContext(); var cb = (ClassBase)ManagedType.GetManagedObject(cls.Value)!; - cb.Save(cls.Value, context); + var context = cb.Save(cls.Value); + if (context is not null) + { + contexts[cls.Value] = context; + } // Remove all members added in InitBaseClass. // this is done so that if domain reloads and a member of a @@ -201,7 +204,7 @@ internal static ClassBase CreateClass(Type type) return impl; } - internal static void InitClassBase(Type type, ClassBase impl, PyType pyType) + internal static void InitClassBase(Type type, ClassBase impl, ReflectedClrType pyType) { // First, we introspect the managed type and build some class // information, including generating the member descriptors diff --git a/src/runtime/clrobject.cs b/src/runtime/clrobject.cs index c178ca459..db6e99121 100644 --- a/src/runtime/clrobject.cs +++ b/src/runtime/clrobject.cs @@ -53,13 +53,13 @@ internal static NewReference GetReference(object ob) return Create(ob, cc); } - internal static void Restore(object ob, BorrowedReference pyHandle, InterDomainContext context) + internal static void Restore(object ob, BorrowedReference pyHandle, Dictionary context) { var co = new CLRObject(ob); co.OnLoad(pyHandle, context); } - protected override void OnLoad(BorrowedReference ob, InterDomainContext? context) + protected override void OnLoad(BorrowedReference ob, Dictionary? context) { base.OnLoad(ob, context); GCHandle gc = GCHandle.Alloc(this); diff --git a/src/runtime/constructorbinding.cs b/src/runtime/constructorbinding.cs index 780db6424..4f82c7728 100644 --- a/src/runtime/constructorbinding.cs +++ b/src/runtime/constructorbinding.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics; using System.Reflection; namespace Python.Runtime @@ -23,16 +24,15 @@ namespace Python.Runtime internal class ConstructorBinding : ExtensionType { private MaybeType type; // The managed Type being wrapped in a ClassObject - private PyType typeToCreate; // The python type tells GetInstHandle which Type to create. private ConstructorBinder ctorBinder; [NonSerialized] private PyObject? repr; - public ConstructorBinding(Type type, PyType typeToCreate, ConstructorBinder ctorBinder) + public ConstructorBinding(Type type, ReflectedClrType typeToCreate, ConstructorBinder ctorBinder) { this.type = type; - this.typeToCreate = typeToCreate; + Debug.Assert(typeToCreate == ReflectedClrType.GetOrCreate(type)); this.ctorBinder = ctorBinder; } @@ -109,7 +109,7 @@ public static NewReference mp_subscript(BorrowedReference op, BorrowedReference { return Exceptions.RaiseTypeError("No match found for constructor signature"); } - var boundCtor = new BoundContructor(tp, self.typeToCreate, self.ctorBinder, ci); + var boundCtor = new BoundContructor(tp, self.ctorBinder, ci); return boundCtor.Alloc(); } @@ -146,15 +146,6 @@ public static NewReference tp_repr(BorrowedReference ob) self.repr = docStr.MoveToPyObject(); return new NewReference(self.repr); } - - public static int tp_traverse(BorrowedReference ob, IntPtr visit, IntPtr arg) - { - var self = (ConstructorBinding?)GetManagedObject(ob); - if (self is null) return 0; - - int res = PyVisit(self.typeToCreate, visit, arg); - return res; - } } /// @@ -169,15 +160,13 @@ public static int tp_traverse(BorrowedReference ob, IntPtr visit, IntPtr arg) internal class BoundContructor : ExtensionType { private Type type; // The managed Type being wrapped in a ClassObject - private PyType typeToCreate; // The python type tells GetInstHandle which Type to create. private ConstructorBinder ctorBinder; private ConstructorInfo ctorInfo; private PyObject? repr; - public BoundContructor(Type type, PyType typeToCreate, ConstructorBinder ctorBinder, ConstructorInfo ci) + public BoundContructor(Type type, ConstructorBinder ctorBinder, ConstructorInfo ci) { this.type = type; - this.typeToCreate = typeToCreate; this.ctorBinder = ctorBinder; ctorInfo = ci; } @@ -207,7 +196,7 @@ public static NewReference tp_call(BorrowedReference op, BorrowedReference args, } // Instantiate the python object that wraps the result of the method call // and return the PyObject* to it. - return CLRObject.GetReference(obj, self.typeToCreate); + return CLRObject.GetReference(obj, ReflectedClrType.GetOrCreate(self.type)); } /// @@ -229,14 +218,5 @@ public static NewReference tp_repr(BorrowedReference ob) self.repr = docStr.MoveToPyObject(); return new NewReference(self.repr); } - - public static int tp_traverse(BorrowedReference ob, IntPtr visit, IntPtr arg) - { - var self = (BoundContructor?)GetManagedObject(ob); - if (self is null) return 0; - - int res = PyVisit(self.typeToCreate, visit, arg); - return res; - } } } diff --git a/src/runtime/extensiontype.cs b/src/runtime/extensiontype.cs index e3f049e1a..4ed5d8417 100644 --- a/src/runtime/extensiontype.cs +++ b/src/runtime/extensiontype.cs @@ -95,7 +95,7 @@ public static int tp_clear(BorrowedReference ob) return res; } - protected override void OnLoad(BorrowedReference ob, InterDomainContext? context) + protected override void OnLoad(BorrowedReference ob, Dictionary? context) { base.OnLoad(ob, context); SetupGc(ob, Runtime.PyObject_TYPE(ob)); diff --git a/src/runtime/finalizer.cs b/src/runtime/finalizer.cs index 09ffe5c06..bfb1e228d 100644 --- a/src/runtime/finalizer.cs +++ b/src/runtime/finalizer.cs @@ -27,7 +27,7 @@ public ErrorArgs(Exception error) public Exception Error { get; } } - public static readonly Finalizer Instance = new Finalizer(); + public static Finalizer Instance { get; } = new (); public event EventHandler? BeforeCollect; public event EventHandler? ErrorHandler; diff --git a/src/runtime/importhook.cs b/src/runtime/importhook.cs index b40fa2cd6..b82c503b5 100644 --- a/src/runtime/importhook.cs +++ b/src/runtime/importhook.cs @@ -87,11 +87,11 @@ internal static void Shutdown() } TeardownNameSpaceTracking(); + clrModule.ResetModuleMembers(); Runtime.Py_CLEAR(ref py_clr_module!); root.Dispose(); root = null!; - CLRModule.Reset(); } private static Dictionary GetDotNetModules() diff --git a/src/runtime/interop.cs b/src/runtime/interop.cs index adaac1b6e..bcf99bede 100644 --- a/src/runtime/interop.cs +++ b/src/runtime/interop.cs @@ -61,7 +61,7 @@ public ModulePropertyAttribute() /// // Py_TPFLAGS_* [Flags] - public enum TypeFlags: int + public enum TypeFlags: long { HeapType = (1 << 9), BaseType = (1 << 10), diff --git a/src/runtime/managedtype.cs b/src/runtime/managedtype.cs index 91ed43473..c71529628 100644 --- a/src/runtime/managedtype.cs +++ b/src/runtime/managedtype.cs @@ -109,19 +109,18 @@ internal static unsafe int CallTypeClear(BorrowedReference ob, BorrowedReference return clearFunc(ob); } - internal void Save(BorrowedReference ob, InterDomainContext context) + internal Dictionary? Save(BorrowedReference ob) { - OnSave(ob, context); + return OnSave(ob); } -#warning context appears to be unused - internal void Load(BorrowedReference ob, InterDomainContext? context) + internal void Load(BorrowedReference ob, Dictionary? context) { OnLoad(ob, context); } - protected virtual void OnSave(BorrowedReference ob, InterDomainContext context) { } - protected virtual void OnLoad(BorrowedReference ob, InterDomainContext? context) { } + protected virtual Dictionary? OnSave(BorrowedReference ob) => null; + protected virtual void OnLoad(BorrowedReference ob, Dictionary? context) { } protected static void ClearObjectDict(BorrowedReference ob) { diff --git a/src/runtime/methodobject.cs b/src/runtime/methodobject.cs index 14e26c86d..ce64a3f7c 100644 --- a/src/runtime/methodobject.cs +++ b/src/runtime/methodobject.cs @@ -21,7 +21,6 @@ internal class MethodObject : ExtensionType private MethodInfo[]? _info = null; private readonly List infoList; internal string name; - internal PyObject? unbound; internal readonly MethodBinder binder; internal bool is_static = false; @@ -164,11 +163,8 @@ public static NewReference tp_descr_get(BorrowedReference ds, BorrowedReference if (ob == null) { - if (self.unbound is null) - { - self.unbound = new PyObject(new MethodBinding(self, target: null, targetType: new PyType(tp)).Alloc().Steal()); - } - return new NewReference(self.unbound); + var binding = new MethodBinding(self, target: null, targetType: new PyType(tp)); + return binding.Alloc(); } if (Runtime.PyObject_IsInstance(ob, tp) < 1) diff --git a/src/runtime/moduleobject.cs b/src/runtime/moduleobject.cs index 68acaf022..1e86d4472 100644 --- a/src/runtime/moduleobject.cs +++ b/src/runtime/moduleobject.cs @@ -204,6 +204,7 @@ public void LoadNames() } } + const BindingFlags ModuleMethodFlags = BindingFlags.Public | BindingFlags.Static; /// /// Initialize module level functions and attributes /// @@ -214,11 +215,9 @@ internal void InitializeModuleMembers() Type ftmarker = typeof(ForbidPythonThreadsAttribute); Type type = GetType(); - BindingFlags flags = BindingFlags.Public | BindingFlags.Static; - while (type != null) { - MethodInfo[] methods = type.GetMethods(flags); + MethodInfo[] methods = type.GetMethods(ModuleMethodFlags); foreach (MethodInfo method in methods) { object[] attrs = method.GetCustomAttributes(funcmarker, false); @@ -249,6 +248,28 @@ internal void InitializeModuleMembers() } } + internal void ResetModuleMembers() + { + Type type = GetType(); + var methods = type.GetMethods(ModuleMethodFlags) + .Where(m => m.GetCustomAttribute() is not null) + .OfType(); + var properties = type.GetProperties().Where(p => p.GetCustomAttribute() is not null); + + foreach (string memberName in methods.Concat(properties).Select(m => m.Name)) + { + if (Runtime.PyDict_DelItemString(dict, memberName) != 0) + { + if (!PythonException.CurrentMatches(Exceptions.KeyError)) + { + throw PythonException.ThrowLastAsClrException(); + } + Runtime.PyErr_Clear(); + } + cache.Remove(memberName); + } + } + /// /// ModuleObject __getattribute__ implementation. Module attributes @@ -353,9 +374,9 @@ public static int tp_traverse(BorrowedReference ob, IntPtr visit, IntPtr arg) return ExtensionType.tp_setattro(ob, key, val); } - protected override void OnSave(BorrowedReference ob, InterDomainContext context) + protected override Dictionary? OnSave(BorrowedReference ob) { - base.OnSave(ob, context); + var context = base.OnSave(ob); System.Diagnostics.Debug.Assert(dict == GetObjectDict(ob)); // destroy the cache(s) foreach (var pair in cache) @@ -374,9 +395,10 @@ protected override void OnSave(BorrowedReference ob, InterDomainContext context) } cache.Clear(); + return context; } - protected override void OnLoad(BorrowedReference ob, InterDomainContext? context) + protected override void OnLoad(BorrowedReference ob, Dictionary? context) { base.OnLoad(ob, context); SetObjectDict(ob, new NewReference(dict).Steal()); diff --git a/src/runtime/pythonengine.cs b/src/runtime/pythonengine.cs index f3b7fa770..c93443025 100644 --- a/src/runtime/pythonengine.cs +++ b/src/runtime/pythonengine.cs @@ -17,14 +17,6 @@ namespace Python.Runtime /// public class PythonEngine : IDisposable { - public static ShutdownMode ShutdownMode - { - get => Runtime.ShutdownMode; - set => Runtime.ShutdownMode = value; - } - - public static ShutdownMode DefaultShutdownMode => Runtime.GetDefaultShutdownMode(); - private static DelegateManager? delegateManager; private static bool initialized; private static IntPtr _pythonHome = IntPtr.Zero; @@ -182,9 +174,9 @@ public static void Initialize() Initialize(setSysArgv: true); } - public static void Initialize(bool setSysArgv = true, bool initSigs = false, ShutdownMode mode = ShutdownMode.Default) + public static void Initialize(bool setSysArgv = true, bool initSigs = false) { - Initialize(Enumerable.Empty(), setSysArgv: setSysArgv, initSigs: initSigs, mode); + Initialize(Enumerable.Empty(), setSysArgv: setSysArgv, initSigs: initSigs); } /// @@ -197,7 +189,7 @@ public static void Initialize(bool setSysArgv = true, bool initSigs = false, Shu /// interpreter lock (GIL) to call this method. /// initSigs can be set to 1 to do default python signal configuration. This will override the way signals are handled by the application. /// - public static void Initialize(IEnumerable args, bool setSysArgv = true, bool initSigs = false, ShutdownMode mode = ShutdownMode.Default) + public static void Initialize(IEnumerable args, bool setSysArgv = true, bool initSigs = false) { if (initialized) { @@ -209,7 +201,7 @@ public static void Initialize(IEnumerable args, bool setSysArgv = true, // during an initial "import clr", and the world ends shortly thereafter. // This is probably masking some bad mojo happening somewhere in Runtime.Initialize(). delegateManager = new DelegateManager(); - Runtime.Initialize(initSigs, mode); + Runtime.Initialize(initSigs); initialized = true; Exceptions.Clear(); @@ -318,7 +310,16 @@ public static IntPtr InitExt() { try { - Initialize(setSysArgv: false, mode: ShutdownMode.Extension); + if (Runtime.IsInitialized) + { + var builtins = Runtime.PyEval_GetBuiltins(); + var runtimeError = Runtime.PyDict_GetItemString(builtins, "RuntimeError"); + Exceptions.SetError(runtimeError, "Python.NET runtime is already initialized"); + return IntPtr.Zero; + } + Runtime.HostedInPython = true; + + Initialize(setSysArgv: false); Finalizer.Instance.ErrorHandler += AllowLeaksDuringShutdown; @@ -372,15 +373,11 @@ private static void AllowLeaksDuringShutdown(object sender, Finalizer.ErrorArgs } /// - /// Shutdown Method - /// - /// /// Shutdown and release resources held by the Python runtime. The /// Python runtime can no longer be used in the current process /// after calling the Shutdown method. - /// - /// The ShutdownMode to use when shutting down the Runtime - public static void Shutdown(ShutdownMode mode) + /// + public static void Shutdown() { if (!initialized) { @@ -393,26 +390,13 @@ public static void Shutdown(ShutdownMode mode) ExecuteShutdownHandlers(); // Remember to shut down the runtime. - Runtime.Shutdown(mode); + Runtime.Shutdown(); initialized = false; InteropConfiguration = InteropConfiguration.MakeDefault(); } - /// - /// Shutdown Method - /// - /// - /// Shutdown and release resources held by the Python runtime. The - /// Python runtime can no longer be used in the current process - /// after calling the Shutdown method. - /// - public static void Shutdown() - { - Shutdown(Runtime.ShutdownMode); - } - /// /// Called when the engine is shut down. /// diff --git a/src/runtime/runtime.cs b/src/runtime/runtime.cs index 2f1d36ac6..9a99c5d80 100644 --- a/src/runtime/runtime.cs +++ b/src/runtime/runtime.cs @@ -56,6 +56,7 @@ private static string GetDefaultDllName(Version version) private static bool _isInitialized = false; + internal static bool IsInitialized => _isInitialized; internal static readonly bool Is32Bit = IntPtr.Size == 4; // .NET core: System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(OSPlatform.Windows) @@ -66,7 +67,6 @@ private static string GetDefaultDllName(Version version) public static int MainManagedThreadId { get; private set; } - public static ShutdownMode ShutdownMode { get; internal set; } private static readonly List _pyRefs = new (); internal static Version PyVersion @@ -91,11 +91,13 @@ internal static int GetRun() return runNumber; } + internal static bool HostedInPython; + /// Initialize the runtime... /// /// Always call this method from the Main thread. After the /// first call to this method, the main thread has acquired the GIL. - internal static void Initialize(bool initSigs = false, ShutdownMode mode = ShutdownMode.Default) + internal static void Initialize(bool initSigs = false) { if (_isInitialized) { @@ -103,12 +105,6 @@ internal static void Initialize(bool initSigs = false, ShutdownMode mode = Shutd } _isInitialized = true; - if (mode == ShutdownMode.Default) - { - mode = GetDefaultShutdownMode(); - } - ShutdownMode = mode; - bool interpreterAlreadyInitialized = TryUsingDll( () => Py_IsInitialized() != 0 ); @@ -122,19 +118,11 @@ internal static void Initialize(bool initSigs = false, ShutdownMode mode = Shutd { PyEval_InitThreads(); } - // XXX: Reload mode may reduct to Soft mode, - // so even on Reload mode it still needs to save the RuntimeState - if (mode == ShutdownMode.Soft || mode == ShutdownMode.Reload) - { - RuntimeState.Save(); - } + RuntimeState.Save(); } else { - // If we're coming back from a domain reload or a soft shutdown, - // we have previously released the thread state. Restore the main - // thread state here. - if (mode != ShutdownMode.Extension) + if (!HostedInPython) { PyGILState_Ensure(); } @@ -167,7 +155,7 @@ internal static void Initialize(bool initSigs = false, ShutdownMode mode = Shutd // Initialize modules that depend on the runtime class. AssemblyManager.Initialize(); OperatorMethod.Initialize(); - if (mode == ShutdownMode.Reload && RuntimeData.HasStashData()) + if (RuntimeData.HasStashData()) { RuntimeData.RestoreRuntimeData(); } @@ -256,33 +244,7 @@ private static void InitPyMembers() return Util.ReadPtr(pyType.Borrow(), TypeOffset.tp_iternext); } - /// - /// Tries to downgrade the shutdown mode, if possible. - /// The only possibles downgrades are: - /// Soft -> Normal - /// Reload -> Soft - /// Reload -> Normal - /// - /// The desired shutdown mode - /// The `mode` parameter if the downgrade is supported, the ShutdownMode - /// set at initialization otherwise. - static ShutdownMode TryDowngradeShutdown(ShutdownMode mode) - { - if ( - mode == Runtime.ShutdownMode - || mode == ShutdownMode.Normal - || (mode == ShutdownMode.Soft && Runtime.ShutdownMode == ShutdownMode.Reload) - ) - { - return mode; - } - else // we can't downgrade - { - return Runtime.ShutdownMode; - } - } - - internal static void Shutdown(ShutdownMode mode) + internal static void Shutdown() { if (Py_IsInitialized() == 0 || !_isInitialized) { @@ -290,21 +252,16 @@ internal static void Shutdown(ShutdownMode mode) } _isInitialized = false; - // If the shutdown mode specified is not the the same as the one specified - // during Initialization, we need to validate it; we can only downgrade, - // not upgrade the shutdown mode. - mode = TryDowngradeShutdown(mode); - var state = PyGILState_Ensure(); - if (mode == ShutdownMode.Soft) - { - RunExitFuncs(); - } - if (mode == ShutdownMode.Reload) + if (!HostedInPython) { + // avoid saving dead objects + TryCollectingGarbage(runs: 3); + RuntimeData.Stash(); } + AssemblyManager.Shutdown(); OperatorMethod.Shutdown(); ImportHook.Shutdown(); @@ -314,7 +271,7 @@ internal static void Shutdown(ShutdownMode mode) NullGCHandles(ExtensionType.loadedExtensions); ClassManager.RemoveClasses(); - TypeManager.RemoveTypes(mode); + TypeManager.RemoveTypes(); MetaType.Release(); PyCLRMetaType.Dispose(); @@ -327,18 +284,15 @@ internal static void Shutdown(ShutdownMode mode) PyObjectConversions.Reset(); PyGC_Collect(); - bool everythingSeemsCollected = TryCollectingGarbage(); + bool everythingSeemsCollected = TryCollectingGarbage(MaxCollectRetriesOnShutdown, + forceBreakLoops: true); Debug.Assert(everythingSeemsCollected); Finalizer.Shutdown(); InternString.Shutdown(); - if (mode != ShutdownMode.Normal && mode != ShutdownMode.Extension) + if (!HostedInPython) { - if (mode == ShutdownMode.Soft) - { - RuntimeState.Restore(); - } ResetPyMembers(); GC.Collect(); GC.WaitForPendingFinalizers(); @@ -351,26 +305,23 @@ internal static void Shutdown(ShutdownMode mode) PyEval_SaveThread(); } + ExtensionType.loadedExtensions.Clear(); + CLRObject.reflectedObjects.Clear(); } else { ResetPyMembers(); - if (mode != ShutdownMode.Extension) - { - Py_Finalize(); - } - else - { - PyGILState_Release(state); - } + PyGILState_Release(state); } } const int MaxCollectRetriesOnShutdown = 20; internal static int _collected; - static bool TryCollectingGarbage() + static bool TryCollectingGarbage(int runs, bool forceBreakLoops) { - for (int attempt = 0; attempt < MaxCollectRetriesOnShutdown; attempt++) + if (runs <= 0) throw new ArgumentOutOfRangeException(nameof(runs)); + + for (int attempt = 0; attempt < runs; attempt++) { Interlocked.Exchange(ref _collected, 0); nint pyCollected = 0; @@ -383,21 +334,22 @@ static bool TryCollectingGarbage() } if (Volatile.Read(ref _collected) == 0 && pyCollected == 0) { - return true; + if (attempt + 1 == runs) return true; } - else + else if (forceBreakLoops) { NullGCHandles(CLRObject.reflectedObjects); } } return false; } - - internal static void Shutdown() - { - var mode = ShutdownMode; - Shutdown(mode); - } + /// + /// Alternates .NET and Python GC runs in an attempt to collect all garbage + /// + /// Total number of GC loops to run + /// true if a steady state was reached upon the requested number of tries (e.g. on the last try no objects were collected). + public static bool TryCollectingGarbage(int runs) + => TryCollectingGarbage(runs, forceBreakLoops: false); static void DisposeLazyModule(Lazy module) { @@ -412,21 +364,6 @@ private static Lazy GetModuleLazy(string moduleName) ? throw new ArgumentNullException(nameof(moduleName)) : new Lazy(() => PyModule.Import(moduleName), isThreadSafe: false); - internal static ShutdownMode GetDefaultShutdownMode() - { - string modeEvn = Environment.GetEnvironmentVariable("PYTHONNET_SHUTDOWN_MODE"); - if (modeEvn == null) - { - return ShutdownMode.Normal; - } - ShutdownMode mode; - if (Enum.TryParse(modeEvn, true, out mode)) - { - return mode; - } - return ShutdownMode.Normal; - } - private static void RunExitFuncs() { PyObject atexit; @@ -2495,14 +2432,4 @@ internal class BadPythonDllException : MissingMethodException public BadPythonDllException(string message, Exception innerException) : base(message, innerException) { } } - - - public enum ShutdownMode - { - Default, - Normal, - Soft, - Reload, - Extension, - } } diff --git a/src/runtime/runtime_data.cs b/src/runtime/runtime_data.cs index a4726b479..204e15b5b 100644 --- a/src/runtime/runtime_data.cs +++ b/src/runtime/runtime_data.cs @@ -138,7 +138,7 @@ static bool CheckSerializable (object o) private static SharedObjectsState SaveRuntimeDataObjects() { - var contexts = new Dictionary(PythonReferenceComparer.Instance); + var contexts = new Dictionary>(PythonReferenceComparer.Instance); var extensionObjs = new Dictionary(PythonReferenceComparer.Instance); // make a copy with strongly typed references to avoid concurrent modification var extensions = ExtensionType.loadedExtensions @@ -151,9 +151,11 @@ private static SharedObjectsState SaveRuntimeDataObjects() { var extension = (ExtensionType)ManagedType.GetManagedObject(pyObj)!; Debug.Assert(CheckSerializable(extension)); - var context = new InterDomainContext(); - contexts[pyObj] = context; - extension.Save(pyObj, context); + var context = extension.Save(pyObj); + if (context is not null) + { + contexts[pyObj] = context; + } extensionObjs.Add(pyObj, extension); } @@ -189,7 +191,7 @@ private static SharedObjectsState SaveRuntimeDataObjects() mappedObjs.Add(clrObj); } - var wrapperStorage = new RuntimeDataStorage(); + var wrapperStorage = new Dictionary(); WrappersStorer?.Store(userObjects, wrapperStorage); var internalStores = new Dictionary(PythonReferenceComparer.Instance); @@ -225,7 +227,8 @@ private static void RestoreRuntimeDataObjects(SharedObjectsState storage) var contexts = storage.Contexts; foreach (var extension in extensions) { - extension.Value.Load(extension.Key, contexts[extension.Key]); + contexts.TryGetValue(extension.Key, out var context); + extension.Value.Load(extension.Key, context); } foreach (var clrObj in internalStores) { @@ -254,41 +257,4 @@ internal static IFormatter CreateFormatter() : new BinaryFormatter(); } } - - - [Serializable] - public class RuntimeDataStorage - { - private Dictionary? _namedValues; - - public T AddValue(string name, T value) - { - if (_namedValues == null) - { - _namedValues = new Dictionary(); - } - _namedValues.Add(name, value); - return value; - } - - public object? GetValue(string name) - { - return _namedValues is null - ? throw new KeyNotFoundException() - : _namedValues[name]; - } - - public T? GetValue(string name) - { - return (T?)GetValue(name); - } - } - - - [Serializable] - class InterDomainContext - { - private RuntimeDataStorage? _storage; - public RuntimeDataStorage Storage => _storage ?? (_storage = new RuntimeDataStorage()); - } } diff --git a/src/runtime/runtime_state.cs b/src/runtime/runtime_state.cs index 3cd842d39..2bb78094a 100644 --- a/src/runtime/runtime_state.cs +++ b/src/runtime/runtime_state.cs @@ -10,7 +10,7 @@ class RuntimeState { public static void Save() { - if (!PySys_GetObject("dummy_gc").IsNull) + if (!PySys_GetObject("initial_modules").IsNull) { throw new Exception("Runtime State set already"); } diff --git a/src/runtime/typemanager.cs b/src/runtime/typemanager.cs index b6fea0ca1..cc2874c96 100644 --- a/src/runtime/typemanager.cs +++ b/src/runtime/typemanager.cs @@ -49,11 +49,11 @@ internal static void Initialize() pythonBaseTypeProvider = PythonEngine.InteropConfiguration.pythonBaseTypeProviders; } - internal static void RemoveTypes(ShutdownMode shutdownMode) + internal static void RemoveTypes() { foreach (var type in cache.Values) { - if (shutdownMode == ShutdownMode.Extension + if (Runtime.HostedInPython && _slotsHolders.TryGetValue(type, out var holder)) { // If refcount > 1, it needs to reset the managed slot, @@ -540,7 +540,7 @@ internal static SlotsHolder SetupMetaSlots(Type impl, PyType type) Util.WriteIntPtr(type, TypeOffset.tp_methods, mdefStart); // XXX: Hard code with mode check. - if (Runtime.ShutdownMode != ShutdownMode.Reload) + if (Runtime.HostedInPython) { slotsHolder.Set(TypeOffset.tp_methods, (t, offset) => { @@ -559,7 +559,7 @@ private static IntPtr AddCustomMetaMethod(string name, PyType type, IntPtr mdef, slotsHolder.KeeapAlive(thunkInfo); // XXX: Hard code with mode check. - if (Runtime.ShutdownMode != ShutdownMode.Reload) + if (Runtime.HostedInPython) { IntPtr mdefAddr = mdef; slotsHolder.AddDealloctor(() => diff --git a/tests/domain_tests/TestRunner.cs b/tests/domain_tests/TestRunner.cs index cec380467..4f6a3ea28 100644 --- a/tests/domain_tests/TestRunner.cs +++ b/tests/domain_tests/TestRunner.cs @@ -48,6 +48,8 @@ class TestCase /// public string Name; + public override string ToString() => Name; + /// /// The C# code to run in the first domain. /// @@ -1135,8 +1137,7 @@ import System /// /// The runner's code. Runs the python code /// This is a template for string.Format - /// Arg 0 is the reload mode: ShutdownMode.Reload or other. - /// Arg 1 is the no-arg python function to run, before or after. + /// Arg 0 is the no-arg python function to run, before or after. /// const string CaseRunnerTemplate = @" using System; @@ -1150,14 +1151,14 @@ public static int Main() {{ try {{ - PythonEngine.Initialize(mode:{0}); + PythonEngine.Initialize(); using (Py.GIL()) {{ var temp = AppDomain.CurrentDomain.BaseDirectory; dynamic sys = Py.Import(""sys""); sys.path.append(new PyString(temp)); dynamic test_mod = Py.Import(""domain_test_module.mod""); - test_mod.{1}_reload(); + test_mod.{0}_reload(); }} PythonEngine.Shutdown(); }} @@ -1280,9 +1281,9 @@ static string CreateTestClassAssembly(string code) return CreateAssembly(TestAssemblyName + ".dll", code, exe: false); } - static string CreateCaseRunnerAssembly(string verb, string shutdownMode = "ShutdownMode.Reload") + static string CreateCaseRunnerAssembly(string verb) { - var code = string.Format(CaseRunnerTemplate, shutdownMode, verb); + var code = string.Format(CaseRunnerTemplate, verb); var name = "TestCaseRunner.exe"; return CreateAssembly(name, code, exe: true); diff --git a/tests/domain_tests/test_domain_reload.py b/tests/domain_tests/test_domain_reload.py index f0890c7c3..d04d5a1f6 100644 --- a/tests/domain_tests/test_domain_reload.py +++ b/tests/domain_tests/test_domain_reload.py @@ -65,7 +65,6 @@ def test_method_return_type_change(): def test_field_type_change(): _run_test("field_type_change") -@pytest.mark.xfail(reason="Events not yet serializable") def test_rename_event(): _run_test('event_rename') From e9c3a3d5230dbff4cf1eb333bbed96bef0610de9 Mon Sep 17 00:00:00 2001 From: Victor Nova Date: Mon, 20 Dec 2021 10:05:08 -0800 Subject: [PATCH 09/75] fixed InterfaceObject and MethodObject not being serializable --- src/runtime/interfaceobject.cs | 22 +++++++++++++++++----- src/runtime/methodobject.cs | 13 +++++++++---- 2 files changed, 26 insertions(+), 9 deletions(-) diff --git a/src/runtime/interfaceobject.cs b/src/runtime/interfaceobject.cs index f71f78236..b7e865b62 100644 --- a/src/runtime/interfaceobject.cs +++ b/src/runtime/interfaceobject.cs @@ -13,15 +13,18 @@ namespace Python.Runtime [Serializable] internal class InterfaceObject : ClassBase { + [NonSerialized] internal ConstructorInfo? ctor; internal InterfaceObject(Type tp) : base(tp) { - var coclass = (CoClassAttribute)Attribute.GetCustomAttribute(tp, cc_attr); - if (coclass != null) - { - ctor = coclass.CoClass.GetConstructor(Type.EmptyTypes); - } + this.ctor = TryGetCOMConstructor(tp); + } + + static ConstructorInfo? TryGetCOMConstructor(Type tp) + { + var comClass = (CoClassAttribute?)Attribute.GetCustomAttribute(tp, cc_attr); + return comClass?.CoClass.GetConstructor(Type.EmptyTypes); } private static Type cc_attr; @@ -113,5 +116,14 @@ public static NewReference tp_getattro(BorrowedReference ob, BorrowedReference k return Runtime.PyObject_GenericGetAttr(ob, key); } + + protected override void OnDeserialization(object sender) + { + base.OnDeserialization(sender); + if (this.type.Valid) + { + this.ctor = TryGetCOMConstructor(this.type.Value); + } + } } } diff --git a/src/runtime/methodobject.cs b/src/runtime/methodobject.cs index ce64a3f7c..afbcaf631 100644 --- a/src/runtime/methodobject.cs +++ b/src/runtime/methodobject.cs @@ -25,7 +25,7 @@ internal class MethodObject : ExtensionType internal bool is_static = false; internal PyString? doc; - internal Type type; + internal MaybeType type; public MethodObject(Type type, string name, MethodInfo[] info, bool allow_threads = MethodBinder.DefaultAllowThreads) { @@ -157,6 +157,11 @@ public static NewReference tp_descr_get(BorrowedReference ds, BorrowedReference { var self = (MethodObject)GetManagedObject(ds)!; + if (!self.type.Valid) + { + return Exceptions.RaiseTypeError(self.type.DeletedMessage); + } + // If the method is accessed through its type (rather than via // an instance) we return an 'unbound' MethodBinding that will // cached for future accesses through the type. @@ -178,11 +183,11 @@ public static NewReference tp_descr_get(BorrowedReference ds, BorrowedReference // In which case create a MethodBinding bound to the base class. var obj = GetManagedObject(ob) as CLRObject; if (obj != null - && obj.inst.GetType() != self.type + && obj.inst.GetType() != self.type.Value && obj.inst is IPythonDerivedType - && self.type.IsInstanceOfType(obj.inst)) + && self.type.Value.IsInstanceOfType(obj.inst)) { - var basecls = ClassManager.GetClass(self.type); + var basecls = ClassManager.GetClass(self.type.Value); return new MethodBinding(self, new PyObject(ob), basecls).Alloc(); } From 2596cdf61c32744979758817f4dce7ccfb15dfdc Mon Sep 17 00:00:00 2001 From: Victor Nova Date: Mon, 20 Dec 2021 12:27:56 -0800 Subject: [PATCH 10/75] fixed Python derived types crashing on shutdown of Python process clearing GCHandle from an instance of Python derived type would drop the last reference to it, so it was destroyed without being removed from reflectedObjects collection --- src/runtime/extensiontype.cs | 9 +++++---- src/runtime/runtime.cs | 1 + 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/runtime/extensiontype.cs b/src/runtime/extensiontype.cs index 4ed5d8417..d680067c2 100644 --- a/src/runtime/extensiontype.cs +++ b/src/runtime/extensiontype.cs @@ -80,16 +80,17 @@ public unsafe static void tp_dealloc(NewReference lastRef) tp_clear(lastRef.Borrow()); - bool deleted = loadedExtensions.Remove(lastRef.DangerousGetAddress()); - Debug.Assert(deleted); - // we must decref our type: https://docs.python.org/3/c-api/typeobj.html#c.PyTypeObject.tp_dealloc DecrefTypeAndFree(lastRef.Steal()); } public static int tp_clear(BorrowedReference ob) { - TryFreeGCHandle(ob); + if (TryFreeGCHandle(ob)) + { + bool deleted = loadedExtensions.Remove(ob.DangerousGetAddress()); + Debug.Assert(deleted); + } int res = ClassBase.BaseUnmanagedClear(ob); return res; diff --git a/src/runtime/runtime.cs b/src/runtime/runtime.cs index 9a99c5d80..1db86bc49 100644 --- a/src/runtime/runtime.cs +++ b/src/runtime/runtime.cs @@ -339,6 +339,7 @@ static bool TryCollectingGarbage(int runs, bool forceBreakLoops) else if (forceBreakLoops) { NullGCHandles(CLRObject.reflectedObjects); + CLRObject.reflectedObjects.Clear(); } } return false; From ec8b69fd35c24efffe39ea3aeb46e373efffb48b Mon Sep 17 00:00:00 2001 From: Victor Nova Date: Mon, 20 Dec 2021 15:31:56 -0800 Subject: [PATCH 11/75] attempt to fix crash on shutdown of Python executable --- src/runtime/metatype.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/runtime/metatype.cs b/src/runtime/metatype.cs index f4ad5a4b1..c51ce1a22 100644 --- a/src/runtime/metatype.cs +++ b/src/runtime/metatype.cs @@ -37,6 +37,10 @@ public static PyType Initialize() public static void Release() { + if (Runtime.HostedInPython) + { + _metaSlotsHodler.ResetSlots(); + } PyCLRMetaType.Dispose(); } From 8d61215d03c3a232efeca2a70d929a017ec49312 Mon Sep 17 00:00:00 2001 From: Victor Nova Date: Sun, 26 Dec 2021 17:04:02 -0800 Subject: [PATCH 12/75] when process is exiting, there's no need to save live .NET objects as they won't be resurrected --- src/runtime/pythonengine.cs | 1 + src/runtime/runtime.cs | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/runtime/pythonengine.cs b/src/runtime/pythonengine.cs index c93443025..1338e2631 100644 --- a/src/runtime/pythonengine.cs +++ b/src/runtime/pythonengine.cs @@ -296,6 +296,7 @@ static void OnDomainUnload(object _, EventArgs __) static void OnProcessExit(object _, EventArgs __) { + Runtime.ProcessIsTerminating = true; Shutdown(); } diff --git a/src/runtime/runtime.cs b/src/runtime/runtime.cs index 1db86bc49..a2ee45e9c 100644 --- a/src/runtime/runtime.cs +++ b/src/runtime/runtime.cs @@ -92,6 +92,7 @@ internal static int GetRun() } internal static bool HostedInPython; + internal static bool ProcessIsTerminating; /// Initialize the runtime... /// @@ -254,7 +255,7 @@ internal static void Shutdown() var state = PyGILState_Ensure(); - if (!HostedInPython) + if (!HostedInPython && !ProcessIsTerminating) { // avoid saving dead objects TryCollectingGarbage(runs: 3); From cc72be4243a993d694a5e392e99d793ceb350484 Mon Sep 17 00:00:00 2001 From: Victor Date: Wed, 29 Dec 2021 01:18:13 -0800 Subject: [PATCH 13/75] moved Py class into its own file (#1649) --- src/runtime/Py.cs | 197 ++++++++++++++++++++++++++++++++++++ src/runtime/pythonengine.cs | 187 ---------------------------------- 2 files changed, 197 insertions(+), 187 deletions(-) create mode 100644 src/runtime/Py.cs diff --git a/src/runtime/Py.cs b/src/runtime/Py.cs new file mode 100644 index 000000000..7a2369413 --- /dev/null +++ b/src/runtime/Py.cs @@ -0,0 +1,197 @@ +namespace Python.Runtime; + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.Serialization; +using System.Threading; + +using Python.Runtime.Native; + +public static class Py +{ + public static GILState GIL() + { + if (!PythonEngine.IsInitialized) + { + PythonEngine.Initialize(); + } + + return PythonEngine.DebugGIL ? new DebugGILState() : new GILState(); + } + + public static PyModule CreateScope() => new(); + public static PyModule CreateScope(string name) + => new(name ?? throw new ArgumentNullException(nameof(name))); + + + public class GILState : IDisposable + { + private readonly PyGILState state; + private bool isDisposed; + + internal GILState() + { + state = PythonEngine.AcquireLock(); + } + + public virtual void Dispose() + { + if (this.isDisposed) return; + + PythonEngine.ReleaseLock(state); + GC.SuppressFinalize(this); + this.isDisposed = true; + } + + ~GILState() + { + throw new InvalidOperationException("GIL must always be released, and it must be released from the same thread that acquired it."); + } + } + + public class DebugGILState : GILState + { + readonly Thread owner; + internal DebugGILState() : base() + { + this.owner = Thread.CurrentThread; + } + public override void Dispose() + { + if (this.owner != Thread.CurrentThread) + throw new InvalidOperationException("GIL must always be released from the same thread, that acquired it"); + + base.Dispose(); + } + } + + public class KeywordArguments : PyDict + { + public KeywordArguments() : base() + { + } + + protected KeywordArguments(SerializationInfo info, StreamingContext context) + : base(info, context) { } + } + + public static KeywordArguments kw(params object?[] kv) + { + var dict = new KeywordArguments(); + if (kv.Length % 2 != 0) + { + throw new ArgumentException("Must have an equal number of keys and values"); + } + for (var i = 0; i < kv.Length; i += 2) + { + var key = kv[i] as string; + if (key is null) + throw new ArgumentException("Keys must be non-null strings"); + + BorrowedReference value; + NewReference temp = default; + if (kv[i + 1] is PyObject pyObj) + { + value = pyObj; + } + else + { + temp = Converter.ToPythonDetectType(kv[i + 1]); + value = temp.Borrow(); + } + using (temp) + { + if (Runtime.PyDict_SetItemString(dict, key, value) != 0) + { + throw new ArgumentException( + string.Format("Cannot add key '{0}' to dictionary.", key), + innerException: PythonException.FetchCurrent()); + } + } + } + return dict; + } + + /// + /// Given a module or package name, import the module and return the resulting object. + /// + /// Fully-qualified module or package name + public static PyObject Import(string name) => PyModule.Import(name); + + public static void SetArgv() + { + IEnumerable args; + try + { + args = Environment.GetCommandLineArgs(); + } + catch (NotSupportedException) + { + args = Enumerable.Empty(); + } + + SetArgv( + new[] { "" }.Concat( + Environment.GetCommandLineArgs().Skip(1) + ) + ); + } + + public static void SetArgv(params string[] argv) + { + SetArgv(argv as IEnumerable); + } + + public static void SetArgv(IEnumerable argv) + { + if (argv is null) throw new ArgumentNullException(nameof(argv)); + + using (GIL()) + { + string[] arr = argv.ToArray(); + Runtime.PySys_SetArgvEx(arr.Length, arr, 0); + Runtime.CheckExceptionOccurred(); + } + } + + public static void With(PyObject obj, Action Body) + { + if (obj is null) throw new ArgumentNullException(nameof(obj)); + if (Body is null) throw new ArgumentNullException(nameof(Body)); + + // Behavior described here: + // https://docs.python.org/2/reference/datamodel.html#with-statement-context-managers + + Exception? ex = null; + PythonException? pyError = null; + + try + { + PyObject enterResult = obj.InvokeMethod("__enter__"); + + Body(enterResult); + } + catch (PythonException e) + { + ex = pyError = e; + } + catch (Exception e) + { + ex = e; + Exceptions.SetError(e); + pyError = PythonException.FetchCurrentRaw(); + } + + PyObject type = pyError?.Type ?? PyObject.None; + PyObject val = pyError?.Value ?? PyObject.None; + PyObject traceBack = pyError?.Traceback ?? PyObject.None; + + var exitResult = obj.InvokeMethod("__exit__", type, val, traceBack); + + if (ex != null && !exitResult.IsTrue()) throw ex; + } + + public static void With(PyObject obj, Action Body) + => With(obj, (PyObject context) => Body(context)); +} diff --git a/src/runtime/pythonengine.cs b/src/runtime/pythonengine.cs index 1338e2631..5223bb089 100644 --- a/src/runtime/pythonengine.cs +++ b/src/runtime/pythonengine.cs @@ -5,8 +5,6 @@ using System.Linq; using System.Reflection; using System.Runtime.InteropServices; -using System.Runtime.Serialization; -using System.Threading; using Python.Runtime.Native; @@ -671,189 +669,4 @@ public enum RunFlagType : int File = 257, /* Py_file_input */ Eval = 258 } - - public static class Py - { - public static GILState GIL() - { - if (!PythonEngine.IsInitialized) - { - PythonEngine.Initialize(); - } - - return PythonEngine.DebugGIL ? new DebugGILState() : new GILState(); - } - - public static PyModule CreateScope() => new(); - public static PyModule CreateScope(string name) - => new(name ?? throw new ArgumentNullException(nameof(name))); - - - public class GILState : IDisposable - { - private readonly PyGILState state; - private bool isDisposed; - - internal GILState() - { - state = PythonEngine.AcquireLock(); - } - - public virtual void Dispose() - { - if (this.isDisposed) return; - - PythonEngine.ReleaseLock(state); - GC.SuppressFinalize(this); - this.isDisposed = true; - } - - ~GILState() - { - throw new InvalidOperationException("GIL must always be released, and it must be released from the same thread that acquired it."); - } - } - - public class DebugGILState : GILState - { - readonly Thread owner; - internal DebugGILState() : base() - { - this.owner = Thread.CurrentThread; - } - public override void Dispose() - { - if (this.owner != Thread.CurrentThread) - throw new InvalidOperationException("GIL must always be released from the same thread, that acquired it"); - - base.Dispose(); - } - } - - public class KeywordArguments : PyDict - { - public KeywordArguments() : base() - { - } - - protected KeywordArguments(SerializationInfo info, StreamingContext context) - : base(info, context) { } - } - - public static KeywordArguments kw(params object?[] kv) - { - var dict = new KeywordArguments(); - if (kv.Length % 2 != 0) - { - throw new ArgumentException("Must have an equal number of keys and values"); - } - for (var i = 0; i < kv.Length; i += 2) - { - var key = kv[i] as string; - if (key is null) - throw new ArgumentException("Keys must be non-null strings"); - - BorrowedReference value; - NewReference temp = default; - if (kv[i + 1] is PyObject pyObj) - { - value = pyObj; - } - else - { - temp = Converter.ToPythonDetectType(kv[i + 1]); - value = temp.Borrow(); - } - using (temp) - { - if (Runtime.PyDict_SetItemString(dict, key, value) != 0) - { - throw new ArgumentException( - string.Format("Cannot add key '{0}' to dictionary.", key), - innerException: PythonException.FetchCurrent()); - } - } - } - return dict; - } - - /// - /// Given a module or package name, import the module and return the resulting object. - /// - /// Fully-qualified module or package name - public static PyObject Import(string name) => PyModule.Import(name); - - public static void SetArgv() - { - IEnumerable args; - try - { - args = Environment.GetCommandLineArgs(); - } - catch (NotSupportedException) - { - args = Enumerable.Empty(); - } - - SetArgv( - new[] { "" }.Concat( - Environment.GetCommandLineArgs().Skip(1) - ) - ); - } - - public static void SetArgv(params string[] argv) - { - SetArgv(argv as IEnumerable); - } - - public static void SetArgv(IEnumerable argv) - { - if (argv is null) throw new ArgumentNullException(nameof(argv)); - - using (GIL()) - { - string[] arr = argv.ToArray(); - Runtime.PySys_SetArgvEx(arr.Length, arr, 0); - Runtime.CheckExceptionOccurred(); - } - } - - public static void With(PyObject obj, Action Body) - { - if (obj is null) throw new ArgumentNullException(nameof(obj)); - if (Body is null) throw new ArgumentNullException(nameof(Body)); - - // Behavior described here: - // https://docs.python.org/2/reference/datamodel.html#with-statement-context-managers - - Exception? ex = null; - PythonException? pyError = null; - - try - { - PyObject enterResult = obj.InvokeMethod("__enter__"); - - Body(enterResult); - } - catch (PythonException e) - { - ex = pyError = e; - } - catch (Exception e) - { - ex = e; - Exceptions.SetError(e); - pyError = PythonException.FetchCurrentRaw(); - } - - PyObject type = pyError?.Type ?? PyObject.None; - PyObject val = pyError?.Value ?? PyObject.None; - PyObject traceBack = pyError?.Traceback ?? PyObject.None; - - var exitResult = obj.InvokeMethod("__exit__", type, val, traceBack); - - if (ex != null && !exitResult.IsTrue()) throw ex; - } - } } From 8d6a91891dea44c508f867a6d5e4cdde191f3e1a Mon Sep 17 00:00:00 2001 From: Victor Date: Thu, 30 Dec 2021 01:20:31 -0800 Subject: [PATCH 14/75] added a regression test for https://github.com/pythonnet/pythonnet/issues/1420 (#1652) closes https://github.com/pythonnet/pythonnet/issues/1420 --- src/embed_tests/Inheritance.cs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/embed_tests/Inheritance.cs b/src/embed_tests/Inheritance.cs index 950c08548..a992ee8e4 100644 --- a/src/embed_tests/Inheritance.cs +++ b/src/embed_tests/Inheritance.cs @@ -59,6 +59,14 @@ public void InheritedFromInheritedClassIsSelf() Assert.IsTrue(PythonReferenceComparer.Instance.Equals(b, bInstanceClass)); } + // https://github.com/pythonnet/pythonnet/issues/1420 + [Test] + public void CallBaseMethodFromContainerInNestedClass() + { + using var nested = new ContainerClass.InnerClass().ToPython(); + nested.InvokeMethod(nameof(ContainerClass.BaseMethod)); + } + [Test] public void Grandchild_PassesExtraBaseInstanceCheck() { @@ -183,4 +191,14 @@ public int XProp set => this.extras[nameof(this.XProp)] = value; } } + + public class ContainerClass + { + public void BaseMethod() { } + + public class InnerClass: ContainerClass + { + + } + } } From fc31de1731ece52e934204808106741b75340133 Mon Sep 17 00:00:00 2001 From: Victor Nova Date: Tue, 28 Dec 2021 12:50:32 -0800 Subject: [PATCH 15/75] base accessors were not exposed to Python because .NET PropertyInfo GetMethod would not return base non-overriden accessor for a partially overriden property because of that when constructing PropertyObject we scan base classes to find base accessor (if any) this might have performance implications due to replacement of PropertyInfo.GetValue with getter.Invoke (not tested) fixes https://github.com/pythonnet/pythonnet/issues/1455 --- src/embed_tests/Inheritance.cs | 21 +++++++++++++ src/runtime/ReflectionUtil.cs | 56 ++++++++++++++++++++++++++++++++++ src/runtime/propertyobject.cs | 35 +++++++++++++++------ 3 files changed, 103 insertions(+), 9 deletions(-) create mode 100644 src/runtime/ReflectionUtil.cs diff --git a/src/embed_tests/Inheritance.cs b/src/embed_tests/Inheritance.cs index a992ee8e4..ebbc24dc4 100644 --- a/src/embed_tests/Inheritance.cs +++ b/src/embed_tests/Inheritance.cs @@ -119,6 +119,15 @@ public void BaseClearIsCalled() scope.Set("exn", null); Assert.AreEqual(1, msg.Refcount); } + + // https://github.com/pythonnet/pythonnet/issues/1455 + [Test] + public void PropertyAccessorOverridden() + { + using var derived = new PropertyAccessorDerived().ToPython(); + derived.SetAttr(nameof(PropertyAccessorDerived.VirtualProp), "hi".ToPython()); + Assert.AreEqual("HI", derived.GetAttr(nameof(PropertyAccessorDerived.VirtualProp)).As()); + } } class ExtraBaseTypeProvider : IPythonBaseTypeProvider @@ -192,6 +201,18 @@ public int XProp } } + public class PropertyAccessorBase + { + public virtual string VirtualProp { get; set; } + } + + public class PropertyAccessorIntermediate: PropertyAccessorBase { } + + public class PropertyAccessorDerived: PropertyAccessorIntermediate + { + public override string VirtualProp { set => base.VirtualProp = value.ToUpperInvariant(); } + } + public class ContainerClass { public void BaseMethod() { } diff --git a/src/runtime/ReflectionUtil.cs b/src/runtime/ReflectionUtil.cs new file mode 100644 index 000000000..58d0a506e --- /dev/null +++ b/src/runtime/ReflectionUtil.cs @@ -0,0 +1,56 @@ +namespace Python.Runtime; + +using System; +using System.Reflection; + +static class ReflectionUtil +{ + public static MethodInfo? GetBaseGetMethod(this PropertyInfo property, bool nonPublic) + { + if (property is null) throw new ArgumentNullException(nameof(property)); + + Type baseType = property.DeclaringType.BaseType; + BindingFlags bindingFlags = property.GetBindingFlags(); + + while (baseType is not null) + { + var baseProperty = baseType.GetProperty(property.Name, bindingFlags | BindingFlags.DeclaredOnly); + var accessor = baseProperty?.GetGetMethod(nonPublic); + if (accessor is not null) + return accessor; + + baseType = baseType.BaseType; + } + + return null; + } + + public static MethodInfo? GetBaseSetMethod(this PropertyInfo property, bool nonPublic) + { + if (property is null) throw new ArgumentNullException(nameof(property)); + + Type baseType = property.DeclaringType.BaseType; + BindingFlags bindingFlags = property.GetBindingFlags(); + + while (baseType is not null) + { + var baseProperty = baseType.GetProperty(property.Name, bindingFlags | BindingFlags.DeclaredOnly); + var accessor = baseProperty?.GetSetMethod(nonPublic); + if (accessor is not null) + return accessor; + + baseType = baseType.BaseType; + } + + return null; + } + + public static BindingFlags GetBindingFlags(this PropertyInfo property) + { + var accessor = property.GetMethod ?? property.SetMethod; + BindingFlags flags = default; + flags |= accessor.IsStatic ? BindingFlags.Static : BindingFlags.Instance; + flags |= accessor.IsPublic ? BindingFlags.Public : BindingFlags.NonPublic; + return flags; + } +} diff --git a/src/runtime/propertyobject.cs b/src/runtime/propertyobject.cs index 140bd47b5..f09d1696a 100644 --- a/src/runtime/propertyobject.cs +++ b/src/runtime/propertyobject.cs @@ -1,5 +1,6 @@ using System; using System.Reflection; +using System.Runtime.Serialization; namespace Python.Runtime { @@ -8,17 +9,25 @@ namespace Python.Runtime /// Implements a Python descriptor type that manages CLR properties. /// [Serializable] - internal class PropertyObject : ExtensionType + internal class PropertyObject : ExtensionType, IDeserializationCallback { internal MaybeMemberInfo info; - private MaybeMethodInfo getter; - private MaybeMethodInfo setter; + [NonSerialized] + private MethodInfo? getter; + [NonSerialized] + private MethodInfo? setter; public PropertyObject(PropertyInfo md) { - getter = md.GetGetMethod(true); - setter = md.GetSetMethod(true); info = new MaybeMemberInfo(md); + CacheAccessors(); + } + + void CacheAccessors() + { + PropertyInfo md = info.Value; + getter = md.GetGetMethod(true) ?? md.GetBaseGetMethod(true); + setter = md.GetSetMethod(true) ?? md.GetBaseSetMethod(true); } @@ -35,7 +44,7 @@ public static NewReference tp_descr_get(BorrowedReference ds, BorrowedReference return Exceptions.RaiseTypeError(self.info.DeletedMessage); } var info = self.info.Value; - MethodInfo getter = self.getter.UnsafeValue; + MethodInfo? getter = self.getter; object result; @@ -70,7 +79,7 @@ public static NewReference tp_descr_get(BorrowedReference ds, BorrowedReference try { - result = info.GetValue(co.inst, null); + result = getter.Invoke(co.inst, Array.Empty()); return Converter.ToPython(result, info.PropertyType); } catch (Exception e) @@ -100,7 +109,7 @@ public static int tp_descr_set(BorrowedReference ds, BorrowedReference ob, Borro } var info = self.info.Value; - MethodInfo setter = self.setter.UnsafeValue; + MethodInfo? setter = self.setter; if (val == null) { @@ -141,7 +150,7 @@ public static int tp_descr_set(BorrowedReference ds, BorrowedReference ob, Borro Exceptions.RaiseTypeError("invalid target"); return -1; } - info.SetValue(co.inst, newval, null); + setter.Invoke(co.inst, new object?[] { newval }); } else { @@ -169,5 +178,13 @@ public static NewReference tp_repr(BorrowedReference ob) var self = (PropertyObject)GetManagedObject(ob)!; return Runtime.PyString_FromString($""); } + + void IDeserializationCallback.OnDeserialization(object sender) + { + if (info.Valid) + { + CacheAccessors(); + } + } } } From c4238d9a69fa714458c24006c3411c49697c0047 Mon Sep 17 00:00:00 2001 From: Victor Nova Date: Wed, 29 Dec 2021 15:01:23 -0800 Subject: [PATCH 16/75] reworked the way .NET objects are constructed from Python was: tp_new implementation would call .NET constructor and return a fully constructed object now: Except for some special .NET types tp_new creates uninitialized .NET object, which is later initialized by calling __init__. __init__ is set using type dictionary to a MethodObject, that contains ConstructorInfo[] instead of MethodInfo[] This allows Python to: 1) freely override __init__ 2) when deriving from .NET types call base __init__ (e.g. .NET constructor), and choose the overload as needed fixes https://github.com/pythonnet/pythonnet/issues/238 --- CHANGELOG.md | 5 + .../StateSerialization/MethodSerialization.cs | 11 + src/runtime/NewReference.cs | 11 + src/runtime/classbase.cs | 11 + src/runtime/classderived.cs | 66 +++--- src/runtime/classmanager.cs | 39 ++- src/runtime/classobject.cs | 83 ++++++- src/runtime/constructorbinder.cs | 138 ----------- src/runtime/constructorbinding.cs | 222 ------------------ src/runtime/exceptions.cs | 16 +- src/runtime/managedtype.cs | 22 ++ src/runtime/metatype.cs | 30 +-- src/runtime/methodbinder.cs | 24 +- src/runtime/methodbinding.cs | 10 +- src/runtime/methodobject.cs | 12 +- src/runtime/operatormethod.cs | 10 +- src/runtime/overload.cs | 2 +- src/testing/constructortests.cs | 17 ++ tests/test_class.py | 16 +- tests/test_constructors.py | 18 +- tests/test_generic.py | 12 + 21 files changed, 294 insertions(+), 481 deletions(-) delete mode 100644 src/runtime/constructorbinder.cs delete mode 100644 src/runtime/constructorbinding.cs diff --git a/CHANGELOG.md b/CHANGELOG.md index 86c2a808a..16489a9c9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -46,6 +46,9 @@ details about the cause of the failure - floating point values passed from Python are no longer silently truncated when .NET expects an integer [#1342][i1342] - More specific error messages for method argument mismatch +- BREAKING: when inheriting from .NET types in Python if you override `__init__` you +must explicitly call base constructor using `super().__init__(.....)`. Not doing so will lead +to undefined behavior. - BREAKING: most `PyScope` methods will never return `null`. Instead, `PyObject` `None` will be returned. - BREAKING: `PyScope` was renamed to `PyModule` - BREAKING: Methods with `ref` or `out` parameters and void return type return a tuple of only the `ref` and `out` parameters. @@ -85,6 +88,7 @@ Instead, `PyIterable` does that. ### Fixed - Fix incorrect dereference of wrapper object in `tp_repr`, which may result in a program crash +- Fixed parameterless .NET constructor being silently called when a matching constructor overload is not found ([#238][i238]) - Fix incorrect dereference in params array handling - Fixes issue with function resolution when calling overloaded function with keyword arguments from python ([#1097][i1097]) - Fix `object[]` parameters taking precedence when should not in overload resolution @@ -874,3 +878,4 @@ This version improves performance on benchmarks significantly compared to 2.3. [p534]: https://github.com/pythonnet/pythonnet/pull/534 [i449]: https://github.com/pythonnet/pythonnet/issues/449 [i1342]: https://github.com/pythonnet/pythonnet/issues/1342 +[i238]: https://github.com/pythonnet/pythonnet/issues/238 diff --git a/src/embed_tests/StateSerialization/MethodSerialization.cs b/src/embed_tests/StateSerialization/MethodSerialization.cs index 0e584fc37..80b7a08ee 100644 --- a/src/embed_tests/StateSerialization/MethodSerialization.cs +++ b/src/embed_tests/StateSerialization/MethodSerialization.cs @@ -19,6 +19,16 @@ public void GenericRoundtrip() Assert.AreEqual(method, restored.Value); } + [Test] + public void ConstrctorRoundtrip() + { + var ctor = typeof(MethodTestHost).GetConstructor(new[] { typeof(int) }); + var maybeConstructor = new MaybeMethodBase(ctor); + var restored = SerializationRoundtrip(maybeConstructor); + Assert.IsTrue(restored.Valid); + Assert.AreEqual(ctor, restored.Value); + } + static T SerializationRoundtrip(T item) { using var buf = new MemoryStream(); @@ -31,5 +41,6 @@ static T SerializationRoundtrip(T item) public class MethodTestHost { + public MethodTestHost(int _) { } public void Generic(T item, T[] array, ref T @ref) { } } diff --git a/src/runtime/NewReference.cs b/src/runtime/NewReference.cs index bbd021ad3..91ebbdb01 100644 --- a/src/runtime/NewReference.cs +++ b/src/runtime/NewReference.cs @@ -41,6 +41,17 @@ public PyObject MoveToPyObject() return new PyObject(this.StealNullable()); } + /// + /// Creates new instance of which now owns the pointer. + /// Sets the original reference to null, as it no longer owns the pointer. + /// + public NewReference Move() + { + var result = new NewReference(this); + this.pointer = default; + return result; + } + /// Moves ownership of this instance to unmanged pointer public IntPtr DangerousMoveToPointer() { diff --git a/src/runtime/classbase.cs b/src/runtime/classbase.cs index 349d20b6b..028788742 100644 --- a/src/runtime/classbase.cs +++ b/src/runtime/classbase.cs @@ -556,6 +556,17 @@ public virtual void InitializeSlots(BorrowedReference pyType, SlotsHolder slotsH } } + public virtual bool HasCustomNew() => this.GetType().GetMethod("tp_new") is not null; + + public override bool Init(BorrowedReference obj, BorrowedReference args, BorrowedReference kw) + { + if (this.HasCustomNew()) + // initialization must be done in tp_new + return true; + + return base.Init(obj, args, kw); + } + protected virtual void OnDeserialization(object sender) { this.dotNetMembers = new List(); diff --git a/src/runtime/classderived.cs b/src/runtime/classderived.cs index 5b9e630ca..47c9b4e0e 100644 --- a/src/runtime/classderived.cs +++ b/src/runtime/classderived.cs @@ -50,23 +50,26 @@ internal ClassDerivedObject(Type tp) : base(tp) { } - /// - /// Implements __new__ for derived classes of reflected classes. - /// - public new static NewReference tp_new(BorrowedReference tp, BorrowedReference args, BorrowedReference kw) + protected override NewReference NewObjectToPython(object obj, BorrowedReference tp) { - var cls = (ClassDerivedObject)GetManagedObject(tp)!; + var self = base.NewObjectToPython(obj, tp); - // call the managed constructor - object? obj = cls.binder.InvokeRaw(null, args, kw); - if (obj == null) + SetPyObj((IPythonDerivedType)obj, self.Borrow()); + + // Decrement the python object's reference count. + // This doesn't actually destroy the object, it just sets the reference to this object + // to be a weak reference and it will be destroyed when the C# object is destroyed. + if (!self.IsNull()) { - return default; + Runtime.XDecref(self.Steal()); } - // return the pointer to the python object - // (this indirectly calls ClassDerivedObject.ToPython) - return Converter.ToPython(obj, cls.GetType()); + return Converter.ToPython(obj, type.Value); + } + + protected override void SetTypeNewSlot(BorrowedReference pyType, SlotsHolder slotsHolder) + { + // Python derived types rely on base tp_new and overridden __init__ } public new static void tp_dealloc(NewReference ob) @@ -824,37 +827,24 @@ public static void InvokeSetProperty(IPythonDerivedType obj, string propertyN public static void InvokeCtor(IPythonDerivedType obj, string origCtorName, object[] args) { + var selfRef = GetPyObj(obj); + if (selfRef.Ref == null) + { + // this might happen when the object is created from .NET + using var _ = Py.GIL(); + // In the end we decrement the python object's reference count. + // This doesn't actually destroy the object, it just sets the reference to this object + // to be a weak reference and it will be destroyed when the C# object is destroyed. + using var self = CLRObject.GetReference(obj, obj.GetType()); + SetPyObj(obj, self.Borrow()); + } + // call the base constructor obj.GetType().InvokeMember(origCtorName, BindingFlags.InvokeMethod, null, obj, args); - - NewReference self = default; - PyGILState gs = Runtime.PyGILState_Ensure(); - try - { - // create the python object - var type = ClassManager.GetClass(obj.GetType()); - self = CLRObject.GetReference(obj, type); - - // set __pyobj__ to self and deref the python object which will allow this - // object to be collected. - SetPyObj(obj, self.Borrow()); - } - finally - { - // Decrement the python object's reference count. - // This doesn't actually destroy the object, it just sets the reference to this object - // to be a weak reference and it will be destroyed when the C# object is destroyed. - if (!self.IsNull()) - { - Runtime.XDecref(self.Steal()); - } - - Runtime.PyGILState_Release(gs); - } } public static void PyFinalize(IPythonDerivedType obj) @@ -890,7 +880,7 @@ internal static UnsafeReferenceWithRun GetPyObj(IPythonDerivedType obj) return (UnsafeReferenceWithRun)fi.GetValue(obj); } - static void SetPyObj(IPythonDerivedType obj, BorrowedReference pyObj) + internal static void SetPyObj(IPythonDerivedType obj, BorrowedReference pyObj) { FieldInfo fi = GetPyObjField(obj.GetType())!; fi.SetValue(obj, new UnsafeReferenceWithRun(pyObj)); diff --git a/src/runtime/classmanager.cs b/src/runtime/classmanager.cs index bfc07874f..f8e108f41 100644 --- a/src/runtime/classmanager.cs +++ b/src/runtime/classmanager.cs @@ -210,7 +210,7 @@ internal static void InitClassBase(Type type, ClassBase impl, ReflectedClrType p // information, including generating the member descriptors // that we'll be putting in the Python class __dict__. - ClassInfo info = GetClassInfo(type); + ClassInfo info = GetClassInfo(type, impl); impl.indexer = info.indexer; impl.richcompare.Clear(); @@ -252,16 +252,17 @@ internal static void InitClassBase(Type type, ClassBase impl, ReflectedClrType p // required that the ClassObject.ctors be changed to internal if (co != null) { - if (co.NumCtors > 0) + if (co.NumCtors > 0 && !co.HasCustomNew()) { // Implement Overloads on the class object if (!CLRModule._SuppressOverloads) { - using var ctors = new ConstructorBinding(type, pyType, co.binder).Alloc(); - // ExtensionType types are untracked, so don't Incref() them. + // HACK: __init__ points to instance constructors. + // When unbound they fully instantiate object, so we get overloads for free from MethodBinding. + var init = info.members["__init__"]; // TODO: deprecate __overloads__ soon... - Runtime.PyDict_SetItem(dict, PyIdentifier.__overloads__, ctors.Borrow()); - Runtime.PyDict_SetItem(dict, PyIdentifier.Overloads, ctors.Borrow()); + Runtime.PyDict_SetItem(dict, PyIdentifier.__overloads__, init); + Runtime.PyDict_SetItem(dict, PyIdentifier.Overloads, init); } // don't generate the docstring if one was already set from a DocStringAttribute. @@ -320,10 +321,10 @@ internal static bool ShouldBindEvent(EventInfo ei) return ShouldBindMethod(ei.GetAddMethod(true)); } - private static ClassInfo GetClassInfo(Type type) + private static ClassInfo GetClassInfo(Type type, ClassBase impl) { var ci = new ClassInfo(); - var methods = new Dictionary>(); + var methods = new Dictionary>(); MethodInfo meth; ExtensionType ob; string name; @@ -420,13 +421,33 @@ private static ClassInfo GetClassInfo(Type type) continue; } name = meth.Name; + + //TODO mangle? + if (name == "__init__" && !impl.HasCustomNew()) + continue; + if (!methods.TryGetValue(name, out var methodList)) { - methodList = methods[name] = new List(); + methodList = methods[name] = new List(); } methodList.Add(meth); continue; + case MemberTypes.Constructor when !impl.HasCustomNew(): + var ctor = (ConstructorInfo)mi; + if (ctor.IsStatic) + { + continue; + } + + name = "__init__"; + if (!methods.TryGetValue(name, out methodList)) + { + methodList = methods[name] = new List(); + } + methodList.Add(ctor); + continue; + case MemberTypes.Property: var pi = (PropertyInfo)mi; diff --git a/src/runtime/classobject.cs b/src/runtime/classobject.cs index 6a5c17236..5ba83c25e 100644 --- a/src/runtime/classobject.cs +++ b/src/runtime/classobject.cs @@ -1,6 +1,8 @@ using System; +using System.Diagnostics; using System.Linq; using System.Reflection; +using System.Runtime.Serialization; namespace Python.Runtime { @@ -13,18 +15,12 @@ namespace Python.Runtime [Serializable] internal class ClassObject : ClassBase { - internal ConstructorBinder binder; - internal int NumCtors = 0; + internal readonly int NumCtors = 0; internal ClassObject(Type tp) : base(tp) { var _ctors = type.Value.GetConstructors(); NumCtors = _ctors.Length; - binder = new ConstructorBinder(type.Value); - foreach (ConstructorInfo t in _ctors) - { - binder.AddMethod(t); - } } @@ -33,7 +29,12 @@ internal ClassObject(Type tp) : base(tp) /// internal NewReference GetDocString() { - MethodBase[] methods = binder.GetMethods(); + if (!type.Valid) + { + return Exceptions.RaiseTypeError(type.DeletedMessage); + } + + MethodBase[] methods = type.Value.GetConstructors(); var str = ""; foreach (MethodBase t in methods) { @@ -50,7 +51,7 @@ internal NewReference GetDocString() /// /// Implements __new__ for reflected classes and value types. /// - public static NewReference tp_new(BorrowedReference tp, BorrowedReference args, BorrowedReference kw) + static NewReference tp_new_impl(BorrowedReference tp, BorrowedReference args, BorrowedReference kw) { var self = GetManagedObject(tp) as ClassObject; @@ -100,15 +101,49 @@ public static NewReference tp_new(BorrowedReference tp, BorrowedReference args, return NewEnum(type, args, tp); } - object? obj = self.binder.InvokeRaw(null, args, kw); - if (obj == null) + if (IsGenericNullable(type)) { - return default; + // Nullable has special handling in .NET runtime. + // Invoking its constructor via reflection on an uninitialized instance + // does not actually set the object fields. + return NewNullable(type, args, kw, tp); } - return CLRObject.GetReference(obj, tp); + object obj = FormatterServices.GetUninitializedObject(type); + + return self.NewObjectToPython(obj, tp); + } + + protected virtual void SetTypeNewSlot(BorrowedReference pyType, SlotsHolder slotsHolder) + { + TypeManager.InitializeSlotIfEmpty(pyType, TypeOffset.tp_new, new Interop.BBB_N(tp_new_impl), slotsHolder); + } + + public override bool HasCustomNew() + { + if (base.HasCustomNew()) return true; + + Type clrType = type.Value; + return clrType.IsPrimitive + || clrType.IsEnum + || clrType == typeof(string) + || IsGenericNullable(clrType); } + static bool IsGenericNullable(Type type) + => type.IsValueType && type.IsGenericType + && type.GetGenericTypeDefinition() == typeof(Nullable<>); + + public override void InitializeSlots(BorrowedReference pyType, SlotsHolder slotsHolder) + { + base.InitializeSlots(pyType, slotsHolder); + + this.SetTypeNewSlot(pyType, slotsHolder); + } + + protected virtual NewReference NewObjectToPython(object obj, BorrowedReference tp) + => CLRObject.GetReference(obj, tp); + private static NewReference NewEnum(Type type, BorrowedReference args, BorrowedReference tp) { nint argCount = Runtime.PyTuple_Size(args); @@ -146,6 +181,28 @@ private static NewReference NewEnum(Type type, BorrowedReference args, BorrowedR return CLRObject.GetReference(enumValue, tp); } + private static NewReference NewNullable(Type type, BorrowedReference args, BorrowedReference kw, BorrowedReference tp) + { + Debug.Assert(IsGenericNullable(type)); + + if (kw != null) + { + return Exceptions.RaiseTypeError("System.Nullable constructor does not support keyword arguments"); + } + + nint argsCount = Runtime.PyTuple_Size(args); + if (argsCount != 1) + { + return Exceptions.RaiseTypeError("System.Nullable constructor expects 1 argument, got " + (int)argsCount); + } + + var value = Runtime.PyTuple_GetItem(args, 0); + var elementType = type.GetGenericArguments()[0]; + return Converter.ToManaged(value, elementType, out var result, setError: true) + ? CLRObject.GetReference(result!, tp) + : default; + } + /// /// Implementation of [] semantics for reflected types. This exists diff --git a/src/runtime/constructorbinder.cs b/src/runtime/constructorbinder.cs deleted file mode 100644 index 4868c5f1a..000000000 --- a/src/runtime/constructorbinder.cs +++ /dev/null @@ -1,138 +0,0 @@ -using System; -using System.Reflection; -using System.Text; - -namespace Python.Runtime -{ - /// - /// A ConstructorBinder encapsulates information about one or more managed - /// constructors, and is responsible for selecting the right constructor - /// given a set of Python arguments. This is slightly different than the - /// standard MethodBinder because of a difference in invoking constructors - /// using reflection (which is seems to be a CLR bug). - /// - [Serializable] - internal class ConstructorBinder : MethodBinder - { - private MaybeType _containingType; - - internal ConstructorBinder(Type containingType) - { - _containingType = containingType; - } - - /// - /// Constructors get invoked when an instance of a wrapped managed - /// class or a subclass of a managed class is created. This differs - /// from the MethodBinder implementation in that we return the raw - /// result of the constructor rather than wrapping it as a Python - /// object - the reason is that only the caller knows the correct - /// Python type to use when wrapping the result (may be a subclass). - /// - internal object? InvokeRaw(BorrowedReference inst, BorrowedReference args, BorrowedReference kw) - { - return InvokeRaw(inst, args, kw, null); - } - - /// - /// Allows ctor selection to be limited to a single attempt at a - /// match by providing the MethodBase to use instead of searching - /// the entire MethodBinder.list (generic ArrayList) - /// - /// (possibly null) instance - /// PyObject* to the arg tuple - /// PyObject* to the keyword args dict - /// The sole ContructorInfo to use or null - /// The result of the constructor call with converted params - /// - /// 2010-07-24 BC: I added the info parameter to the call to Bind() - /// Binding binding = this.Bind(inst, args, kw, info); - /// to take advantage of Bind()'s ability to use a single MethodBase (CI or MI). - /// - internal object? InvokeRaw(BorrowedReference inst, BorrowedReference args, BorrowedReference kw, MethodBase? info) - { - if (!_containingType.Valid) - { - Exceptions.RaiseTypeError(_containingType.DeletedMessage); - return null; - } - object result; - Type tp = _containingType.Value; - - if (tp.IsValueType && !tp.IsPrimitive && - !tp.IsEnum && tp != typeof(decimal) && - Runtime.PyTuple_Size(args) == 0) - { - // If you are trying to construct an instance of a struct by - // calling its default constructor, that ConstructorInfo - // instance will not appear in reflection and the object must - // instead be constructed via a call to - // Activator.CreateInstance(). - try - { - result = Activator.CreateInstance(tp); - } - catch (Exception e) - { - if (e.InnerException != null) - { - e = e.InnerException; - } - Exceptions.SetError(e); - return null; - } - return result; - } - - Binding? binding = Bind(inst, args, kw, info); - - if (binding == null) - { - // It is possible for __new__ to be invoked on construction - // of a Python subclass of a managed class, so args may - // reflect more args than are required to instantiate the - // class. So if we cant find a ctor that matches, we'll see - // if there is a default constructor and, if so, assume that - // any extra args are intended for the subclass' __init__. - - using var eargs = Runtime.PyTuple_New(0); - binding = Bind(inst, eargs.BorrowOrThrow(), kw: null); - - if (binding == null) - { - var errorMessage = new StringBuilder("No constructor matches given arguments"); - if (info != null && info.IsConstructor && info.DeclaringType != null) - { - errorMessage.Append(" for ").Append(info.DeclaringType.Name); - } - - errorMessage.Append(": "); - Runtime.PyErr_Fetch(out var errType, out var errVal, out var errTrace); - AppendArgumentTypes(to: errorMessage, args); - Runtime.PyErr_Restore(errType.StealNullable(), errVal.StealNullable(), errTrace.StealNullable()); - Exceptions.RaiseTypeError(errorMessage.ToString()); - return null; - } - } - - // Fire the selected ctor and catch errors... - var ci = (ConstructorInfo)binding.info; - // Object construction is presumed to be non-blocking and fast - // enough that we shouldn't really need to release the GIL. - try - { - result = ci.Invoke(binding.args); - } - catch (Exception e) - { - if (e.InnerException != null) - { - e = e.InnerException; - } - Exceptions.SetError(e); - return null; - } - return result; - } - } -} diff --git a/src/runtime/constructorbinding.cs b/src/runtime/constructorbinding.cs deleted file mode 100644 index 4f82c7728..000000000 --- a/src/runtime/constructorbinding.cs +++ /dev/null @@ -1,222 +0,0 @@ -using System; -using System.Diagnostics; -using System.Reflection; - -namespace Python.Runtime -{ - /// - /// Implements a Python type that wraps a CLR ctor call. Constructor objects - /// support a .Overloads[] syntax to allow explicit ctor overload selection. - /// - /// - /// ClassManager stores a ConstructorBinding instance in the class's __dict__['Overloads'] - /// SomeType.Overloads[Type, ...] works like this: - /// 1) Python retrieves the Overloads attribute from this ClassObject's dictionary normally - /// and finds a non-null tp_descr_get slot which is called by the interpreter - /// and returns an IncRef()ed pyHandle to itself. - /// 2) The ConstructorBinding object handles the [] syntax in its mp_subscript by matching - /// the Type object parameters to a constructor overload using Type.GetConstructor() - /// [NOTE: I don't know why method overloads are not searched the same way.] - /// and creating the BoundContructor object which contains ContructorInfo object. - /// 3) In tp_call, if ctorInfo is not null, ctorBinder.InvokeRaw() is called. - /// - [Serializable] - internal class ConstructorBinding : ExtensionType - { - private MaybeType type; // The managed Type being wrapped in a ClassObject - private ConstructorBinder ctorBinder; - - [NonSerialized] - private PyObject? repr; - - public ConstructorBinding(Type type, ReflectedClrType typeToCreate, ConstructorBinder ctorBinder) - { - this.type = type; - Debug.Assert(typeToCreate == ReflectedClrType.GetOrCreate(type)); - this.ctorBinder = ctorBinder; - } - - /// - /// Descriptor __get__ implementation. - /// Implements a Python type that wraps a CLR ctor call that requires the use - /// of a .Overloads[pyTypeOrType...] syntax to allow explicit ctor overload - /// selection. - /// - /// PyObject* to a Constructors wrapper - /// - /// the instance that the attribute was accessed through, - /// or None when the attribute is accessed through the owner - /// - /// always the owner class - /// - /// a CtorMapper (that borrows a reference to this python type and the - /// ClassObject's ConstructorBinder) wrapper. - /// - /// - /// Python 2.6.5 docs: - /// object.__get__(self, instance, owner) - /// Called to get the attribute of the owner class (class attribute access) - /// or of an instance of that class (instance attribute access). - /// owner is always the owner class, while instance is the instance that - /// the attribute was accessed through, or None when the attribute is accessed through the owner. - /// This method should return the (computed) attribute value or raise an AttributeError exception. - /// - public static NewReference tp_descr_get(BorrowedReference op, BorrowedReference instance, BorrowedReference owner) - { - var self = (ConstructorBinding?)GetManagedObject(op); - if (self == null) - { - Exceptions.SetError(Exceptions.AssertionError, "attempting to access destroyed object"); - return default; - } - - // It doesn't seem to matter if it's accessed through an instance (rather than via the type). - /*if (instance != IntPtr.Zero) { - // This is ugly! PyObject_IsInstance() returns 1 for true, 0 for false, -1 for error... - if (Runtime.PyObject_IsInstance(instance, owner) < 1) { - return Exceptions.RaiseTypeError("How in the world could that happen!"); - } - }*/ - return new NewReference(op); - } - - /// - /// Implement explicit overload selection using subscript syntax ([]). - /// - /// - /// ConstructorBinding.GetItem(PyObject *o, PyObject *key) - /// Return element of o corresponding to the object key or NULL on failure. - /// This is the equivalent of the Python expression o[key]. - /// - public static NewReference mp_subscript(BorrowedReference op, BorrowedReference key) - { - var self = (ConstructorBinding)GetManagedObject(op)!; - if (!self.type.Valid) - { - return Exceptions.RaiseTypeError(self.type.DeletedMessage); - } - Type tp = self.type.Value; - - Type[]? types = Runtime.PythonArgsToTypeArray(key); - if (types == null) - { - return Exceptions.RaiseTypeError("type(s) expected"); - } - //MethodBase[] methBaseArray = self.ctorBinder.GetMethods(); - //MethodBase ci = MatchSignature(methBaseArray, types); - ConstructorInfo ci = tp.GetConstructor(types); - if (ci == null) - { - return Exceptions.RaiseTypeError("No match found for constructor signature"); - } - var boundCtor = new BoundContructor(tp, self.ctorBinder, ci); - return boundCtor.Alloc(); - } - - /// - /// ConstructorBinding __repr__ implementation [borrowed from MethodObject]. - /// - public static NewReference tp_repr(BorrowedReference ob) - { - var self = (ConstructorBinding)GetManagedObject(ob)!; - if (self.repr is not null) - { - return new NewReference(self.repr); - } - MethodBase[] methods = self.ctorBinder.GetMethods(); - - if (!self.type.Valid) - { - return Exceptions.RaiseTypeError(self.type.DeletedMessage); - } - string name = self.type.Value.FullName; - var doc = ""; - foreach (MethodBase t in methods) - { - if (doc.Length > 0) - { - doc += "\n"; - } - string str = t.ToString(); - int idx = str.IndexOf("("); - doc += string.Format("{0}{1}", name, str.Substring(idx)); - } - using var docStr = Runtime.PyString_FromString(doc); - if (docStr.IsNull()) return default; - self.repr = docStr.MoveToPyObject(); - return new NewReference(self.repr); - } - } - - /// - /// Implements a Python type that constructs the given Type given a particular ContructorInfo. - /// - /// - /// Here mostly because I wanted a new __repr__ function for the selected constructor. - /// An earlier implementation hung the __call__ on the ContructorBinding class and - /// returned an Incref()ed self.pyHandle from the __get__ function. - /// - [Serializable] - internal class BoundContructor : ExtensionType - { - private Type type; // The managed Type being wrapped in a ClassObject - private ConstructorBinder ctorBinder; - private ConstructorInfo ctorInfo; - private PyObject? repr; - - public BoundContructor(Type type, ConstructorBinder ctorBinder, ConstructorInfo ci) - { - this.type = type; - this.ctorBinder = ctorBinder; - ctorInfo = ci; - } - - /// - /// BoundContructor.__call__(PyObject *callable_object, PyObject *args, PyObject *kw) - /// - /// PyObject *callable_object - /// PyObject *args - /// PyObject *kw - /// A reference to a new instance of the class by invoking the selected ctor(). - public static NewReference tp_call(BorrowedReference op, BorrowedReference args, BorrowedReference kw) - { - var self = (BoundContructor)GetManagedObject(op)!; - // Even though a call with null ctorInfo just produces the old behavior - /*if (self.ctorInfo == null) { - string msg = "Usage: Class.Overloads[CLR_or_python_Type, ...]"; - return Exceptions.RaiseTypeError(msg); - }*/ - // Bind using ConstructorBinder.Bind and invoke the ctor providing a null instancePtr - // which will fire self.ctorInfo using ConstructorInfo.Invoke(). - object? obj = self.ctorBinder.InvokeRaw(null, args, kw, self.ctorInfo); - if (obj == null) - { - // XXX set an error - return default; - } - // Instantiate the python object that wraps the result of the method call - // and return the PyObject* to it. - return CLRObject.GetReference(obj, ReflectedClrType.GetOrCreate(self.type)); - } - - /// - /// BoundContructor __repr__ implementation [borrowed from MethodObject]. - /// - public static NewReference tp_repr(BorrowedReference ob) - { - var self = (BoundContructor)GetManagedObject(ob)!; - if (self.repr is not null) - { - return new NewReference(self.repr); - } - string name = self.type.FullName; - string str = self.ctorInfo.ToString(); - int idx = str.IndexOf("("); - str = string.Format("returns a new {0}{1}", name, str.Substring(idx)); - using var docStr = Runtime.PyString_FromString(str); - if (docStr.IsNull()) return default; - self.repr = docStr.MoveToPyObject(); - return new NewReference(self.repr); - } - } -} diff --git a/src/runtime/exceptions.cs b/src/runtime/exceptions.cs index 479e7a5d5..5cf845155 100644 --- a/src/runtime/exceptions.cs +++ b/src/runtime/exceptions.cs @@ -76,6 +76,15 @@ internal ExceptionClassObject(Type tp) : base(tp) } return Runtime.PyString_FromString(message); } + + public override bool Init(BorrowedReference obj, BorrowedReference args, BorrowedReference kw) + { + if (!base.Init(obj, args, kw)) return false; + + var e = (CLRObject)GetManagedObject(obj)!; + + return Exceptions.SetArgsAndCause(obj, (Exception)e.inst); + } } /// @@ -149,7 +158,7 @@ internal static void Shutdown() /// __getattr__ implementation, and thus dereferencing a NULL /// pointer. /// - internal static void SetArgsAndCause(BorrowedReference ob, Exception e) + internal static bool SetArgsAndCause(BorrowedReference ob, Exception e) { NewReference args; if (!string.IsNullOrEmpty(e.Message)) @@ -167,8 +176,7 @@ internal static void SetArgsAndCause(BorrowedReference ob, Exception e) { if (Runtime.PyObject_SetAttrString(ob, "args", args.Borrow()) != 0) { - args.Dispose(); - throw PythonException.ThrowLastAsClrException(); + return false; } } @@ -178,6 +186,8 @@ internal static void SetArgsAndCause(BorrowedReference ob, Exception e) using var cause = CLRObject.GetReference(e.InnerException); Runtime.PyException_SetCause(ob, cause.Steal()); } + + return true; } /// diff --git a/src/runtime/managedtype.cs b/src/runtime/managedtype.cs index c71529628..2ed9d7970 100644 --- a/src/runtime/managedtype.cs +++ b/src/runtime/managedtype.cs @@ -122,6 +122,28 @@ internal void Load(BorrowedReference ob, Dictionary? context) protected virtual Dictionary? OnSave(BorrowedReference ob) => null; protected virtual void OnLoad(BorrowedReference ob, Dictionary? context) { } + /// + /// Initializes given object, or returns false and sets Python error on failure + /// + public virtual bool Init(BorrowedReference obj, BorrowedReference args, BorrowedReference kw) + { + // this just calls obj.__init__(*args, **kw) + using var init = Runtime.PyObject_GetAttr(obj, PyIdentifier.__init__); + Runtime.PyErr_Clear(); + + if (!init.IsNull()) + { + using var result = Runtime.PyObject_Call(init.Borrow(), args, kw); + + if (result.IsNull()) + { + return false; + } + } + + return true; + } + protected static void ClearObjectDict(BorrowedReference ob) { BorrowedReference type = Runtime.PyObject_TYPE(ob); diff --git a/src/runtime/metatype.cs b/src/runtime/metatype.cs index c51ce1a22..7558269b4 100644 --- a/src/runtime/metatype.cs +++ b/src/runtime/metatype.cs @@ -197,41 +197,25 @@ public static void tp_free(NewReference tp) /// public static NewReference tp_call(BorrowedReference tp, BorrowedReference args, BorrowedReference kw) { - IntPtr func = Util.ReadIntPtr(tp, TypeOffset.tp_new); - if (func == IntPtr.Zero) + IntPtr tp_new = Util.ReadIntPtr(tp, TypeOffset.tp_new); + if (tp_new == IntPtr.Zero) { return Exceptions.RaiseTypeError("invalid object"); } - using NewReference obj = NativeCall.Call_3(func, tp, args, kw); + using NewReference obj = NativeCall.Call_3(tp_new, tp, args, kw); if (obj.IsNull()) { return default; } - BorrowedReference objOrNull = CallInit(obj.Borrow(), args, kw); - return new NewReference(objOrNull, canBeNull: true); - } - - private static BorrowedReference CallInit(BorrowedReference obj, BorrowedReference args, BorrowedReference kw) - { - using var init = Runtime.PyObject_GetAttr(obj, PyIdentifier.__init__); - Runtime.PyErr_Clear(); - - if (!init.IsNull()) - { - using var result = Runtime.PyObject_Call(init.Borrow(), args, kw); + var type = GetManagedObject(tp)!; - if (result.IsNull()) - { - return default; - } - } - - return obj; + return type.Init(obj.Borrow(), args, kw) + ? obj.Move() + : default; } - /// /// Type __setattr__ implementation for reflected types. Note that this /// is slightly different than the standard setattr implementation for diff --git a/src/runtime/methodbinder.cs b/src/runtime/methodbinder.cs index 1c5da07c5..95c3aaa4a 100644 --- a/src/runtime/methodbinder.cs +++ b/src/runtime/methodbinder.cs @@ -55,14 +55,14 @@ internal void AddMethod(MethodBase m) /// Given a sequence of MethodInfo and a sequence of types, return the /// MethodInfo that matches the signature represented by those types. /// - internal static MethodInfo? MatchSignature(MethodInfo[] mi, Type[] tp) + internal static MethodBase? MatchSignature(MethodBase[] mi, Type[] tp) { if (tp == null) { return null; } int count = tp.Length; - foreach (MethodInfo t in mi) + foreach (MethodBase t in mi) { ParameterInfo[] pi = t.GetParameters(); if (pi.Length != count) @@ -89,7 +89,7 @@ internal void AddMethod(MethodBase m) /// return the MethodInfo that represents the matching closed generic. /// If unsuccessful, returns null and may set a Python error. /// - internal static MethodInfo? MatchParameters(MethodInfo[] mi, Type[]? tp) + internal static MethodInfo? MatchParameters(MethodBase[] mi, Type[]? tp) { if (tp == null) { @@ -128,7 +128,7 @@ internal void AddMethod(MethodBase m) /// Given a sequence of MethodInfo and two sequences of type parameters, /// return the MethodInfo that matches the signature and the closed generic. /// - internal static MethodInfo? MatchSignatureAndParameters(MethodInfo[] mi, Type[] genericTp, Type[] sigTp) + internal static MethodInfo? MatchSignatureAndParameters(MethodBase[] mi, Type[] genericTp, Type[] sigTp) { if (genericTp == null || sigTp == null) { @@ -364,7 +364,7 @@ public MismatchedMethod(Exception exception, MethodBase mb) /// If not null, only bind to that method. /// If not null, additionally attempt to bind to the generic methods in this array by inferring generic type parameters. /// A Binding if successful. Otherwise null. - internal Binding? Bind(BorrowedReference inst, BorrowedReference args, BorrowedReference kw, MethodBase? info, MethodInfo[]? methodinfo) + internal Binding? Bind(BorrowedReference inst, BorrowedReference args, BorrowedReference kw, MethodBase? info, MethodBase[]? methodinfo) { // loop to find match, return invoker w/ or w/o error var kwargDict = new Dictionary(); @@ -873,7 +873,7 @@ protected static void AppendArgumentTypes(StringBuilder to, BorrowedReference ar to.Append(')'); } - internal virtual NewReference Invoke(BorrowedReference inst, BorrowedReference args, BorrowedReference kw, MethodBase? info, MethodInfo[]? methodinfo) + internal virtual NewReference Invoke(BorrowedReference inst, BorrowedReference args, BorrowedReference kw, MethodBase? info, MethodBase[]? methodinfo) { // No valid methods, nothing to bind. if (GetMethods().Length == 0) @@ -943,20 +943,20 @@ internal virtual NewReference Invoke(BorrowedReference inst, BorrowedReference a // we return the out parameter as the result to Python (for // code compatibility with ironpython). - var mi = (MethodInfo)binding.info; + var returnType = binding.info.IsConstructor ? typeof(void) : ((MethodInfo)binding.info).ReturnType; if (binding.outs > 0) { - ParameterInfo[] pi = mi.GetParameters(); + ParameterInfo[] pi = binding.info.GetParameters(); int c = pi.Length; var n = 0; - bool isVoid = mi.ReturnType == typeof(void); + bool isVoid = returnType == typeof(void); int tupleSize = binding.outs + (isVoid ? 0 : 1); using var t = Runtime.PyTuple_New(tupleSize); if (!isVoid) { - using var v = Converter.ToPython(result, mi.ReturnType); + using var v = Converter.ToPython(result, returnType); Runtime.PyTuple_SetItem(t.Borrow(), n, v.Steal()); n++; } @@ -972,7 +972,7 @@ internal virtual NewReference Invoke(BorrowedReference inst, BorrowedReference a } } - if (binding.outs == 1 && mi.ReturnType == typeof(void)) + if (binding.outs == 1 && returnType == typeof(void)) { BorrowedReference item = Runtime.PyTuple_GetItem(t.Borrow(), 0); return new NewReference(item); @@ -981,7 +981,7 @@ internal virtual NewReference Invoke(BorrowedReference inst, BorrowedReference a return new NewReference(t.Borrow()); } - return Converter.ToPython(result, mi.ReturnType); + return Converter.ToPython(result, returnType); } } diff --git a/src/runtime/methodbinding.cs b/src/runtime/methodbinding.cs index 8dcd985d0..d9bf3aec6 100644 --- a/src/runtime/methodbinding.cs +++ b/src/runtime/methodbinding.cs @@ -1,11 +1,12 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using System.Reflection; namespace Python.Runtime { - using MaybeMethodInfo = MaybeMethodBase; + using MaybeMethodInfo = MaybeMethodBase; /// /// Implements a Python binding type for CLR methods. These work much like /// standard Python method bindings, but the same type is used to bind @@ -42,7 +43,9 @@ public static NewReference mp_subscript(BorrowedReference tp, BorrowedReference return Exceptions.RaiseTypeError("type(s) expected"); } - MethodInfo? mi = MethodBinder.MatchParameters(self.m.info, types); + MethodBase? mi = self.m.IsInstanceConstructor + ? self.m.type.Value.GetConstructor(types) + : MethodBinder.MatchParameters(self.m.info, types); if (mi == null) { return Exceptions.RaiseTypeError("No match found for given type params"); @@ -63,10 +66,9 @@ PyObject Signature infos = infos.Where(info => info.DeclaringType == type).ToArray(); // this is a primitive version // the overload with the maximum number of parameters should be used - MethodInfo primary = infos.OrderByDescending(i => i.GetParameters().Length).First(); + MethodBase primary = infos.OrderByDescending(i => i.GetParameters().Length).First(); var primaryParameters = primary.GetParameters(); PyObject signatureClass = Runtime.InspectModule.GetAttr("Signature"); - var primaryReturn = primary.ReturnParameter; using var parameters = new PyList(); using var parameterClass = primaryParameters.Length > 0 ? Runtime.InspectModule.GetAttr("Parameter") : null; diff --git a/src/runtime/methodobject.cs b/src/runtime/methodobject.cs index afbcaf631..397547616 100644 --- a/src/runtime/methodobject.cs +++ b/src/runtime/methodobject.cs @@ -5,7 +5,7 @@ namespace Python.Runtime { - using MaybeMethodInfo = MaybeMethodBase; + using MaybeMethodInfo = MaybeMethodBase; /// /// Implements a Python type that represents a CLR method. Method objects @@ -18,7 +18,7 @@ namespace Python.Runtime internal class MethodObject : ExtensionType { [NonSerialized] - private MethodInfo[]? _info = null; + private MethodBase[]? _info = null; private readonly List infoList; internal string name; internal readonly MethodBinder binder; @@ -27,13 +27,13 @@ internal class MethodObject : ExtensionType internal PyString? doc; internal MaybeType type; - public MethodObject(Type type, string name, MethodInfo[] info, bool allow_threads = MethodBinder.DefaultAllowThreads) + public MethodObject(Type type, string name, MethodBase[] info, bool allow_threads = MethodBinder.DefaultAllowThreads) { this.type = type; this.name = name; this.infoList = new List(); binder = new MethodBinder(); - foreach (MethodInfo item in info) + foreach (MethodBase item in info) { this.infoList.Add(item); binder.AddMethod(item); @@ -45,7 +45,9 @@ public MethodObject(Type type, string name, MethodInfo[] info, bool allow_thread binder.allow_threads = allow_threads; } - internal MethodInfo[] info + public bool IsInstanceConstructor => name == "__init__"; + + internal MethodBase[] info { get { diff --git a/src/runtime/operatormethod.cs b/src/runtime/operatormethod.cs index aad3f013f..035198f3e 100644 --- a/src/runtime/operatormethod.cs +++ b/src/runtime/operatormethod.cs @@ -86,7 +86,7 @@ public static bool IsOperatorMethod(MethodBase method) return OpMethodMap.ContainsKey(method.Name) || ComparisonOpMap.ContainsKey(method.Name); } - public static bool IsComparisonOp(MethodInfo method) + public static bool IsComparisonOp(MethodBase method) { return ComparisonOpMap.ContainsKey(method.Name); } @@ -170,7 +170,7 @@ public static string ReversePyMethodName(string pyName) /// /// The operator method. /// - public static bool IsReverse(MethodInfo method) + public static bool IsReverse(MethodBase method) { Type primaryType = method.IsOpsHelper() ? method.DeclaringType.GetGenericArguments()[0] @@ -179,10 +179,10 @@ public static bool IsReverse(MethodInfo method) return leftOperandType != primaryType; } - public static void FilterMethods(MethodInfo[] methods, out MethodInfo[] forwardMethods, out MethodInfo[] reverseMethods) + public static void FilterMethods(MethodBase[] methods, out MethodBase[] forwardMethods, out MethodBase[] reverseMethods) { - List forwardMethodsList = new List(); - List reverseMethodsList = new List(); + var forwardMethodsList = new List(); + var reverseMethodsList = new List(); foreach (var method in methods) { if (IsReverse(method)) diff --git a/src/runtime/overload.cs b/src/runtime/overload.cs index c75d38574..20939f4c6 100644 --- a/src/runtime/overload.cs +++ b/src/runtime/overload.cs @@ -35,7 +35,7 @@ public static NewReference mp_subscript(BorrowedReference tp, BorrowedReference return Exceptions.RaiseTypeError("type(s) expected"); } - MethodInfo? mi = MethodBinder.MatchSignature(self.m.info, types); + MethodBase? mi = MethodBinder.MatchSignature(self.m.info, types); if (mi == null) { var e = "No match found for signature"; diff --git a/src/testing/constructortests.cs b/src/testing/constructortests.cs index 4dc7f04d8..732692b3a 100644 --- a/src/testing/constructortests.cs +++ b/src/testing/constructortests.cs @@ -38,6 +38,16 @@ public StructConstructorTest(Guid v) } } + public struct GenericStructConstructorTest where T : struct + { + public T Value; + + public GenericStructConstructorTest(T value) + { + this.Value = value; + } + } + public class SubclassConstructorTest { @@ -66,4 +76,11 @@ public MultipleConstructorsTest(string s, params Type[] tp) type = tp; } } + + public class DefaultConstructorMatching + { + public double a; + public DefaultConstructorMatching() { a = 1; } + public DefaultConstructorMatching(double a) { this.a = a; } + } } diff --git a/tests/test_class.py b/tests/test_class.py index f961b3975..f63f05f4d 100644 --- a/tests/test_class.py +++ b/tests/test_class.py @@ -108,19 +108,21 @@ def test_subclass_with_various_constructors(): class SubClass(ClassCtorTest2): def __init__(self, v): - ClassCtorTest2.__init__(self) - self.value = v + ClassCtorTest2.__init__(self, v) + self.value2 = v inst = SubClass('test') assert inst.value == 'test' + assert inst.value2 == 'test' class SubClass2(ClassCtorTest2): def __init__(self, v): - ClassCtorTest2.__init__(self) - self.value = v + ClassCtorTest2.__init__(self, v) + self.value2 = v inst = SubClass2('test') assert inst.value == 'test' + assert inst.value2 == 'test' def test_struct_construction(): @@ -128,9 +130,9 @@ def test_struct_construction(): from Python.Test import Point - p = Point() - assert p.X == 0 - assert p.Y == 0 + # no default constructor, must supply arguments + with pytest.raises(TypeError): + p = Point() p = Point(0, 0) assert p.X == 0 diff --git a/tests/test_constructors.py b/tests/test_constructors.py index c305377f3..8e7ef2794 100644 --- a/tests/test_constructors.py +++ b/tests/test_constructors.py @@ -2,6 +2,8 @@ """Test CLR class constructor support.""" +import pytest + import System @@ -34,6 +36,11 @@ def test_struct_constructor(): assert ob.value == guid +def test_datetime(): + inst = System.DateTime(2021, 12, 29) + assert inst.Year == 2021 + + def test_subclass_constructor(): """Test subclass constructor args""" from Python.Test import SubclassConstructorTest @@ -48,8 +55,17 @@ class Sub(System.Exception): def test_multiple_constructor(): from Python.Test import MultipleConstructorsTest - import System # Test parameterless ob = MultipleConstructorsTest() assert ob.value == "" + + +def test_default_constructor_fallback(): + from Python.Test import DefaultConstructorMatching + + ob = DefaultConstructorMatching(2) + assert ob.a == 2 + + with pytest.raises(TypeError): + ob = DefaultConstructorMatching("2") diff --git a/tests/test_generic.py b/tests/test_generic.py index 9e1f1226b..e03ac57af 100644 --- a/tests/test_generic.py +++ b/tests/test_generic.py @@ -177,10 +177,22 @@ def test_generic_reference_type(): def test_generic_value_type(): """Test usage of generic value type definitions.""" + from System import Int32 + from Python.Test import GenericStructConstructorTest + + ob = GenericStructConstructorTest[Int32](42) + assert ob.Value == 42 + + +def test_nullable(): + """Test usage of Nullable[T] (special runtime handling).""" inst = System.Nullable[System.Int32](10) assert inst.HasValue assert inst.Value == 10 + with pytest.raises(TypeError): + inst = System.Nullable[System.Int32]() + def test_generic_interface(): # TODO NotImplemented From fabb22ca07514311d29c3d04874af7e69d9ed4cf Mon Sep 17 00:00:00 2001 From: Victor Nova Date: Tue, 4 Jan 2022 12:57:09 -0800 Subject: [PATCH 17/75] improved support for generic method overloading Prior to this change if method had multiple generic overloads, only 1 of them could be matched (whichever one reflection would return first) Now MethodBinder.MatchParameters returns all matching generic overloads, not just the first one. fixes https://github.com/pythonnet/pythonnet/issues/1522 --- src/runtime/methodbinder.cs | 38 ++++++++++++++++++++---------------- src/runtime/methodbinding.cs | 11 +++++++---- src/runtime/methodobject.cs | 5 ++++- src/testing/methodtest.cs | 3 +++ tests/test_generic.py | 12 ++++++++++++ 5 files changed, 47 insertions(+), 22 deletions(-) diff --git a/src/runtime/methodbinder.cs b/src/runtime/methodbinder.cs index 95c3aaa4a..42d3822fc 100644 --- a/src/runtime/methodbinder.cs +++ b/src/runtime/methodbinder.cs @@ -86,16 +86,17 @@ internal void AddMethod(MethodBase m) /// /// Given a sequence of MethodInfo and a sequence of type parameters, - /// return the MethodInfo that represents the matching closed generic. + /// return the MethodInfo(s) that represents the matching closed generic. /// If unsuccessful, returns null and may set a Python error. /// - internal static MethodInfo? MatchParameters(MethodBase[] mi, Type[]? tp) + internal static MethodInfo[] MatchParameters(MethodBase[] mi, Type[]? tp) { if (tp == null) { - return null; + return Array.Empty(); } int count = tp.Length; + var result = new List(); foreach (MethodInfo t in mi) { if (!t.IsGenericMethodDefinition) @@ -111,16 +112,14 @@ internal void AddMethod(MethodBase m) { // MakeGenericMethod can throw ArgumentException if the type parameters do not obey the constraints. MethodInfo method = t.MakeGenericMethod(tp); - Exceptions.Clear(); - return method; + result.Add(method); } - catch (ArgumentException e) + catch (ArgumentException) { - Exceptions.SetError(e); // The error will remain set until cleared by a successful match. } } - return null; + return result.ToArray(); } @@ -381,9 +380,6 @@ public MismatchedMethod(Exception exception, MethodBase mb) } } - var pynargs = (int)Runtime.PyTuple_Size(args); - var isGeneric = false; - MethodBase[] _methods; if (info != null) { @@ -395,11 +391,19 @@ public MismatchedMethod(Exception exception, MethodBase mb) _methods = GetMethods(); } - var argMatchedMethods = new List(_methods.Length); + return Bind(inst, args, kwargDict, _methods, matchGenerics: true); + } + + static Binding? Bind(BorrowedReference inst, BorrowedReference args, Dictionary kwargDict, MethodBase[] methods, bool matchGenerics) + { + var pynargs = (int)Runtime.PyTuple_Size(args); + var isGeneric = false; + + var argMatchedMethods = new List(methods.Length); var mismatchedMethods = new List(); // TODO: Clean up - foreach (MethodBase mi in _methods) + foreach (MethodBase mi in methods) { if (mi.IsGenericMethod) { @@ -535,17 +539,17 @@ public MismatchedMethod(Exception exception, MethodBase mb) return new Binding(mi, target, margs, outs); } - else if (isGeneric && info == null && methodinfo != null) + else if (matchGenerics && isGeneric) { // We weren't able to find a matching method but at least one // is a generic method and info is null. That happens when a generic // method was not called using the [] syntax. Let's introspect the // type of the arguments and use it to construct the correct method. Type[]? types = Runtime.PythonArgsToTypeArray(args, true); - MethodInfo? mi = MatchParameters(methodinfo, types); - if (mi != null) + MethodInfo[] overloads = MatchParameters(methods, types); + if (overloads.Length != 0) { - return Bind(inst, args, kw, mi, null); + return Bind(inst, args, kwargDict, overloads, matchGenerics: false); } } if (mismatchedMethods.Count > 0) diff --git a/src/runtime/methodbinding.cs b/src/runtime/methodbinding.cs index d9bf3aec6..6d21af01e 100644 --- a/src/runtime/methodbinding.cs +++ b/src/runtime/methodbinding.cs @@ -43,15 +43,18 @@ public static NewReference mp_subscript(BorrowedReference tp, BorrowedReference return Exceptions.RaiseTypeError("type(s) expected"); } - MethodBase? mi = self.m.IsInstanceConstructor - ? self.m.type.Value.GetConstructor(types) + MethodBase[] overloads = self.m.IsInstanceConstructor + ? self.m.type.Value.GetConstructor(types) is { } ctor + ? new[] { ctor } + : Array.Empty() : MethodBinder.MatchParameters(self.m.info, types); - if (mi == null) + if (overloads.Length == 0) { return Exceptions.RaiseTypeError("No match found for given type params"); } - var mb = new MethodBinding(self.m, self.target, self.targetType) { info = mi }; + MethodObject overloaded = self.m.WithOverloads(overloads); + var mb = new MethodBinding(overloaded, self.target, self.targetType); return mb.Alloc(); } diff --git a/src/runtime/methodobject.cs b/src/runtime/methodobject.cs index 397547616..b0fda49d3 100644 --- a/src/runtime/methodobject.cs +++ b/src/runtime/methodobject.cs @@ -27,7 +27,7 @@ internal class MethodObject : ExtensionType internal PyString? doc; internal MaybeType type; - public MethodObject(Type type, string name, MethodBase[] info, bool allow_threads = MethodBinder.DefaultAllowThreads) + public MethodObject(MaybeType type, string name, MethodBase[] info, bool allow_threads = MethodBinder.DefaultAllowThreads) { this.type = type; this.name = name; @@ -47,6 +47,9 @@ public MethodObject(Type type, string name, MethodBase[] info, bool allow_thread public bool IsInstanceConstructor => name == "__init__"; + public MethodObject WithOverloads(MethodBase[] overloads) + => new(type, name, overloads, allow_threads: binder.allow_threads); + internal MethodBase[] info { get diff --git a/src/testing/methodtest.cs b/src/testing/methodtest.cs index 9eae0e9f0..fe49de88d 100644 --- a/src/testing/methodtest.cs +++ b/src/testing/methodtest.cs @@ -646,6 +646,9 @@ public static int Overloaded(int i, string s) return i; } + public virtual void OverloadedConstrainedGeneric(T generic) where T : MethodTest { } + public virtual void OverloadedConstrainedGeneric(T generic, string str) where T: MethodTest { } + public static string CaseSensitive() { return "CaseSensitive"; diff --git a/tests/test_generic.py b/tests/test_generic.py index e03ac57af..6d514d638 100644 --- a/tests/test_generic.py +++ b/tests/test_generic.py @@ -762,6 +762,18 @@ def test_missing_generic_type(): with pytest.raises(TypeError): IList[bool] +# https://github.com/pythonnet/pythonnet/issues/1522 +def test_overload_generic_parameter(): + from Python.Test import MethodTest, MethodTestSub + + inst = MethodTest() + generic = MethodTestSub() + inst.OverloadedConstrainedGeneric(generic) + inst.OverloadedConstrainedGeneric[MethodTestSub](generic) + + inst.OverloadedConstrainedGeneric[MethodTestSub](generic, '42') + inst.OverloadedConstrainedGeneric[MethodTestSub](generic, System.String('42')) + def test_invalid_generic_type_parameter(): from Python.Test import GenericTypeWithConstraint with pytest.raises(TypeError): From 7e5cc29a62f987555d1adcfc120052805dde6ed8 Mon Sep 17 00:00:00 2001 From: Victor Date: Thu, 6 Jan 2022 00:56:04 -0800 Subject: [PATCH 18/75] use the same facility to access Py_NoSiteFlag as the one used to load Python C API functions (#1659) fixes https://github.com/pythonnet/pythonnet/issues/1517 --- src/runtime/runtime.cs | 25 ++++++------------------- 1 file changed, 6 insertions(+), 19 deletions(-) diff --git a/src/runtime/runtime.cs b/src/runtime/runtime.cs index a2ee45e9c..fc851c8ea 100644 --- a/src/runtime/runtime.cs +++ b/src/runtime/runtime.cs @@ -495,8 +495,6 @@ private static void NullGCHandles(IEnumerable objects) internal static PyType PyNoneType; internal static BorrowedReference PyTypeType => new(Delegates.PyType_Type); - internal static int* Py_NoSiteFlag; - internal static PyObject PyBytesType; internal static NativeFunc* _PyObject_NextNotImplemented; @@ -1881,24 +1879,11 @@ internal static IntPtr PyCapsule_GetPointer(BorrowedReference capsule, IntPtr na internal static void SetNoSiteFlag() { - var loader = LibraryLoader.Instance; - IntPtr dllLocal = IntPtr.Zero; - if (_PythonDll != "__Internal") - { - dllLocal = loader.Load(_PythonDll); - } - try - { - Py_NoSiteFlag = (int*)loader.GetFunction(dllLocal, "Py_NoSiteFlag"); - *Py_NoSiteFlag = 1; - } - finally + TryUsingDll(() => { - if (dllLocal != IntPtr.Zero) - { - loader.Free(dllLocal); - } - } + *Delegates.Py_NoSiteFlag = 1; + return *Delegates.Py_NoSiteFlag; + }); } internal static class Delegates @@ -2170,6 +2155,7 @@ static Delegates() catch (MissingMethodException) { } PyType_Type = GetFunctionByName(nameof(PyType_Type), GetUnmanagedDll(_PythonDll)); + Py_NoSiteFlag = (int*)GetFunctionByName(nameof(Py_NoSiteFlag), GetUnmanagedDll(_PythonDll)); } static global::System.IntPtr GetUnmanagedDll(string? libraryName) @@ -2426,6 +2412,7 @@ static Delegates() internal static delegate* unmanaged[Cdecl] _Py_NewReference { get; } internal static delegate* unmanaged[Cdecl] _Py_IsFinalizing { get; } internal static IntPtr PyType_Type { get; } + internal static int* Py_NoSiteFlag { get; } } } From efad01cf2efc4f8355ca0834ea347dd7a48458d2 Mon Sep 17 00:00:00 2001 From: Victor Date: Thu, 6 Jan 2022 01:24:15 -0800 Subject: [PATCH 19/75] provide `__int__` instance method on .NET enum types to support int(Enum.Member) (#1661) implements https://github.com/pythonnet/pythonnet/issues/1585 --- src/runtime/classmanager.cs | 14 ++++++++++++-- src/runtime/native/ITypeOffsets.cs | 1 + src/runtime/native/TypeOffset.cs | 1 + src/runtime/operatormethod.cs | 23 +++++++++++++++++------ src/runtime/opshelper.cs | 14 +++++++++++++- src/testing/enumtest.cs | 7 +++++-- tests/test_enum.py | 9 +++++++++ 7 files changed, 58 insertions(+), 11 deletions(-) diff --git a/src/runtime/classmanager.cs b/src/runtime/classmanager.cs index f8e108f41..647cec3ed 100644 --- a/src/runtime/classmanager.cs +++ b/src/runtime/classmanager.cs @@ -346,8 +346,7 @@ private static ClassInfo GetClassInfo(Type type, ClassBase impl) } } - // only [Flags] enums support bitwise operations - if (type.IsEnum && type.IsFlagsEnum()) + if (type.IsEnum) { var opsImpl = typeof(EnumOps<>).MakeGenericType(type); foreach (var op in opsImpl.GetMethods(OpsHelper.BindingFlags)) @@ -355,6 +354,17 @@ private static ClassInfo GetClassInfo(Type type, ClassBase impl) local.Add(op.Name); } info = info.Concat(opsImpl.GetMethods(OpsHelper.BindingFlags)).ToArray(); + + // only [Flags] enums support bitwise operations + if (type.IsFlagsEnum()) + { + opsImpl = typeof(FlagEnumOps<>).MakeGenericType(type); + foreach (var op in opsImpl.GetMethods(OpsHelper.BindingFlags)) + { + local.Add(op.Name); + } + info = info.Concat(opsImpl.GetMethods(OpsHelper.BindingFlags)).ToArray(); + } } // Now again to filter w/o losing overloaded member info diff --git a/src/runtime/native/ITypeOffsets.cs b/src/runtime/native/ITypeOffsets.cs index 0829e5bc9..2c4fdf59a 100644 --- a/src/runtime/native/ITypeOffsets.cs +++ b/src/runtime/native/ITypeOffsets.cs @@ -21,6 +21,7 @@ interface ITypeOffsets int nb_multiply { get; } int nb_true_divide { get; } int nb_and { get; } + int nb_int { get; } int nb_or { get; } int nb_xor { get; } int nb_lshift { get; } diff --git a/src/runtime/native/TypeOffset.cs b/src/runtime/native/TypeOffset.cs index 3f97b0f45..a1bae8253 100644 --- a/src/runtime/native/TypeOffset.cs +++ b/src/runtime/native/TypeOffset.cs @@ -30,6 +30,7 @@ static partial class TypeOffset internal static int nb_and { get; private set; } internal static int nb_or { get; private set; } internal static int nb_xor { get; private set; } + internal static int nb_int { get; private set; } internal static int nb_lshift { get; private set; } internal static int nb_rshift { get; private set; } internal static int nb_remainder { get; private set; } diff --git a/src/runtime/operatormethod.cs b/src/runtime/operatormethod.cs index 035198f3e..abe6ded1a 100644 --- a/src/runtime/operatormethod.cs +++ b/src/runtime/operatormethod.cs @@ -1,8 +1,8 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Linq; using System.Reflection; -using System.Runtime.InteropServices; using System.Text; namespace Python.Runtime @@ -51,6 +51,8 @@ static OperatorMethod() ["op_OnesComplement"] = new SlotDefinition("__invert__", TypeOffset.nb_invert), ["op_UnaryNegation"] = new SlotDefinition("__neg__", TypeOffset.nb_negative), ["op_UnaryPlus"] = new SlotDefinition("__pos__", TypeOffset.nb_positive), + + ["__int__"] = new SlotDefinition("__int__", TypeOffset.nb_int), }; ComparisonOpMap = new Dictionary { @@ -97,14 +99,11 @@ public static bool IsComparisonOp(MethodBase method) /// public static void FixupSlots(BorrowedReference pyType, Type clrType) { - const BindingFlags flags = BindingFlags.Public | BindingFlags.Static; Debug.Assert(_opType != null); - var staticMethods = - clrType.IsEnum ? typeof(EnumOps<>).MakeGenericType(clrType).GetMethods(flags) - : clrType.GetMethods(flags); + var operatorCandidates = GetOperatorCandidates(clrType); - foreach (var method in staticMethods) + foreach (var method in operatorCandidates) { // We only want to override slots for operators excluding // comparison operators, which are handled by ClassBase.tp_richcompare. @@ -124,6 +123,18 @@ public static void FixupSlots(BorrowedReference pyType, Type clrType) } } + static IEnumerable GetOperatorCandidates(Type clrType) + { + const BindingFlags flags = BindingFlags.Public | BindingFlags.Static; + if (clrType.IsEnum) + { + return typeof(EnumOps<>).MakeGenericType(clrType).GetMethods(flags) + .Concat(typeof(FlagEnumOps<>).MakeGenericType(clrType).GetMethods(flags)); + } + + return clrType.GetMethods(flags); + } + public static string GetPyMethodName(string clrName) { if (OpMethodMap.ContainsKey(clrName)) diff --git a/src/runtime/opshelper.cs b/src/runtime/opshelper.cs index 59f7704b7..ab623f3de 100644 --- a/src/runtime/opshelper.cs +++ b/src/runtime/opshelper.cs @@ -38,7 +38,7 @@ public static Expression EnumUnderlyingValue(Expression enumValue) internal class OpsAttribute: Attribute { } [Ops] - internal static class EnumOps where T : Enum + internal static class FlagEnumOps where T : Enum { static readonly Func and = BinaryOp(Expression.And); static readonly Func or = BinaryOp(Expression.Or); @@ -74,4 +74,16 @@ static Func UnaryOp(Func op) }); } } + + [Ops] + internal static class EnumOps where T : Enum + { + [ForbidPythonThreads] +#pragma warning disable IDE1006 // Naming Styles - must match Python + public static PyInt __int__(T value) +#pragma warning restore IDE1006 // Naming Styles + => typeof(T).GetEnumUnderlyingType() == typeof(UInt64) + ? new PyInt(Convert.ToUInt64(value)) + : new PyInt(Convert.ToInt64(value)); + } } diff --git a/src/testing/enumtest.cs b/src/testing/enumtest.cs index de5d8f5ee..f7d07339f 100644 --- a/src/testing/enumtest.cs +++ b/src/testing/enumtest.cs @@ -72,7 +72,9 @@ public enum LongEnum : long Two, Three, Four, - Five + Five, + Max = long.MaxValue, + Min = long.MinValue, } public enum ULongEnum : ulong @@ -82,7 +84,8 @@ public enum ULongEnum : ulong Two, Three, Four, - Five + Five, + Max = ulong.MaxValue, } [Flags] diff --git a/tests/test_enum.py b/tests/test_enum.py index 1f0711a94..b2eb0569f 100644 --- a/tests/test_enum.py +++ b/tests/test_enum.py @@ -87,6 +87,15 @@ def test_ulong_enum(): assert Test.ULongEnum.Two == Test.ULongEnum(2) +def test_long_enum_to_int(): + assert int(Test.LongEnum.Max) == 9223372036854775807 + assert int(Test.LongEnum.Min) == -9223372036854775808 + + +def test_ulong_enum_to_int(): + assert int(Test.ULongEnum.Max) == 18446744073709551615 + + def test_instantiate_enum_fails(): """Test that instantiation of an enum class fails.""" from System import DayOfWeek From 88850f5dc9fed1658c23ff2088ae66d3e7676bfb Mon Sep 17 00:00:00 2001 From: Victor Nova Date: Wed, 5 Jan 2022 18:58:21 -0800 Subject: [PATCH 20/75] cleanup PyBuffer a bit: - better finalizer, that actually calls PyBuffer_Release - improved input parameter handling for common routines - added support for copying data to/from large Python buffers fixes https://github.com/pythonnet/pythonnet/issues/1556 --- src/embed_tests/TestPyBuffer.cs | 104 +++++++++++++++++++++++--------- src/runtime/bufferinterface.cs | 5 +- src/runtime/finalizer.cs | 25 +++++++- src/runtime/pybuffer.cs | 68 ++++++++++++--------- src/runtime/pyobject.cs | 6 ++ src/runtime/runtime.cs | 15 ++++- 6 files changed, 160 insertions(+), 63 deletions(-) diff --git a/src/embed_tests/TestPyBuffer.cs b/src/embed_tests/TestPyBuffer.cs index 43ed5ffd4..a1bcc161d 100644 --- a/src/embed_tests/TestPyBuffer.cs +++ b/src/embed_tests/TestPyBuffer.cs @@ -1,4 +1,5 @@ using System; +using System.Runtime.CompilerServices; using System.Text; using NUnit.Framework; using Python.Runtime; @@ -24,24 +25,20 @@ public void Dispose() public void TestBufferWrite() { string bufferTestString = "hello world! !$%&/()=?"; + string bufferTestString2 = "h llo world! !$%&/()=?"; - using (Py.GIL()) + using var _ = Py.GIL(); + + using var pythonArray = ByteArrayFromAsciiString(bufferTestString); + + using (PyBuffer buf = pythonArray.GetBuffer(PyBUF.WRITABLE)) { - using (var scope = Py.CreateScope()) - { - scope.Exec($"arr = bytearray({bufferTestString.Length})"); - PyObject pythonArray = scope.Get("arr"); - byte[] managedArray = new UTF8Encoding().GetBytes(bufferTestString); - - using (PyBuffer buf = pythonArray.GetBuffer()) - { - buf.Write(managedArray, 0, managedArray.Length); - } - - string result = scope.Eval("arr.decode('utf-8')").ToString(); - Assert.IsTrue(result == bufferTestString); - } + byte[] managedArray = { (byte)' ' }; + buf.Write(managedArray, 0, managedArray.Length, 1); } + + string result = pythonArray.InvokeMethod("decode", "utf-8".ToPython()).As(); + Assert.IsTrue(result == bufferTestString2); } [Test] @@ -49,23 +46,19 @@ public void TestBufferRead() { string bufferTestString = "hello world! !$%&/()=?"; - using (Py.GIL()) + using var _ = Py.GIL(); + + using var pythonArray = ByteArrayFromAsciiString(bufferTestString); + byte[] managedArray = new byte[bufferTestString.Length]; + + using (PyBuffer buf = pythonArray.GetBuffer()) { - using (var scope = Py.CreateScope()) - { - scope.Exec($"arr = b'{bufferTestString}'"); - PyObject pythonArray = scope.Get("arr"); - byte[] managedArray = new byte[bufferTestString.Length]; - - using (PyBuffer buf = pythonArray.GetBuffer()) - { - buf.Read(managedArray, 0, managedArray.Length); - } - - string result = new UTF8Encoding().GetString(managedArray); - Assert.IsTrue(result == bufferTestString); - } + managedArray[0] = (byte)' '; + buf.Read(managedArray, 1, managedArray.Length - 1, 1); } + + string result = new UTF8Encoding().GetString(managedArray); + Assert.IsTrue(result == " " + bufferTestString.Substring(1)); } [Test] @@ -77,5 +70,56 @@ public void ArrayHasBuffer() Assert.AreEqual(1, mem[(0, 0).ToPython()].As()); Assert.AreEqual(array[1,0], mem[(1, 0).ToPython()].As()); } + + [Test] + public void RefCount() + { + using var _ = Py.GIL(); + using var arr = ByteArrayFromAsciiString("hello world! !$%&/()=?"); + + Assert.AreEqual(1, arr.Refcount); + + using (PyBuffer buf = arr.GetBuffer()) + { + Assert.AreEqual(2, arr.Refcount); + } + + Assert.AreEqual(1, arr.Refcount); + } + + [Test] + public void Finalization() + { + if (Type.GetType("Mono.Runtime") is not null) + { + Assert.Inconclusive("test unreliable in Mono"); + return; + } + + using var _ = Py.GIL(); + using var arr = ByteArrayFromAsciiString("hello world! !$%&/()=?"); + + Assert.AreEqual(1, arr.Refcount); + + MakeBufAndLeak(arr); + + GC.Collect(); + GC.WaitForPendingFinalizers(); + Finalizer.Instance.Collect(); + + Assert.AreEqual(1, arr.Refcount); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static void MakeBufAndLeak(PyObject bufProvider) + { + PyBuffer buf = bufProvider.GetBuffer(); + } + + static PyObject ByteArrayFromAsciiString(string str) + { + using var scope = Py.CreateScope(); + return Runtime.Runtime.PyByteArray_FromStringAndSize(str).MoveToPyObject(); + } } } diff --git a/src/runtime/bufferinterface.cs b/src/runtime/bufferinterface.cs index e0b71a925..431d609f4 100644 --- a/src/runtime/bufferinterface.cs +++ b/src/runtime/bufferinterface.cs @@ -8,10 +8,11 @@ namespace Python.Runtime internal struct Py_buffer { public IntPtr buf; public IntPtr obj; /* owned reference */ + /// Buffer size in bytes [MarshalAs(UnmanagedType.SysInt)] - public IntPtr len; + public nint len; [MarshalAs(UnmanagedType.SysInt)] - public IntPtr itemsize; /* This is Py_ssize_t so it can be + public nint itemsize; /* This is Py_ssize_t so it can be pointed to by strides in simple case.*/ [MarshalAs(UnmanagedType.Bool)] public bool _readonly; diff --git a/src/runtime/finalizer.cs b/src/runtime/finalizer.cs index bfb1e228d..be17d62e3 100644 --- a/src/runtime/finalizer.cs +++ b/src/runtime/finalizer.cs @@ -43,6 +43,7 @@ public ErrorArgs(Exception error) private ConcurrentQueue _objQueue = new(); private readonly ConcurrentQueue _derivedQueue = new(); + private readonly ConcurrentQueue _bufferQueue = new(); private int _throttled; #region FINALIZER_CHECK @@ -165,6 +166,19 @@ internal void AddDerivedFinalizedObject(ref IntPtr derived, int run) _derivedQueue.Enqueue(pending); } + internal void AddFinalizedBuffer(ref Py_buffer buffer) + { + if (buffer.obj == IntPtr.Zero) + throw new ArgumentNullException(nameof(buffer)); + + if (!Enable) + return; + + var pending = buffer; + buffer = default; + _bufferQueue.Enqueue(pending); + } + internal static void Initialize() { Instance.started = true; @@ -178,7 +192,7 @@ internal static void Shutdown() internal nint DisposeAll() { - if (_objQueue.IsEmpty && _derivedQueue.IsEmpty) + if (_objQueue.IsEmpty && _derivedQueue.IsEmpty && _bufferQueue.IsEmpty) return 0; nint collected = 0; @@ -242,6 +256,15 @@ internal nint DisposeAll() collected++; } + + while (!_bufferQueue.IsEmpty) + { + if (!_bufferQueue.TryDequeue(out var buffer)) + continue; + + Runtime.PyBuffer_Release(ref buffer); + collected++; + } } finally { diff --git a/src/runtime/pybuffer.cs b/src/runtime/pybuffer.cs index 60aeaf0b9..de0e7122f 100644 --- a/src/runtime/pybuffer.cs +++ b/src/runtime/pybuffer.cs @@ -89,7 +89,9 @@ public static long SizeFromFormat(string format) { if (Runtime.PyVersion < new Version(3,9)) throw new NotSupportedException("SizeFromFormat requires at least Python 3.9"); - return (long)Runtime.PyBuffer_SizeFromFormat(format); + nint result = Runtime.PyBuffer_SizeFromFormat(format); + if (result == -1) throw PythonException.ThrowLastAsClrException(); + return result; } /// @@ -113,7 +115,7 @@ public IntPtr GetPointer(long[] indices) throw new ObjectDisposedException(nameof(PyBuffer)); if (Runtime.PyVersion < new Version(3, 7)) throw new NotSupportedException("GetPointer requires at least Python 3.7"); - return Runtime.PyBuffer_GetPointer(ref _view, indices.Select(x => (IntPtr)x).ToArray()); + return Runtime.PyBuffer_GetPointer(ref _view, indices.Select(x => checked((nint)x)).ToArray()); } /// @@ -126,7 +128,7 @@ public void FromContiguous(IntPtr buf, long len, BufferOrderStyle fort) if (Runtime.PyVersion < new Version(3, 7)) throw new NotSupportedException("FromContiguous requires at least Python 3.7"); - if (Runtime.PyBuffer_FromContiguous(ref _view, buf, (IntPtr)len, OrderStyleToChar(fort, false)) < 0) + if (Runtime.PyBuffer_FromContiguous(ref _view, buf, checked((nint)len), OrderStyleToChar(fort, false)) < 0) throw PythonException.ThrowLastAsClrException(); } @@ -173,44 +175,60 @@ internal void FillInfo(BorrowedReference exporter, IntPtr buf, long len, bool _r /// /// Writes a managed byte array into the buffer of a python object. This can be used to pass data like images from managed to python. /// - public void Write(byte[] buffer, int offset, int count) + public void Write(byte[] buffer, int sourceOffset, int count, nint destinationOffset) { if (disposedValue) throw new ObjectDisposedException(nameof(PyBuffer)); + if (_view.ndim != 1) + throw new NotImplementedException("Multidimensional arrays, scalars and objects without a buffer are not supported."); + if (!this.IsContiguous(BufferOrderStyle.C)) + throw new NotImplementedException("Only continuous buffers are supported"); if (ReadOnly) throw new InvalidOperationException("Buffer is read-only"); - if ((long)_view.len > int.MaxValue) - throw new NotSupportedException("Python buffers bigger than int.MaxValue are not supported."); - if (count > buffer.Length) + if (buffer is null) + throw new ArgumentNullException(nameof(buffer)); + + if (sourceOffset < 0) + throw new IndexOutOfRangeException($"{nameof(sourceOffset)} is negative"); + if (destinationOffset < 0) + throw new IndexOutOfRangeException($"{nameof(destinationOffset)} is negative"); + if (count < 0) + throw new ArgumentOutOfRangeException(nameof(count), count, "Value must be >= 0"); + + if (checked(count + sourceOffset) > buffer.Length) throw new ArgumentOutOfRangeException("count", "Count is bigger than the buffer."); - if (count > (int)_view.len) + if (checked(count + destinationOffset) > _view.len) throw new ArgumentOutOfRangeException("count", "Count is bigger than the python buffer."); - if (_view.ndim != 1) - throw new NotSupportedException("Multidimensional arrays, scalars and objects without a buffer are not supported."); - if (!this.IsContiguous(BufferOrderStyle.C)) - throw new NotImplementedException("Only continuous buffers are supported"); - Marshal.Copy(buffer, offset, _view.buf, count); + Marshal.Copy(buffer, sourceOffset, _view.buf + destinationOffset, count); } /// /// Reads the buffer of a python object into a managed byte array. This can be used to pass data like images from python to managed. /// - public int Read(byte[] buffer, int offset, int count) { + public void Read(byte[] buffer, int destinationOffset, int count, nint sourceOffset) { if (disposedValue) throw new ObjectDisposedException(nameof(PyBuffer)); - if (count > buffer.Length) - throw new ArgumentOutOfRangeException("count", "Count is bigger than the buffer."); if (_view.ndim != 1) - throw new NotSupportedException("Multidimensional arrays, scalars and objects without a buffer are not supported."); - if (_view.len.ToInt64() > int.MaxValue) - throw new NotSupportedException("Python buffers bigger than int.MaxValue are not supported."); + throw new NotImplementedException("Multidimensional arrays, scalars and objects without a buffer are not supported."); if (!this.IsContiguous(BufferOrderStyle.C)) throw new NotImplementedException("Only continuous buffers are supported"); + if (buffer is null) + throw new ArgumentNullException(nameof(buffer)); + + if (sourceOffset < 0) + throw new IndexOutOfRangeException($"{nameof(sourceOffset)} is negative"); + if (destinationOffset < 0) + throw new IndexOutOfRangeException($"{nameof(destinationOffset)} is negative"); + if (count < 0) + throw new ArgumentOutOfRangeException(nameof(count), count, "Value must be >= 0"); + + if (checked(count + destinationOffset) > buffer.Length) + throw new ArgumentOutOfRangeException("count", "Count is bigger than the buffer."); + if (checked(count + sourceOffset) > _view.len) + throw new ArgumentOutOfRangeException("count", "Count is bigger than the python buffer."); - int copylen = count < (int)_view.len ? count : (int)_view.len; - Marshal.Copy(_view.buf, buffer, offset, copylen); - return copylen; + Marshal.Copy(_view.buf + sourceOffset, buffer, destinationOffset, count); } private bool disposedValue = false; // To detect redundant calls @@ -240,11 +258,7 @@ private void Dispose(bool disposing) if (_view.obj != IntPtr.Zero) { - Finalizer.Instance.AddFinalizedObject(ref _view.obj, _exporter.run -#if TRACE_ALLOC - , _exporter.Traceback -#endif - ); + Finalizer.Instance.AddFinalizedBuffer(ref _view); } Dispose(false); diff --git a/src/runtime/pyobject.cs b/src/runtime/pyobject.cs index 709bc11c4..373751cf6 100644 --- a/src/runtime/pyobject.cs +++ b/src/runtime/pyobject.cs @@ -179,6 +179,11 @@ public static PyObject FromManagedObject(object ob) internal bool IsDisposed => rawPtr == IntPtr.Zero; + void CheckDisposed() + { + if (IsDisposed) throw new ObjectDisposedException(nameof(PyObject)); + } + protected virtual void Dispose(bool disposing) { if (IsDisposed) @@ -1114,6 +1119,7 @@ public override int GetHashCode() /// public PyBuffer GetBuffer(PyBUF flags = PyBUF.SIMPLE) { + CheckDisposed(); return new PyBuffer(this, flags); } diff --git a/src/runtime/runtime.cs b/src/runtime/runtime.cs index fc851c8ea..c8489f7cf 100644 --- a/src/runtime/runtime.cs +++ b/src/runtime/runtime.cs @@ -1107,7 +1107,7 @@ internal static nint PyBuffer_SizeFromFormat(string format) internal static int PyBuffer_IsContiguous(ref Py_buffer view, char order) => Delegates.PyBuffer_IsContiguous(ref view, order); - internal static IntPtr PyBuffer_GetPointer(ref Py_buffer view, IntPtr[] indices) => Delegates.PyBuffer_GetPointer(ref view, indices); + internal static IntPtr PyBuffer_GetPointer(ref Py_buffer view, nint[] indices) => Delegates.PyBuffer_GetPointer(ref view, indices); internal static int PyBuffer_FromContiguous(ref Py_buffer view, IntPtr buf, IntPtr len, char fort) => Delegates.PyBuffer_FromContiguous(ref view, buf, len, fort); @@ -1362,6 +1362,13 @@ internal static NewReference EmptyPyBytes() return Delegates.PyBytes_FromString((IntPtr)bytes); } + internal static NewReference PyByteArray_FromStringAndSize(IntPtr strPtr, nint len) => Delegates.PyByteArray_FromStringAndSize(strPtr, len); + internal static NewReference PyByteArray_FromStringAndSize(string s) + { + using var ptr = new StrPtr(s, Encoding.UTF8); + return PyByteArray_FromStringAndSize(ptr.RawPointer, checked((nint)ptr.ByteCount)); + } + internal static IntPtr PyBytes_AsString(BorrowedReference ob) { Debug.Assert(ob != null); @@ -1977,7 +1984,7 @@ static Delegates() // only in 3.9+ } PyBuffer_IsContiguous = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyBuffer_IsContiguous), GetUnmanagedDll(_PythonDll)); - PyBuffer_GetPointer = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyBuffer_GetPointer), GetUnmanagedDll(_PythonDll)); + PyBuffer_GetPointer = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyBuffer_GetPointer), GetUnmanagedDll(_PythonDll)); PyBuffer_FromContiguous = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyBuffer_FromContiguous), GetUnmanagedDll(_PythonDll)); PyBuffer_ToContiguous = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyBuffer_ToContiguous), GetUnmanagedDll(_PythonDll)); PyBuffer_FillContiguousStrides = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyBuffer_FillContiguousStrides), GetUnmanagedDll(_PythonDll)); @@ -2037,6 +2044,7 @@ static Delegates() PySequence_List = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PySequence_List), GetUnmanagedDll(_PythonDll)); PyBytes_AsString = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyBytes_AsString), GetUnmanagedDll(_PythonDll)); PyBytes_FromString = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyBytes_FromString), GetUnmanagedDll(_PythonDll)); + PyByteArray_FromStringAndSize = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyByteArray_FromStringAndSize), GetUnmanagedDll(_PythonDll)); PyBytes_Size = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyBytes_Size), GetUnmanagedDll(_PythonDll)); PyUnicode_AsUTF8 = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyUnicode_AsUTF8), GetUnmanagedDll(_PythonDll)); PyUnicode_DecodeUTF16 = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyUnicode_DecodeUTF16), GetUnmanagedDll(_PythonDll)); @@ -2250,7 +2258,7 @@ static Delegates() internal static delegate* unmanaged[Cdecl] PyBuffer_Release { get; } internal static delegate* unmanaged[Cdecl] PyBuffer_SizeFromFormat { get; } internal static delegate* unmanaged[Cdecl] PyBuffer_IsContiguous { get; } - internal static delegate* unmanaged[Cdecl] PyBuffer_GetPointer { get; } + internal static delegate* unmanaged[Cdecl] PyBuffer_GetPointer { get; } internal static delegate* unmanaged[Cdecl] PyBuffer_FromContiguous { get; } internal static delegate* unmanaged[Cdecl] PyBuffer_ToContiguous { get; } internal static delegate* unmanaged[Cdecl] PyBuffer_FillContiguousStrides { get; } @@ -2310,6 +2318,7 @@ static Delegates() internal static delegate* unmanaged[Cdecl] PySequence_List { get; } internal static delegate* unmanaged[Cdecl] PyBytes_AsString { get; } internal static delegate* unmanaged[Cdecl] PyBytes_FromString { get; } + internal static delegate* unmanaged[Cdecl] PyByteArray_FromStringAndSize { get; } internal static delegate* unmanaged[Cdecl] PyBytes_Size { get; } internal static delegate* unmanaged[Cdecl] PyUnicode_AsUTF8 { get; } internal static delegate* unmanaged[Cdecl] PyUnicode_DecodeUTF16 { get; } From 7450c5cb9a7adeccf9e871e2a50ee2c2bbeed748 Mon Sep 17 00:00:00 2001 From: Victor Nova Date: Thu, 6 Jan 2022 15:30:38 -0800 Subject: [PATCH 21/75] added support for byref parameters when overriding .NET methods from Python new code is emitted to 1. unpack the tuple returned from Python to extract new values for byref parameters and modify args array correspondingly 2. marshal those new values from the args array back into arguments in IL fixes https://github.com/pythonnet/pythonnet/issues/1481 --- CHANGELOG.md | 2 + src/runtime/classderived.cs | 109 ++++++++++++++++++++++++++++----- src/runtime/codegenerator.cs | 35 +++++++++++ src/runtime/delegatemanager.cs | 23 +------ src/testing/interfacetest.cs | 14 +++++ tests/test_interface.py | 14 +++++ 6 files changed, 160 insertions(+), 37 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 16489a9c9..cf64c3a64 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ This document follows the conventions laid out in [Keep a CHANGELOG][]. - Python operator method will call C# operator method for supported binary and unary operators ([#1324][p1324]). - Add GetPythonThreadID and Interrupt methods in PythonEngine - Ability to implement delegates with `ref` and `out` parameters in Python, by returning the modified parameter values in a tuple. ([#1355][i1355]) +- Ability to override .NET methods that have `out` or `ref` in Python by returning the modified parameter values in a tuple. ([#1481][i1481]) - `PyType` - a wrapper for Python type objects, that also permits creating new heap types from `TypeSpec` - Improved exception handling: * exceptions can now be converted with codecs @@ -879,3 +880,4 @@ This version improves performance on benchmarks significantly compared to 2.3. [i449]: https://github.com/pythonnet/pythonnet/issues/449 [i1342]: https://github.com/pythonnet/pythonnet/issues/1342 [i238]: https://github.com/pythonnet/pythonnet/issues/238 +[i1481]: https://github.com/pythonnet/pythonnet/issues/1481 diff --git a/src/runtime/classderived.cs b/src/runtime/classderived.cs index 47c9b4e0e..da1bf0f9a 100644 --- a/src/runtime/classderived.cs +++ b/src/runtime/classderived.cs @@ -429,24 +429,33 @@ private static void AddVirtualMethod(MethodInfo method, Type baseType, TypeBuild il.Emit(OpCodes.Ldloc_0); il.Emit(OpCodes.Ldc_I4, i); il.Emit(OpCodes.Ldarg, i + 1); - if (parameterTypes[i].IsValueType) + var type = parameterTypes[i]; + if (type.IsByRef) { - il.Emit(OpCodes.Box, parameterTypes[i]); + type = type.GetElementType(); + il.Emit(OpCodes.Ldobj, type); + } + if (type.IsValueType) + { + il.Emit(OpCodes.Box, type); } il.Emit(OpCodes.Stelem, typeof(object)); } il.Emit(OpCodes.Ldloc_0); + + il.Emit(OpCodes.Ldtoken, method); #pragma warning disable CS0618 // PythonDerivedType is for internal use only if (method.ReturnType == typeof(void)) { - il.Emit(OpCodes.Call, typeof(PythonDerivedType).GetMethod("InvokeMethodVoid")); + il.Emit(OpCodes.Call, typeof(PythonDerivedType).GetMethod(nameof(InvokeMethodVoid))); } else { il.Emit(OpCodes.Call, - typeof(PythonDerivedType).GetMethod("InvokeMethod").MakeGenericMethod(method.ReturnType)); + typeof(PythonDerivedType).GetMethod(nameof(InvokeMethod)).MakeGenericMethod(method.ReturnType)); } #pragma warning restore CS0618 // PythonDerivedType is for internal use only + CodeGenerator.GenerateMarshalByRefsBack(il, parameterTypes); il.Emit(OpCodes.Ret); } @@ -500,35 +509,65 @@ private static void AddPythonMethod(string methodName, PyObject func, TypeBuilde argTypes.ToArray()); ILGenerator il = methodBuilder.GetILGenerator(); + il.DeclareLocal(typeof(object[])); + il.DeclareLocal(typeof(RuntimeMethodHandle)); + + // this il.Emit(OpCodes.Ldarg_0); + + // Python method to call il.Emit(OpCodes.Ldstr, methodName); + + // original method name il.Emit(OpCodes.Ldnull); // don't fall back to the base type's method + + // create args array il.Emit(OpCodes.Ldc_I4, argTypes.Count); il.Emit(OpCodes.Newarr, typeof(object)); il.Emit(OpCodes.Stloc_0); + + // fill args array for (var i = 0; i < argTypes.Count; ++i) { il.Emit(OpCodes.Ldloc_0); il.Emit(OpCodes.Ldc_I4, i); il.Emit(OpCodes.Ldarg, i + 1); - if (argTypes[i].IsValueType) + var type = argTypes[i]; + if (type.IsByRef) { - il.Emit(OpCodes.Box, argTypes[i]); + type = type.GetElementType(); + il.Emit(OpCodes.Ldobj, type); + } + if (type.IsValueType) + { + il.Emit(OpCodes.Box, type); } il.Emit(OpCodes.Stelem, typeof(object)); } + + // args array il.Emit(OpCodes.Ldloc_0); + + // method handle for the base method is null + il.Emit(OpCodes.Ldloca_S, 1); + il.Emit(OpCodes.Initobj, typeof(RuntimeMethodHandle)); + il.Emit(OpCodes.Ldloc_1); #pragma warning disable CS0618 // PythonDerivedType is for internal use only + + // invoke the method if (returnType == typeof(void)) { - il.Emit(OpCodes.Call, typeof(PythonDerivedType).GetMethod("InvokeMethodVoid")); + il.Emit(OpCodes.Call, typeof(PythonDerivedType).GetMethod(nameof(InvokeMethodVoid))); } else { il.Emit(OpCodes.Call, - typeof(PythonDerivedType).GetMethod("InvokeMethod").MakeGenericMethod(returnType)); + typeof(PythonDerivedType).GetMethod(nameof(InvokeMethod)).MakeGenericMethod(returnType)); } + + CodeGenerator.GenerateMarshalByRefsBack(il, argTypes); + #pragma warning restore CS0618 // PythonDerivedType is for internal use only il.Emit(OpCodes.Ret); } @@ -672,7 +711,8 @@ public class PythonDerivedType /// method binding (i.e. it has been overridden in the derived python /// class) it calls it, otherwise it calls the base method. /// - public static T? InvokeMethod(IPythonDerivedType obj, string methodName, string origMethodName, object[] args) + public static T? InvokeMethod(IPythonDerivedType obj, string methodName, string origMethodName, + object[] args, RuntimeMethodHandle methodHandle) { var self = GetPyObj(obj); @@ -698,8 +738,10 @@ public class PythonDerivedType } PyObject py_result = method.Invoke(pyargs); - disposeList.Add(py_result); - return py_result.As(); + PyTuple? result_tuple = MarshalByRefsBack(args, methodHandle, py_result, outsOffset: 1); + return result_tuple is not null + ? result_tuple[0].As() + : py_result.As(); } } } @@ -726,7 +768,7 @@ public class PythonDerivedType } public static void InvokeMethodVoid(IPythonDerivedType obj, string methodName, string origMethodName, - object[] args) + object?[] args, RuntimeMethodHandle methodHandle) { var self = GetPyObj(obj); if (null != self.Ref) @@ -736,8 +778,7 @@ public static void InvokeMethodVoid(IPythonDerivedType obj, string methodName, s try { using var pyself = new PyObject(self.CheckRun()); - PyObject method = pyself.GetAttr(methodName, Runtime.None); - disposeList.Add(method); + using PyObject method = pyself.GetAttr(methodName, Runtime.None); if (method.Reference != Runtime.None) { // if the method hasn't been overridden then it will be a managed object @@ -752,7 +793,7 @@ public static void InvokeMethodVoid(IPythonDerivedType obj, string methodName, s } PyObject py_result = method.Invoke(pyargs); - disposeList.Add(py_result); + MarshalByRefsBack(args, methodHandle, py_result, outsOffset: 0); return; } } @@ -779,6 +820,44 @@ public static void InvokeMethodVoid(IPythonDerivedType obj, string methodName, s args); } + /// + /// If the method has byref arguments, reinterprets Python return value + /// as a tuple of new values for those arguments, and updates corresponding + /// elements of array. + /// + private static PyTuple? MarshalByRefsBack(object?[] args, RuntimeMethodHandle methodHandle, PyObject pyResult, int outsOffset) + { + if (methodHandle == default) return null; + + var originalMethod = MethodBase.GetMethodFromHandle(methodHandle); + var parameters = originalMethod.GetParameters(); + PyTuple? outs = null; + int byrefIndex = 0; + for (int i = 0; i < parameters.Length; ++i) + { + Type type = parameters[i].ParameterType; + if (!type.IsByRef) + { + continue; + } + + type = type.GetElementType(); + + if (outs is null) + { + outs = new PyTuple(pyResult); + pyResult.Dispose(); + } + + args[i] = outs[byrefIndex + outsOffset].AsManagedObject(type); + byrefIndex++; + } + if (byrefIndex > 0 && outs!.Length() > byrefIndex + outsOffset) + throw new ArgumentException("Too many output parameters"); + + return outs; + } + public static T? InvokeGetProperty(IPythonDerivedType obj, string propertyName) { var self = GetPyObj(obj); diff --git a/src/runtime/codegenerator.cs b/src/runtime/codegenerator.cs index dc466bafb..d0079fabb 100644 --- a/src/runtime/codegenerator.cs +++ b/src/runtime/codegenerator.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Reflection; using System.Reflection.Emit; using System.Threading; @@ -42,5 +43,39 @@ internal TypeBuilder DefineType(string name, Type basetype) var attrs = TypeAttributes.Public; return mBuilder.DefineType(name, attrs, basetype); } + + /// + /// Generates code, that copies potentially modified objects in args array + /// back to the corresponding byref arguments + /// + internal static void GenerateMarshalByRefsBack(ILGenerator il, IReadOnlyList argTypes) + { + // assumes argument array is in loc_0 + for (int i = 0; i < argTypes.Count; ++i) + { + var type = argTypes[i]; + if (type.IsByRef) + { + type = type.GetElementType(); + + il.Emit(OpCodes.Ldarg, i + 1); // for stobj/stind later at the end + + il.Emit(OpCodes.Ldloc_0); + il.Emit(OpCodes.Ldc_I4, i); + il.Emit(OpCodes.Ldelem_Ref); + + if (type.IsValueType) + { + il.Emit(OpCodes.Unbox_Any, type); + il.Emit(OpCodes.Stobj, type); + } + else + { + il.Emit(OpCodes.Castclass, type); + il.Emit(OpCodes.Stind_Ref); + } + } + } + } } } diff --git a/src/runtime/delegatemanager.cs b/src/runtime/delegatemanager.cs index 24e9d5f0d..092c9be1d 100644 --- a/src/runtime/delegatemanager.cs +++ b/src/runtime/delegatemanager.cs @@ -135,28 +135,7 @@ private Type GetDispatcher(Type dtype) if (anyByRef) { // Dispatch() will have modified elements of the args list that correspond to out parameters. - for (var c = 0; c < signature.Length; c++) - { - Type t = signature[c]; - if (t.IsByRef) - { - t = t.GetElementType(); - // *arg = args[c] - il.Emit(OpCodes.Ldarg_S, (byte)(c + 1)); - il.Emit(OpCodes.Ldloc_0); - il.Emit(OpCodes.Ldc_I4, c); - il.Emit(OpCodes.Ldelem_Ref); - if (t.IsValueType) - { - il.Emit(OpCodes.Unbox_Any, t); - il.Emit(OpCodes.Stobj, t); - } - else - { - il.Emit(OpCodes.Stind_Ref); - } - } - } + CodeGenerator.GenerateMarshalByRefsBack(il, signature); } if (method.ReturnType == voidtype) diff --git a/src/testing/interfacetest.cs b/src/testing/interfacetest.cs index 0158d64da..7c5d937b9 100644 --- a/src/testing/interfacetest.cs +++ b/src/testing/interfacetest.cs @@ -79,4 +79,18 @@ private interface IPrivate { } } + + public interface IOutArg + { + string MyMethod_Out(string name, out int index); + } + + public class OutArgCaller + { + public static int CallMyMethod_Out(IOutArg myInterface) + { + myInterface.MyMethod_Out("myclient", out int index); + return index; + } + } } diff --git a/tests/test_interface.py b/tests/test_interface.py index 130bd71c1..ac620684d 100644 --- a/tests/test_interface.py +++ b/tests/test_interface.py @@ -93,6 +93,20 @@ def test_interface_object_returned_through_out_param(): assert hello2.SayHello() == 'hello 2' +def test_interface_out_param_python_impl(): + from Python.Test import IOutArg, OutArgCaller + + class MyOutImpl(IOutArg): + __namespace__ = "Python.Test" + + def MyMethod_Out(self, name, index): + other_index = 101 + return ('MyName', other_index) + + py_impl = MyOutImpl() + + assert 101 == OutArgCaller.CallMyMethod_Out(py_impl) + def test_null_interface_object_returned(): """Test None is used also for methods with interface return types""" From 7e2ec4d9cfc71a4c54de56707bb3fccc469b5c9b Mon Sep 17 00:00:00 2001 From: Benedikt Reinartz Date: Sun, 9 Jan 2022 01:07:55 +0100 Subject: [PATCH 22/75] Performance tests with baseline from Pypi (#1667) * reenable perf tests for 3.0 * Get baseline dll from pypi fixes https://github.com/pythonnet/pythonnet/issues/1368 Co-authored-by: Victor Nova --- .github/workflows/main.yml | 7 ++++- .gitignore | 1 + .../BaselineComparisonBenchmarkBase.cs | 9 ++++-- src/perf_tests/BaselineComparisonConfig.cs | 3 +- src/perf_tests/BenchmarkTests.cs | 4 +-- src/perf_tests/Python.PerformanceTests.csproj | 30 ++++++++++++------- src/perf_tests/PythonCallingNetBenchmark.cs | 20 ++++++++++--- src/perf_tests/baseline/.gitkeep | 0 8 files changed, 53 insertions(+), 21 deletions(-) create mode 100644 src/perf_tests/baseline/.gitkeep diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 8276be16b..fc9312f58 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -72,5 +72,10 @@ jobs: - name: Python tests run from .NET run: dotnet test --runtime any-${{ matrix.platform }} src/python_tests_runner/ - # TODO: Run perf tests + - name: Perf tests + if: ${{ matrix.python == '3.8' }} + run: | + pip install --force --no-deps --target src/perf_tests/baseline/ pythonnet==2.5.2 + dotnet test --configuration Release --runtime any-${{ matrix.platform }} --logger "console;verbosity=detailed" src/perf_tests/ + # TODO: Run mono tests on Windows? diff --git a/.gitignore b/.gitignore index cdb152157..6159b1b14 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ /src/runtime/interopNative.cs +/src/perf_tests/baseline/ # General binaries and Build results *.dll diff --git a/src/perf_tests/BaselineComparisonBenchmarkBase.cs b/src/perf_tests/BaselineComparisonBenchmarkBase.cs index 2388e3982..06adcbc67 100644 --- a/src/perf_tests/BaselineComparisonBenchmarkBase.cs +++ b/src/perf_tests/BaselineComparisonBenchmarkBase.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using System.Reflection; @@ -17,8 +18,7 @@ public BaselineComparisonBenchmarkBase() try { PythonEngine.Initialize(); Console.WriteLine("Python Initialized"); - if (PythonEngine.BeginAllowThreads() == IntPtr.Zero) - throw new PythonException(); + Trace.Assert(PythonEngine.BeginAllowThreads() != IntPtr.Zero); Console.WriteLine("Threading enabled"); } catch (Exception e) { @@ -28,6 +28,11 @@ public BaselineComparisonBenchmarkBase() } static BaselineComparisonBenchmarkBase() + { + SetupRuntimeResolve(); + } + + public static void SetupRuntimeResolve() { string pythonRuntimeDll = Environment.GetEnvironmentVariable(BaselineComparisonConfig.EnvironmentVariableName); if (string.IsNullOrEmpty(pythonRuntimeDll)) diff --git a/src/perf_tests/BaselineComparisonConfig.cs b/src/perf_tests/BaselineComparisonConfig.cs index 649bb56fd..3f6766554 100644 --- a/src/perf_tests/BaselineComparisonConfig.cs +++ b/src/perf_tests/BaselineComparisonConfig.cs @@ -5,7 +5,8 @@ using BenchmarkDotNet.Configs; using BenchmarkDotNet.Jobs; -using BenchmarkDotNet.Horology; + +using Perfolizer.Horology; namespace Python.PerformanceTests { diff --git a/src/perf_tests/BenchmarkTests.cs b/src/perf_tests/BenchmarkTests.cs index 6e0afca69..9e033d11f 100644 --- a/src/perf_tests/BenchmarkTests.cs +++ b/src/perf_tests/BenchmarkTests.cs @@ -30,14 +30,14 @@ public void SetUp() public void ReadInt64Property() { double optimisticPerfRatio = GetOptimisticPerfRatio(this.summary.Reports); - AssertPerformanceIsBetterOrSame(optimisticPerfRatio, target: 0.57); + AssertPerformanceIsBetterOrSame(optimisticPerfRatio, target: 1.35); } [Test] public void WriteInt64Property() { double optimisticPerfRatio = GetOptimisticPerfRatio(this.summary.Reports); - AssertPerformanceIsBetterOrSame(optimisticPerfRatio, target: 0.57); + AssertPerformanceIsBetterOrSame(optimisticPerfRatio, target: 1.25); } static double GetOptimisticPerfRatio( diff --git a/src/perf_tests/Python.PerformanceTests.csproj b/src/perf_tests/Python.PerformanceTests.csproj index 22783e595..bde07ecab 100644 --- a/src/perf_tests/Python.PerformanceTests.csproj +++ b/src/perf_tests/Python.PerformanceTests.csproj @@ -3,11 +3,25 @@ net472 false - x64;x86 + x64 + x64 + - + + PreserveNewest + + + + + + false + + + + + all runtime; build; native; contentfiles; analyzers; buildtransitive @@ -17,23 +31,17 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - - - compile - + - + - - - - + diff --git a/src/perf_tests/PythonCallingNetBenchmark.cs b/src/perf_tests/PythonCallingNetBenchmark.cs index ef668a911..d7edd4583 100644 --- a/src/perf_tests/PythonCallingNetBenchmark.cs +++ b/src/perf_tests/PythonCallingNetBenchmark.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Reflection; using System.Text; using BenchmarkDotNet.Attributes; @@ -17,11 +18,11 @@ public void ReadInt64Property() { var locals = new PyDict(); locals.SetItem("a", new NetObject().ToPython()); - PythonEngine.Exec($@" + Exec($@" s = 0 for i in range(50000): s += a.{nameof(NetObject.LongProperty)} -", locals: locals.Handle); +", locals: locals); } } @@ -30,13 +31,24 @@ public void WriteInt64Property() { using (Py.GIL()) { var locals = new PyDict(); locals.SetItem("a", new NetObject().ToPython()); - PythonEngine.Exec($@" + Exec($@" s = 0 for i in range(50000): a.{nameof(NetObject.LongProperty)} += i -", locals: locals.Handle); +", locals: locals); } } + + static void Exec(string code, PyDict locals) + { + MethodInfo exec = typeof(PythonEngine).GetMethod(nameof(PythonEngine.Exec)); + object localsArg = typeof(PyObject).Assembly.GetName().Version.Major >= 3 + ? locals : locals.Handle; + exec.Invoke(null, new[] + { + code, localsArg, null + }); + } } class NetObject diff --git a/src/perf_tests/baseline/.gitkeep b/src/perf_tests/baseline/.gitkeep new file mode 100644 index 000000000..e69de29bb From 5cb40290be5a75d2eac1d69873240673750de336 Mon Sep 17 00:00:00 2001 From: Benedikt Reinartz Date: Sat, 8 Jan 2022 15:59:20 +0100 Subject: [PATCH 23/75] Move code to subdirectories and rename or split up --- ...{assemblymanager.cs => AssemblyManager.cs} | 0 .../{classmanager.cs => ClassManager.cs} | 0 src/runtime/Codecs/IPyObjectDecoder.cs | 22 + src/runtime/Codecs/IPyObjectEncoder.cs | 18 + .../PyObjectConversions.cs} | 35 +- src/runtime/{converter.cs => Converter.cs} | 0 ...{delegatemanager.cs => DelegateManager.cs} | 0 src/runtime/{exceptions.cs => Exceptions.cs} | 81 --- src/runtime/{finalizer.cs => Finalizer.cs} | 0 src/runtime/{importhook.cs => ImportHook.cs} | 0 src/runtime/{interfaces.cs => Interfaces.cs} | 0 src/runtime/{intern.cs => InternString.cs} | 0 src/runtime/{interop.cs => Interop.cs} | 0 src/runtime/{loader.cs => Loader.cs} | 0 .../{methodbinder.cs => MethodBinder.cs} | 0 src/runtime/{native => Native}/ABI.cs | 0 src/runtime/{ => Native}/BorrowedReference.cs | 0 src/runtime/{ => Native}/CustomMarshaler.cs | 0 .../GeneratedTypeOffsets.cs | 0 .../{native => Native}/ITypeOffsets.cs | 0 src/runtime/{platform => Native}/LibDL.cs | 0 .../{platform => Native}/LibraryLoader.cs | 0 .../{nativecall.cs => Native/NativeCall.cs} | 0 src/runtime/{native => Native}/NativeFunc.cs | 0 .../{native => Native}/NativeTypeSpec.cs | 0 src/runtime/{ => Native}/NewReference.cs | 0 .../PyBufferInterface.cs} | 0 .../{native => Native}/PyCompilerFlags.cs | 0 src/runtime/{native => Native}/PyGILState.cs | 0 .../{intern_.cs => Native/PyIdentifier_.cs} | 0 .../{intern_.tt => Native/PyIdentifier_.tt} | 0 .../{native => Native}/PyInterpreterState.cs | 0 .../{native => Native}/PyMemberFlags.cs | 0 .../{native => Native}/PyMemberType.cs | 0 .../{native => Native}/PyMethodFlags.cs | 0 .../{native => Native}/PyThreadState.cs | 0 .../{ => Native}/ReferenceExtensions.cs | 0 src/runtime/{ => Native}/StolenReference.cs | 0 src/runtime/{native => Native}/StrPtr.cs | 0 src/runtime/{native => Native}/TypeOffset.cs | 0 .../TypeOffset310.cs} | 0 .../{interop36.cs => Native/TypeOffset36.cs} | 0 .../{interop37.cs => Native/TypeOffset37.cs} | 0 .../{interop38.cs => Native/TypeOffset38.cs} | 0 .../{interop39.cs => Native/TypeOffset39.cs} | 0 src/runtime/Python.Runtime.csproj | 4 +- .../{pythonengine.cs => PythonEngine.cs} | 0 ...{pythonexception.cs => PythonException.cs} | 0 .../{pybuffer.cs => PythonTypes/PyBuffer.cs} | 0 .../{pydict.cs => PythonTypes/PyDict.cs} | 0 .../{pyfloat.cs => PythonTypes/PyFloat.cs} | 0 .../{pyint.cs => PythonTypes/PyInt.cs} | 0 .../{pyiter.cs => PythonTypes/PyIter.cs} | 0 .../PyIterable.cs} | 0 .../{pylist.cs => PythonTypes/PyList.cs} | 0 .../{module.cs => PythonTypes/PyModule.cs} | 0 .../{pynumber.cs => PythonTypes/PyNumber.cs} | 0 .../{pyobject.cs => PythonTypes/PyObject.cs} | 0 .../PySequence.cs} | 0 .../{pystring.cs => PythonTypes/PyString.cs} | 0 .../{pytuple.cs => PythonTypes/PyTuple.cs} | 0 .../{pytype.cs => PythonTypes/PyType.cs} | 0 src/runtime/{ => PythonTypes}/TypeSpec.cs | 0 src/runtime/{resources => Resources}/clr.py | 0 .../{resources => Resources}/interop.py | 0 src/runtime/Runtime.Delegates.cs | 542 +++++++++++++++++ src/runtime/{runtime.cs => Runtime.cs} | 565 +----------------- .../{runtime_state.cs => RuntimeState.cs} | 4 +- .../RuntimeData.cs} | 0 .../{ => StateSerialization}/UnloadedClass.cs | 0 .../{typemanager.cs => TypeManager.cs} | 1 - .../{arrayobject.cs => Types/ArrayObject.cs} | 0 .../{classbase.cs => Types/ClassBase.cs} | 4 +- .../ClassDerived.cs} | 0 .../{classobject.cs => Types/ClassObject.cs} | 0 .../{clrobject.cs => Types/ClrObject.cs} | 0 .../DelegateObject.cs} | 0 .../EventBinding.cs} | 0 .../{eventobject.cs => Types/EventObject.cs} | 0 src/runtime/Types/ExceptionClassObject.cs | 85 +++ .../ExtensionType.cs} | 0 .../{fieldobject.cs => Types/FieldObject.cs} | 0 .../{generictype.cs => Types/GenericType.cs} | 0 src/runtime/{indexer.cs => Types/Indexer.cs} | 0 .../InterfaceObject.cs} | 0 .../{iterator.cs => Types/Iterator.cs} | 0 .../{managedtype.cs => Types/ManagedType.cs} | 0 src/runtime/{ => Types}/ManagedTypes.cd | 0 .../{metatype.cs => Types/MetaType.cs} | 0 .../MethodBinding.cs} | 0 .../MethodObject.cs} | 0 .../ModuleFunctionObject.cs} | 0 .../ModuleObject.cs} | 0 .../ModulePropertyObject.cs} | 0 .../mp_length.cs => Types/MpLengthSlot.cs} | 2 +- .../OperatorMethod.cs} | 0 .../{overload.cs => Types/OverloadMapper.cs} | 0 .../PropertyObject.cs} | 0 src/runtime/{ => Types}/ReflectedClrType.cs | 0 .../{ => Types}/UnsafeReferenceWithRun.cs | 0 .../CodeGenerator.cs} | 0 .../{debughelper.cs => Util/DebugUtil.cs} | 0 .../{ => Util}/EventHandlerCollection.cs | 0 .../{genericutil.cs => Util/GenericUtil.cs} | 0 src/runtime/{tricks => Util}/InitOnly.cs | 0 .../{ => Util}/NonCopyableAttribute.cs | 0 src/runtime/{tricks => Util}/NullOnly.cs | 0 .../{opshelper.cs => Util/OpsHelper.cs} | 0 .../{Reflection => Util}/ParameterHelper.cs | 0 .../{ => Util}/PythonReferenceComparer.cs | 0 .../{polyfill => Util}/ReflectionPolyfills.cs | 0 src/runtime/{ => Util}/ReflectionUtil.cs | 0 src/runtime/{ => Util}/Util.cs | 0 113 files changed, 676 insertions(+), 687 deletions(-) rename src/runtime/{assemblymanager.cs => AssemblyManager.cs} (100%) rename src/runtime/{classmanager.cs => ClassManager.cs} (100%) create mode 100644 src/runtime/Codecs/IPyObjectDecoder.cs create mode 100644 src/runtime/Codecs/IPyObjectEncoder.cs rename src/runtime/{converterextensions.cs => Codecs/PyObjectConversions.cs} (80%) rename src/runtime/{converter.cs => Converter.cs} (100%) rename src/runtime/{delegatemanager.cs => DelegateManager.cs} (100%) rename src/runtime/{exceptions.cs => Exceptions.cs} (84%) rename src/runtime/{finalizer.cs => Finalizer.cs} (100%) rename src/runtime/{importhook.cs => ImportHook.cs} (100%) rename src/runtime/{interfaces.cs => Interfaces.cs} (100%) rename src/runtime/{intern.cs => InternString.cs} (100%) rename src/runtime/{interop.cs => Interop.cs} (100%) rename src/runtime/{loader.cs => Loader.cs} (100%) rename src/runtime/{methodbinder.cs => MethodBinder.cs} (100%) rename src/runtime/{native => Native}/ABI.cs (100%) rename src/runtime/{ => Native}/BorrowedReference.cs (100%) rename src/runtime/{ => Native}/CustomMarshaler.cs (100%) rename src/runtime/{native => Native}/GeneratedTypeOffsets.cs (100%) rename src/runtime/{native => Native}/ITypeOffsets.cs (100%) rename src/runtime/{platform => Native}/LibDL.cs (100%) rename src/runtime/{platform => Native}/LibraryLoader.cs (100%) rename src/runtime/{nativecall.cs => Native/NativeCall.cs} (100%) rename src/runtime/{native => Native}/NativeFunc.cs (100%) rename src/runtime/{native => Native}/NativeTypeSpec.cs (100%) rename src/runtime/{ => Native}/NewReference.cs (100%) rename src/runtime/{bufferinterface.cs => Native/PyBufferInterface.cs} (100%) rename src/runtime/{native => Native}/PyCompilerFlags.cs (100%) rename src/runtime/{native => Native}/PyGILState.cs (100%) rename src/runtime/{intern_.cs => Native/PyIdentifier_.cs} (100%) rename src/runtime/{intern_.tt => Native/PyIdentifier_.tt} (100%) rename src/runtime/{native => Native}/PyInterpreterState.cs (100%) rename src/runtime/{native => Native}/PyMemberFlags.cs (100%) rename src/runtime/{native => Native}/PyMemberType.cs (100%) rename src/runtime/{native => Native}/PyMethodFlags.cs (100%) rename src/runtime/{native => Native}/PyThreadState.cs (100%) rename src/runtime/{ => Native}/ReferenceExtensions.cs (100%) rename src/runtime/{ => Native}/StolenReference.cs (100%) rename src/runtime/{native => Native}/StrPtr.cs (100%) rename src/runtime/{native => Native}/TypeOffset.cs (100%) rename src/runtime/{interop310.cs => Native/TypeOffset310.cs} (100%) rename src/runtime/{interop36.cs => Native/TypeOffset36.cs} (100%) rename src/runtime/{interop37.cs => Native/TypeOffset37.cs} (100%) rename src/runtime/{interop38.cs => Native/TypeOffset38.cs} (100%) rename src/runtime/{interop39.cs => Native/TypeOffset39.cs} (100%) rename src/runtime/{pythonengine.cs => PythonEngine.cs} (100%) rename src/runtime/{pythonexception.cs => PythonException.cs} (100%) rename src/runtime/{pybuffer.cs => PythonTypes/PyBuffer.cs} (100%) rename src/runtime/{pydict.cs => PythonTypes/PyDict.cs} (100%) rename src/runtime/{pyfloat.cs => PythonTypes/PyFloat.cs} (100%) rename src/runtime/{pyint.cs => PythonTypes/PyInt.cs} (100%) rename src/runtime/{pyiter.cs => PythonTypes/PyIter.cs} (100%) rename src/runtime/{pyiterable.cs => PythonTypes/PyIterable.cs} (100%) rename src/runtime/{pylist.cs => PythonTypes/PyList.cs} (100%) rename src/runtime/{module.cs => PythonTypes/PyModule.cs} (100%) rename src/runtime/{pynumber.cs => PythonTypes/PyNumber.cs} (100%) rename src/runtime/{pyobject.cs => PythonTypes/PyObject.cs} (100%) rename src/runtime/{pysequence.cs => PythonTypes/PySequence.cs} (100%) rename src/runtime/{pystring.cs => PythonTypes/PyString.cs} (100%) rename src/runtime/{pytuple.cs => PythonTypes/PyTuple.cs} (100%) rename src/runtime/{pytype.cs => PythonTypes/PyType.cs} (100%) rename src/runtime/{ => PythonTypes}/TypeSpec.cs (100%) rename src/runtime/{resources => Resources}/clr.py (100%) rename src/runtime/{resources => Resources}/interop.py (100%) create mode 100644 src/runtime/Runtime.Delegates.cs rename src/runtime/{runtime.cs => Runtime.cs} (51%) rename src/runtime/{runtime_state.cs => RuntimeState.cs} (96%) rename src/runtime/{runtime_data.cs => StateSerialization/RuntimeData.cs} (100%) rename src/runtime/{ => StateSerialization}/UnloadedClass.cs (100%) rename src/runtime/{typemanager.cs => TypeManager.cs} (99%) rename src/runtime/{arrayobject.cs => Types/ArrayObject.cs} (100%) rename src/runtime/{classbase.cs => Types/ClassBase.cs} (99%) rename src/runtime/{classderived.cs => Types/ClassDerived.cs} (100%) rename src/runtime/{classobject.cs => Types/ClassObject.cs} (100%) rename src/runtime/{clrobject.cs => Types/ClrObject.cs} (100%) rename src/runtime/{delegateobject.cs => Types/DelegateObject.cs} (100%) rename src/runtime/{eventbinding.cs => Types/EventBinding.cs} (100%) rename src/runtime/{eventobject.cs => Types/EventObject.cs} (100%) create mode 100644 src/runtime/Types/ExceptionClassObject.cs rename src/runtime/{extensiontype.cs => Types/ExtensionType.cs} (100%) rename src/runtime/{fieldobject.cs => Types/FieldObject.cs} (100%) rename src/runtime/{generictype.cs => Types/GenericType.cs} (100%) rename src/runtime/{indexer.cs => Types/Indexer.cs} (100%) rename src/runtime/{interfaceobject.cs => Types/InterfaceObject.cs} (100%) rename src/runtime/{iterator.cs => Types/Iterator.cs} (100%) rename src/runtime/{managedtype.cs => Types/ManagedType.cs} (100%) rename src/runtime/{ => Types}/ManagedTypes.cd (100%) rename src/runtime/{metatype.cs => Types/MetaType.cs} (100%) rename src/runtime/{methodbinding.cs => Types/MethodBinding.cs} (100%) rename src/runtime/{methodobject.cs => Types/MethodObject.cs} (100%) rename src/runtime/{modulefunctionobject.cs => Types/ModuleFunctionObject.cs} (100%) rename src/runtime/{moduleobject.cs => Types/ModuleObject.cs} (100%) rename src/runtime/{modulepropertyobject.cs => Types/ModulePropertyObject.cs} (100%) rename src/runtime/{slots/mp_length.cs => Types/MpLengthSlot.cs} (98%) rename src/runtime/{operatormethod.cs => Types/OperatorMethod.cs} (100%) rename src/runtime/{overload.cs => Types/OverloadMapper.cs} (100%) rename src/runtime/{propertyobject.cs => Types/PropertyObject.cs} (100%) rename src/runtime/{ => Types}/ReflectedClrType.cs (100%) rename src/runtime/{ => Types}/UnsafeReferenceWithRun.cs (100%) rename src/runtime/{codegenerator.cs => Util/CodeGenerator.cs} (100%) rename src/runtime/{debughelper.cs => Util/DebugUtil.cs} (100%) rename src/runtime/{ => Util}/EventHandlerCollection.cs (100%) rename src/runtime/{genericutil.cs => Util/GenericUtil.cs} (100%) rename src/runtime/{tricks => Util}/InitOnly.cs (100%) rename src/runtime/{ => Util}/NonCopyableAttribute.cs (100%) rename src/runtime/{tricks => Util}/NullOnly.cs (100%) rename src/runtime/{opshelper.cs => Util/OpsHelper.cs} (100%) rename src/runtime/{Reflection => Util}/ParameterHelper.cs (100%) rename src/runtime/{ => Util}/PythonReferenceComparer.cs (100%) rename src/runtime/{polyfill => Util}/ReflectionPolyfills.cs (100%) rename src/runtime/{ => Util}/ReflectionUtil.cs (100%) rename src/runtime/{ => Util}/Util.cs (100%) diff --git a/src/runtime/assemblymanager.cs b/src/runtime/AssemblyManager.cs similarity index 100% rename from src/runtime/assemblymanager.cs rename to src/runtime/AssemblyManager.cs diff --git a/src/runtime/classmanager.cs b/src/runtime/ClassManager.cs similarity index 100% rename from src/runtime/classmanager.cs rename to src/runtime/ClassManager.cs diff --git a/src/runtime/Codecs/IPyObjectDecoder.cs b/src/runtime/Codecs/IPyObjectDecoder.cs new file mode 100644 index 000000000..a8cd8ff03 --- /dev/null +++ b/src/runtime/Codecs/IPyObjectDecoder.cs @@ -0,0 +1,22 @@ +namespace Python.Runtime; + +using System; + +/// +/// Defines conversion to CLR types (unmarshalling) +/// +public interface IPyObjectDecoder +{ + /// + /// Checks if this decoder can decode from to + /// + bool CanDecode(PyType objectType, Type targetType); + /// + /// Attempts do decode into a variable of specified type + /// + /// CLR type to decode into + /// Object to decode + /// The variable, that will receive decoding result + /// + bool TryDecode(PyObject pyObj, out T? value); +} diff --git a/src/runtime/Codecs/IPyObjectEncoder.cs b/src/runtime/Codecs/IPyObjectEncoder.cs new file mode 100644 index 000000000..94d19da90 --- /dev/null +++ b/src/runtime/Codecs/IPyObjectEncoder.cs @@ -0,0 +1,18 @@ +namespace Python.Runtime; + +using System; + +/// +/// Defines conversion from CLR objects into Python objects (e.g. ) (marshalling) +/// +public interface IPyObjectEncoder +{ + /// + /// Checks if encoder can encode CLR objects of specified type + /// + bool CanEncode(Type type); + /// + /// Attempts to encode CLR object into Python object + /// + PyObject? TryEncode(object value); +} diff --git a/src/runtime/converterextensions.cs b/src/runtime/Codecs/PyObjectConversions.cs similarity index 80% rename from src/runtime/converterextensions.cs rename to src/runtime/Codecs/PyObjectConversions.cs index 9a7ae5403..94ed4cdc3 100644 --- a/src/runtime/converterextensions.cs +++ b/src/runtime/Codecs/PyObjectConversions.cs @@ -6,41 +6,8 @@ namespace Python.Runtime using System.Diagnostics; using System.Linq; using System.Reflection; - using Python.Runtime.Codecs; - - /// - /// Defines conversion to CLR types (unmarshalling) - /// - public interface IPyObjectDecoder - { - /// - /// Checks if this decoder can decode from to - /// - bool CanDecode(PyType objectType, Type targetType); - /// - /// Attempts do decode into a variable of specified type - /// - /// CLR type to decode into - /// Object to decode - /// The variable, that will receive decoding result - /// - bool TryDecode(PyObject pyObj, out T? value); - } - /// - /// Defines conversion from CLR objects into Python objects (e.g. ) (marshalling) - /// - public interface IPyObjectEncoder - { - /// - /// Checks if encoder can encode CLR objects of specified type - /// - bool CanEncode(Type type); - /// - /// Attempts to encode CLR object into Python object - /// - PyObject? TryEncode(object value); - } + using Python.Runtime.Codecs; /// /// This class allows to register additional marshalling codecs. diff --git a/src/runtime/converter.cs b/src/runtime/Converter.cs similarity index 100% rename from src/runtime/converter.cs rename to src/runtime/Converter.cs diff --git a/src/runtime/delegatemanager.cs b/src/runtime/DelegateManager.cs similarity index 100% rename from src/runtime/delegatemanager.cs rename to src/runtime/DelegateManager.cs diff --git a/src/runtime/exceptions.cs b/src/runtime/Exceptions.cs similarity index 84% rename from src/runtime/exceptions.cs rename to src/runtime/Exceptions.cs index 5cf845155..c3ac889ed 100644 --- a/src/runtime/exceptions.cs +++ b/src/runtime/Exceptions.cs @@ -5,87 +5,6 @@ namespace Python.Runtime { - /// - /// Base class for Python types that reflect managed exceptions based on - /// System.Exception - /// - /// - /// The Python wrapper for managed exceptions LIES about its inheritance - /// tree. Although the real System.Exception is a subclass of - /// System.Object the Python type for System.Exception does NOT claim that - /// it subclasses System.Object. Instead TypeManager.CreateType() uses - /// Python's exception.Exception class as base class for System.Exception. - /// - [Serializable] - internal class ExceptionClassObject : ClassObject - { - internal ExceptionClassObject(Type tp) : base(tp) - { - } - - internal static Exception? ToException(BorrowedReference ob) - { - var co = GetManagedObject(ob) as CLRObject; - return co?.inst as Exception; - } - - /// - /// Exception __repr__ implementation - /// - public new static NewReference tp_repr(BorrowedReference ob) - { - Exception? e = ToException(ob); - if (e == null) - { - return Exceptions.RaiseTypeError("invalid object"); - } - string name = e.GetType().Name; - string message; - if (e.Message != String.Empty) - { - message = String.Format("{0}('{1}')", name, e.Message); - } - else - { - message = String.Format("{0}()", name); - } - return Runtime.PyString_FromString(message); - } - - /// - /// Exception __str__ implementation - /// - public new static NewReference tp_str(BorrowedReference ob) - { - Exception? e = ToException(ob); - if (e == null) - { - return Exceptions.RaiseTypeError("invalid object"); - } - - string message = e.ToString(); - string fullTypeName = e.GetType().FullName; - string prefix = fullTypeName + ": "; - if (message.StartsWith(prefix)) - { - message = message.Substring(prefix.Length); - } - else if (message.StartsWith(fullTypeName)) - { - message = message.Substring(fullTypeName.Length); - } - return Runtime.PyString_FromString(message); - } - - public override bool Init(BorrowedReference obj, BorrowedReference args, BorrowedReference kw) - { - if (!base.Init(obj, args, kw)) return false; - - var e = (CLRObject)GetManagedObject(obj)!; - - return Exceptions.SetArgsAndCause(obj, (Exception)e.inst); - } - } /// /// Encapsulates the Python exception APIs. diff --git a/src/runtime/finalizer.cs b/src/runtime/Finalizer.cs similarity index 100% rename from src/runtime/finalizer.cs rename to src/runtime/Finalizer.cs diff --git a/src/runtime/importhook.cs b/src/runtime/ImportHook.cs similarity index 100% rename from src/runtime/importhook.cs rename to src/runtime/ImportHook.cs diff --git a/src/runtime/interfaces.cs b/src/runtime/Interfaces.cs similarity index 100% rename from src/runtime/interfaces.cs rename to src/runtime/Interfaces.cs diff --git a/src/runtime/intern.cs b/src/runtime/InternString.cs similarity index 100% rename from src/runtime/intern.cs rename to src/runtime/InternString.cs diff --git a/src/runtime/interop.cs b/src/runtime/Interop.cs similarity index 100% rename from src/runtime/interop.cs rename to src/runtime/Interop.cs diff --git a/src/runtime/loader.cs b/src/runtime/Loader.cs similarity index 100% rename from src/runtime/loader.cs rename to src/runtime/Loader.cs diff --git a/src/runtime/methodbinder.cs b/src/runtime/MethodBinder.cs similarity index 100% rename from src/runtime/methodbinder.cs rename to src/runtime/MethodBinder.cs diff --git a/src/runtime/native/ABI.cs b/src/runtime/Native/ABI.cs similarity index 100% rename from src/runtime/native/ABI.cs rename to src/runtime/Native/ABI.cs diff --git a/src/runtime/BorrowedReference.cs b/src/runtime/Native/BorrowedReference.cs similarity index 100% rename from src/runtime/BorrowedReference.cs rename to src/runtime/Native/BorrowedReference.cs diff --git a/src/runtime/CustomMarshaler.cs b/src/runtime/Native/CustomMarshaler.cs similarity index 100% rename from src/runtime/CustomMarshaler.cs rename to src/runtime/Native/CustomMarshaler.cs diff --git a/src/runtime/native/GeneratedTypeOffsets.cs b/src/runtime/Native/GeneratedTypeOffsets.cs similarity index 100% rename from src/runtime/native/GeneratedTypeOffsets.cs rename to src/runtime/Native/GeneratedTypeOffsets.cs diff --git a/src/runtime/native/ITypeOffsets.cs b/src/runtime/Native/ITypeOffsets.cs similarity index 100% rename from src/runtime/native/ITypeOffsets.cs rename to src/runtime/Native/ITypeOffsets.cs diff --git a/src/runtime/platform/LibDL.cs b/src/runtime/Native/LibDL.cs similarity index 100% rename from src/runtime/platform/LibDL.cs rename to src/runtime/Native/LibDL.cs diff --git a/src/runtime/platform/LibraryLoader.cs b/src/runtime/Native/LibraryLoader.cs similarity index 100% rename from src/runtime/platform/LibraryLoader.cs rename to src/runtime/Native/LibraryLoader.cs diff --git a/src/runtime/nativecall.cs b/src/runtime/Native/NativeCall.cs similarity index 100% rename from src/runtime/nativecall.cs rename to src/runtime/Native/NativeCall.cs diff --git a/src/runtime/native/NativeFunc.cs b/src/runtime/Native/NativeFunc.cs similarity index 100% rename from src/runtime/native/NativeFunc.cs rename to src/runtime/Native/NativeFunc.cs diff --git a/src/runtime/native/NativeTypeSpec.cs b/src/runtime/Native/NativeTypeSpec.cs similarity index 100% rename from src/runtime/native/NativeTypeSpec.cs rename to src/runtime/Native/NativeTypeSpec.cs diff --git a/src/runtime/NewReference.cs b/src/runtime/Native/NewReference.cs similarity index 100% rename from src/runtime/NewReference.cs rename to src/runtime/Native/NewReference.cs diff --git a/src/runtime/bufferinterface.cs b/src/runtime/Native/PyBufferInterface.cs similarity index 100% rename from src/runtime/bufferinterface.cs rename to src/runtime/Native/PyBufferInterface.cs diff --git a/src/runtime/native/PyCompilerFlags.cs b/src/runtime/Native/PyCompilerFlags.cs similarity index 100% rename from src/runtime/native/PyCompilerFlags.cs rename to src/runtime/Native/PyCompilerFlags.cs diff --git a/src/runtime/native/PyGILState.cs b/src/runtime/Native/PyGILState.cs similarity index 100% rename from src/runtime/native/PyGILState.cs rename to src/runtime/Native/PyGILState.cs diff --git a/src/runtime/intern_.cs b/src/runtime/Native/PyIdentifier_.cs similarity index 100% rename from src/runtime/intern_.cs rename to src/runtime/Native/PyIdentifier_.cs diff --git a/src/runtime/intern_.tt b/src/runtime/Native/PyIdentifier_.tt similarity index 100% rename from src/runtime/intern_.tt rename to src/runtime/Native/PyIdentifier_.tt diff --git a/src/runtime/native/PyInterpreterState.cs b/src/runtime/Native/PyInterpreterState.cs similarity index 100% rename from src/runtime/native/PyInterpreterState.cs rename to src/runtime/Native/PyInterpreterState.cs diff --git a/src/runtime/native/PyMemberFlags.cs b/src/runtime/Native/PyMemberFlags.cs similarity index 100% rename from src/runtime/native/PyMemberFlags.cs rename to src/runtime/Native/PyMemberFlags.cs diff --git a/src/runtime/native/PyMemberType.cs b/src/runtime/Native/PyMemberType.cs similarity index 100% rename from src/runtime/native/PyMemberType.cs rename to src/runtime/Native/PyMemberType.cs diff --git a/src/runtime/native/PyMethodFlags.cs b/src/runtime/Native/PyMethodFlags.cs similarity index 100% rename from src/runtime/native/PyMethodFlags.cs rename to src/runtime/Native/PyMethodFlags.cs diff --git a/src/runtime/native/PyThreadState.cs b/src/runtime/Native/PyThreadState.cs similarity index 100% rename from src/runtime/native/PyThreadState.cs rename to src/runtime/Native/PyThreadState.cs diff --git a/src/runtime/ReferenceExtensions.cs b/src/runtime/Native/ReferenceExtensions.cs similarity index 100% rename from src/runtime/ReferenceExtensions.cs rename to src/runtime/Native/ReferenceExtensions.cs diff --git a/src/runtime/StolenReference.cs b/src/runtime/Native/StolenReference.cs similarity index 100% rename from src/runtime/StolenReference.cs rename to src/runtime/Native/StolenReference.cs diff --git a/src/runtime/native/StrPtr.cs b/src/runtime/Native/StrPtr.cs similarity index 100% rename from src/runtime/native/StrPtr.cs rename to src/runtime/Native/StrPtr.cs diff --git a/src/runtime/native/TypeOffset.cs b/src/runtime/Native/TypeOffset.cs similarity index 100% rename from src/runtime/native/TypeOffset.cs rename to src/runtime/Native/TypeOffset.cs diff --git a/src/runtime/interop310.cs b/src/runtime/Native/TypeOffset310.cs similarity index 100% rename from src/runtime/interop310.cs rename to src/runtime/Native/TypeOffset310.cs diff --git a/src/runtime/interop36.cs b/src/runtime/Native/TypeOffset36.cs similarity index 100% rename from src/runtime/interop36.cs rename to src/runtime/Native/TypeOffset36.cs diff --git a/src/runtime/interop37.cs b/src/runtime/Native/TypeOffset37.cs similarity index 100% rename from src/runtime/interop37.cs rename to src/runtime/Native/TypeOffset37.cs diff --git a/src/runtime/interop38.cs b/src/runtime/Native/TypeOffset38.cs similarity index 100% rename from src/runtime/interop38.cs rename to src/runtime/Native/TypeOffset38.cs diff --git a/src/runtime/interop39.cs b/src/runtime/Native/TypeOffset39.cs similarity index 100% rename from src/runtime/interop39.cs rename to src/runtime/Native/TypeOffset39.cs diff --git a/src/runtime/Python.Runtime.csproj b/src/runtime/Python.Runtime.csproj index a90aa3aaa..fad5b9da8 100644 --- a/src/runtime/Python.Runtime.csproj +++ b/src/runtime/Python.Runtime.csproj @@ -50,10 +50,10 @@ - + clr.py - + interop.py diff --git a/src/runtime/pythonengine.cs b/src/runtime/PythonEngine.cs similarity index 100% rename from src/runtime/pythonengine.cs rename to src/runtime/PythonEngine.cs diff --git a/src/runtime/pythonexception.cs b/src/runtime/PythonException.cs similarity index 100% rename from src/runtime/pythonexception.cs rename to src/runtime/PythonException.cs diff --git a/src/runtime/pybuffer.cs b/src/runtime/PythonTypes/PyBuffer.cs similarity index 100% rename from src/runtime/pybuffer.cs rename to src/runtime/PythonTypes/PyBuffer.cs diff --git a/src/runtime/pydict.cs b/src/runtime/PythonTypes/PyDict.cs similarity index 100% rename from src/runtime/pydict.cs rename to src/runtime/PythonTypes/PyDict.cs diff --git a/src/runtime/pyfloat.cs b/src/runtime/PythonTypes/PyFloat.cs similarity index 100% rename from src/runtime/pyfloat.cs rename to src/runtime/PythonTypes/PyFloat.cs diff --git a/src/runtime/pyint.cs b/src/runtime/PythonTypes/PyInt.cs similarity index 100% rename from src/runtime/pyint.cs rename to src/runtime/PythonTypes/PyInt.cs diff --git a/src/runtime/pyiter.cs b/src/runtime/PythonTypes/PyIter.cs similarity index 100% rename from src/runtime/pyiter.cs rename to src/runtime/PythonTypes/PyIter.cs diff --git a/src/runtime/pyiterable.cs b/src/runtime/PythonTypes/PyIterable.cs similarity index 100% rename from src/runtime/pyiterable.cs rename to src/runtime/PythonTypes/PyIterable.cs diff --git a/src/runtime/pylist.cs b/src/runtime/PythonTypes/PyList.cs similarity index 100% rename from src/runtime/pylist.cs rename to src/runtime/PythonTypes/PyList.cs diff --git a/src/runtime/module.cs b/src/runtime/PythonTypes/PyModule.cs similarity index 100% rename from src/runtime/module.cs rename to src/runtime/PythonTypes/PyModule.cs diff --git a/src/runtime/pynumber.cs b/src/runtime/PythonTypes/PyNumber.cs similarity index 100% rename from src/runtime/pynumber.cs rename to src/runtime/PythonTypes/PyNumber.cs diff --git a/src/runtime/pyobject.cs b/src/runtime/PythonTypes/PyObject.cs similarity index 100% rename from src/runtime/pyobject.cs rename to src/runtime/PythonTypes/PyObject.cs diff --git a/src/runtime/pysequence.cs b/src/runtime/PythonTypes/PySequence.cs similarity index 100% rename from src/runtime/pysequence.cs rename to src/runtime/PythonTypes/PySequence.cs diff --git a/src/runtime/pystring.cs b/src/runtime/PythonTypes/PyString.cs similarity index 100% rename from src/runtime/pystring.cs rename to src/runtime/PythonTypes/PyString.cs diff --git a/src/runtime/pytuple.cs b/src/runtime/PythonTypes/PyTuple.cs similarity index 100% rename from src/runtime/pytuple.cs rename to src/runtime/PythonTypes/PyTuple.cs diff --git a/src/runtime/pytype.cs b/src/runtime/PythonTypes/PyType.cs similarity index 100% rename from src/runtime/pytype.cs rename to src/runtime/PythonTypes/PyType.cs diff --git a/src/runtime/TypeSpec.cs b/src/runtime/PythonTypes/TypeSpec.cs similarity index 100% rename from src/runtime/TypeSpec.cs rename to src/runtime/PythonTypes/TypeSpec.cs diff --git a/src/runtime/resources/clr.py b/src/runtime/Resources/clr.py similarity index 100% rename from src/runtime/resources/clr.py rename to src/runtime/Resources/clr.py diff --git a/src/runtime/resources/interop.py b/src/runtime/Resources/interop.py similarity index 100% rename from src/runtime/resources/interop.py rename to src/runtime/Resources/interop.py diff --git a/src/runtime/Runtime.Delegates.cs b/src/runtime/Runtime.Delegates.cs new file mode 100644 index 000000000..6388bde9f --- /dev/null +++ b/src/runtime/Runtime.Delegates.cs @@ -0,0 +1,542 @@ +using System; + +using Python.Runtime.Native; +using Python.Runtime.Platform; + +namespace Python.Runtime; + +public unsafe partial class Runtime +{ + internal static class Delegates + { + static readonly ILibraryLoader libraryLoader = LibraryLoader.Instance; + + static Delegates() + { + Py_IncRef = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(Py_IncRef), GetUnmanagedDll(_PythonDll)); + Py_DecRef = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(Py_DecRef), GetUnmanagedDll(_PythonDll)); + Py_Initialize = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(Py_Initialize), GetUnmanagedDll(_PythonDll)); + Py_InitializeEx = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(Py_InitializeEx), GetUnmanagedDll(_PythonDll)); + Py_IsInitialized = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(Py_IsInitialized), GetUnmanagedDll(_PythonDll)); + Py_Finalize = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(Py_Finalize), GetUnmanagedDll(_PythonDll)); + Py_NewInterpreter = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(Py_NewInterpreter), GetUnmanagedDll(_PythonDll)); + Py_EndInterpreter = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(Py_EndInterpreter), GetUnmanagedDll(_PythonDll)); + PyThreadState_New = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyThreadState_New), GetUnmanagedDll(_PythonDll)); + PyThreadState_Get = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyThreadState_Get), GetUnmanagedDll(_PythonDll)); + _PyThreadState_UncheckedGet = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(_PyThreadState_UncheckedGet), GetUnmanagedDll(_PythonDll)); + try + { + PyGILState_Check = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyGILState_Check), GetUnmanagedDll(_PythonDll)); + } + catch (MissingMethodException e) + { + throw new NotSupportedException(Util.MinimalPythonVersionRequired, innerException: e); + } + PyGILState_Ensure = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyGILState_Ensure), GetUnmanagedDll(_PythonDll)); + PyGILState_Release = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyGILState_Release), GetUnmanagedDll(_PythonDll)); + PyGILState_GetThisThreadState = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyGILState_GetThisThreadState), GetUnmanagedDll(_PythonDll)); + Py_Main = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(Py_Main), GetUnmanagedDll(_PythonDll)); + PyEval_InitThreads = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyEval_InitThreads), GetUnmanagedDll(_PythonDll)); + PyEval_ThreadsInitialized = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyEval_ThreadsInitialized), GetUnmanagedDll(_PythonDll)); + PyEval_AcquireLock = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyEval_AcquireLock), GetUnmanagedDll(_PythonDll)); + PyEval_ReleaseLock = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyEval_ReleaseLock), GetUnmanagedDll(_PythonDll)); + PyEval_AcquireThread = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyEval_AcquireThread), GetUnmanagedDll(_PythonDll)); + PyEval_ReleaseThread = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyEval_ReleaseThread), GetUnmanagedDll(_PythonDll)); + PyEval_SaveThread = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyEval_SaveThread), GetUnmanagedDll(_PythonDll)); + PyEval_RestoreThread = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyEval_RestoreThread), GetUnmanagedDll(_PythonDll)); + PyEval_GetBuiltins = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyEval_GetBuiltins), GetUnmanagedDll(_PythonDll)); + PyEval_GetGlobals = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyEval_GetGlobals), GetUnmanagedDll(_PythonDll)); + PyEval_GetLocals = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyEval_GetLocals), GetUnmanagedDll(_PythonDll)); + Py_GetProgramName = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(Py_GetProgramName), GetUnmanagedDll(_PythonDll)); + Py_SetProgramName = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(Py_SetProgramName), GetUnmanagedDll(_PythonDll)); + Py_GetPythonHome = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(Py_GetPythonHome), GetUnmanagedDll(_PythonDll)); + Py_SetPythonHome = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(Py_SetPythonHome), GetUnmanagedDll(_PythonDll)); + Py_GetPath = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(Py_GetPath), GetUnmanagedDll(_PythonDll)); + Py_SetPath = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(Py_SetPath), GetUnmanagedDll(_PythonDll)); + Py_GetVersion = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(Py_GetVersion), GetUnmanagedDll(_PythonDll)); + Py_GetPlatform = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(Py_GetPlatform), GetUnmanagedDll(_PythonDll)); + Py_GetCopyright = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(Py_GetCopyright), GetUnmanagedDll(_PythonDll)); + Py_GetCompiler = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(Py_GetCompiler), GetUnmanagedDll(_PythonDll)); + Py_GetBuildInfo = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(Py_GetBuildInfo), GetUnmanagedDll(_PythonDll)); + PyRun_SimpleStringFlags = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyRun_SimpleStringFlags), GetUnmanagedDll(_PythonDll)); + PyRun_StringFlags = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyRun_StringFlags), GetUnmanagedDll(_PythonDll)); + PyEval_EvalCode = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyEval_EvalCode), GetUnmanagedDll(_PythonDll)); + Py_CompileStringObject = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(Py_CompileStringObject), GetUnmanagedDll(_PythonDll)); + PyImport_ExecCodeModule = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyImport_ExecCodeModule), GetUnmanagedDll(_PythonDll)); + PyObject_HasAttrString = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyObject_HasAttrString), GetUnmanagedDll(_PythonDll)); + PyObject_GetAttrString = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyObject_GetAttrString), GetUnmanagedDll(_PythonDll)); + PyObject_SetAttrString = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyObject_SetAttrString), GetUnmanagedDll(_PythonDll)); + PyObject_HasAttr = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyObject_HasAttr), GetUnmanagedDll(_PythonDll)); + PyObject_GetAttr = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyObject_GetAttr), GetUnmanagedDll(_PythonDll)); + PyObject_SetAttr = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyObject_SetAttr), GetUnmanagedDll(_PythonDll)); + PyObject_GetItem = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyObject_GetItem), GetUnmanagedDll(_PythonDll)); + PyObject_SetItem = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyObject_SetItem), GetUnmanagedDll(_PythonDll)); + PyObject_DelItem = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyObject_DelItem), GetUnmanagedDll(_PythonDll)); + PyObject_GetIter = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyObject_GetIter), GetUnmanagedDll(_PythonDll)); + PyObject_Call = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyObject_Call), GetUnmanagedDll(_PythonDll)); + PyObject_CallObject = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyObject_CallObject), GetUnmanagedDll(_PythonDll)); + PyObject_RichCompareBool = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyObject_RichCompareBool), GetUnmanagedDll(_PythonDll)); + PyObject_IsInstance = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyObject_IsInstance), GetUnmanagedDll(_PythonDll)); + PyObject_IsSubclass = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyObject_IsSubclass), GetUnmanagedDll(_PythonDll)); + PyCallable_Check = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyCallable_Check), GetUnmanagedDll(_PythonDll)); + PyObject_IsTrue = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyObject_IsTrue), GetUnmanagedDll(_PythonDll)); + PyObject_Not = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyObject_Not), GetUnmanagedDll(_PythonDll)); + PyObject_Size = (delegate* unmanaged[Cdecl])GetFunctionByName("PyObject_Size", GetUnmanagedDll(_PythonDll)); + PyObject_Hash = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyObject_Hash), GetUnmanagedDll(_PythonDll)); + PyObject_Repr = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyObject_Repr), GetUnmanagedDll(_PythonDll)); + PyObject_Str = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyObject_Str), GetUnmanagedDll(_PythonDll)); + PyObject_Type = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyObject_Type), GetUnmanagedDll(_PythonDll)); + PyObject_Dir = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyObject_Dir), GetUnmanagedDll(_PythonDll)); + PyObject_GetBuffer = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyObject_GetBuffer), GetUnmanagedDll(_PythonDll)); + PyBuffer_Release = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyBuffer_Release), GetUnmanagedDll(_PythonDll)); + try + { + PyBuffer_SizeFromFormat = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyBuffer_SizeFromFormat), GetUnmanagedDll(_PythonDll)); + } + catch (MissingMethodException) + { + // only in 3.9+ + } + PyBuffer_IsContiguous = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyBuffer_IsContiguous), GetUnmanagedDll(_PythonDll)); + PyBuffer_GetPointer = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyBuffer_GetPointer), GetUnmanagedDll(_PythonDll)); + PyBuffer_FromContiguous = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyBuffer_FromContiguous), GetUnmanagedDll(_PythonDll)); + PyBuffer_ToContiguous = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyBuffer_ToContiguous), GetUnmanagedDll(_PythonDll)); + PyBuffer_FillContiguousStrides = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyBuffer_FillContiguousStrides), GetUnmanagedDll(_PythonDll)); + PyBuffer_FillInfo = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyBuffer_FillInfo), GetUnmanagedDll(_PythonDll)); + PyNumber_Long = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyNumber_Long), GetUnmanagedDll(_PythonDll)); + PyNumber_Float = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyNumber_Float), GetUnmanagedDll(_PythonDll)); + PyNumber_Check = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyNumber_Check), GetUnmanagedDll(_PythonDll)); + PyLong_FromLongLong = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyLong_FromLongLong), GetUnmanagedDll(_PythonDll)); + PyLong_FromUnsignedLongLong = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyLong_FromUnsignedLongLong), GetUnmanagedDll(_PythonDll)); + PyLong_FromString = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyLong_FromString), GetUnmanagedDll(_PythonDll)); + PyLong_AsLongLong = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyLong_AsLongLong), GetUnmanagedDll(_PythonDll)); + PyLong_AsUnsignedLongLong = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyLong_AsUnsignedLongLong), GetUnmanagedDll(_PythonDll)); + PyLong_FromVoidPtr = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyLong_FromVoidPtr), GetUnmanagedDll(_PythonDll)); + PyLong_AsVoidPtr = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyLong_AsVoidPtr), GetUnmanagedDll(_PythonDll)); + PyFloat_FromDouble = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyFloat_FromDouble), GetUnmanagedDll(_PythonDll)); + PyFloat_FromString = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyFloat_FromString), GetUnmanagedDll(_PythonDll)); + PyFloat_AsDouble = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyFloat_AsDouble), GetUnmanagedDll(_PythonDll)); + PyNumber_Add = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyNumber_Add), GetUnmanagedDll(_PythonDll)); + PyNumber_Subtract = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyNumber_Subtract), GetUnmanagedDll(_PythonDll)); + PyNumber_Multiply = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyNumber_Multiply), GetUnmanagedDll(_PythonDll)); + PyNumber_TrueDivide = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyNumber_TrueDivide), GetUnmanagedDll(_PythonDll)); + PyNumber_And = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyNumber_And), GetUnmanagedDll(_PythonDll)); + PyNumber_Xor = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyNumber_Xor), GetUnmanagedDll(_PythonDll)); + PyNumber_Or = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyNumber_Or), GetUnmanagedDll(_PythonDll)); + PyNumber_Lshift = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyNumber_Lshift), GetUnmanagedDll(_PythonDll)); + PyNumber_Rshift = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyNumber_Rshift), GetUnmanagedDll(_PythonDll)); + PyNumber_Power = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyNumber_Power), GetUnmanagedDll(_PythonDll)); + PyNumber_Remainder = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyNumber_Remainder), GetUnmanagedDll(_PythonDll)); + PyNumber_InPlaceAdd = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyNumber_InPlaceAdd), GetUnmanagedDll(_PythonDll)); + PyNumber_InPlaceSubtract = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyNumber_InPlaceSubtract), GetUnmanagedDll(_PythonDll)); + PyNumber_InPlaceMultiply = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyNumber_InPlaceMultiply), GetUnmanagedDll(_PythonDll)); + PyNumber_InPlaceTrueDivide = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyNumber_InPlaceTrueDivide), GetUnmanagedDll(_PythonDll)); + PyNumber_InPlaceAnd = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyNumber_InPlaceAnd), GetUnmanagedDll(_PythonDll)); + PyNumber_InPlaceXor = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyNumber_InPlaceXor), GetUnmanagedDll(_PythonDll)); + PyNumber_InPlaceOr = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyNumber_InPlaceOr), GetUnmanagedDll(_PythonDll)); + PyNumber_InPlaceLshift = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyNumber_InPlaceLshift), GetUnmanagedDll(_PythonDll)); + PyNumber_InPlaceRshift = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyNumber_InPlaceRshift), GetUnmanagedDll(_PythonDll)); + PyNumber_InPlacePower = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyNumber_InPlacePower), GetUnmanagedDll(_PythonDll)); + PyNumber_InPlaceRemainder = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyNumber_InPlaceRemainder), GetUnmanagedDll(_PythonDll)); + PyNumber_Negative = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyNumber_Negative), GetUnmanagedDll(_PythonDll)); + PyNumber_Positive = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyNumber_Positive), GetUnmanagedDll(_PythonDll)); + PyNumber_Invert = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyNumber_Invert), GetUnmanagedDll(_PythonDll)); + PySequence_Check = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PySequence_Check), GetUnmanagedDll(_PythonDll)); + PySequence_GetItem = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PySequence_GetItem), GetUnmanagedDll(_PythonDll)); + PySequence_SetItem = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PySequence_SetItem), GetUnmanagedDll(_PythonDll)); + PySequence_DelItem = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PySequence_DelItem), GetUnmanagedDll(_PythonDll)); + PySequence_GetSlice = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PySequence_GetSlice), GetUnmanagedDll(_PythonDll)); + PySequence_SetSlice = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PySequence_SetSlice), GetUnmanagedDll(_PythonDll)); + PySequence_DelSlice = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PySequence_DelSlice), GetUnmanagedDll(_PythonDll)); + PySequence_Size = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PySequence_Size), GetUnmanagedDll(_PythonDll)); + PySequence_Contains = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PySequence_Contains), GetUnmanagedDll(_PythonDll)); + PySequence_Concat = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PySequence_Concat), GetUnmanagedDll(_PythonDll)); + PySequence_Repeat = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PySequence_Repeat), GetUnmanagedDll(_PythonDll)); + PySequence_Index = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PySequence_Index), GetUnmanagedDll(_PythonDll)); + PySequence_Count = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PySequence_Count), GetUnmanagedDll(_PythonDll)); + PySequence_Tuple = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PySequence_Tuple), GetUnmanagedDll(_PythonDll)); + PySequence_List = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PySequence_List), GetUnmanagedDll(_PythonDll)); + PyBytes_AsString = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyBytes_AsString), GetUnmanagedDll(_PythonDll)); + PyBytes_FromString = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyBytes_FromString), GetUnmanagedDll(_PythonDll)); + PyByteArray_FromStringAndSize = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyByteArray_FromStringAndSize), GetUnmanagedDll(_PythonDll)); + PyBytes_Size = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyBytes_Size), GetUnmanagedDll(_PythonDll)); + PyUnicode_AsUTF8 = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyUnicode_AsUTF8), GetUnmanagedDll(_PythonDll)); + PyUnicode_DecodeUTF16 = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyUnicode_DecodeUTF16), GetUnmanagedDll(_PythonDll)); + PyUnicode_GetLength = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyUnicode_GetLength), GetUnmanagedDll(_PythonDll)); + PyUnicode_AsUnicode = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyUnicode_AsUnicode), GetUnmanagedDll(_PythonDll)); + PyUnicode_AsUTF16String = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyUnicode_AsUTF16String), GetUnmanagedDll(_PythonDll)); + PyUnicode_FromOrdinal = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyUnicode_FromOrdinal), GetUnmanagedDll(_PythonDll)); + PyUnicode_InternFromString = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyUnicode_InternFromString), GetUnmanagedDll(_PythonDll)); + PyUnicode_Compare = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyUnicode_Compare), GetUnmanagedDll(_PythonDll)); + PyDict_New = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyDict_New), GetUnmanagedDll(_PythonDll)); + PyDict_GetItem = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyDict_GetItem), GetUnmanagedDll(_PythonDll)); + PyDict_GetItemString = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyDict_GetItemString), GetUnmanagedDll(_PythonDll)); + PyDict_SetItem = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyDict_SetItem), GetUnmanagedDll(_PythonDll)); + PyDict_SetItemString = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyDict_SetItemString), GetUnmanagedDll(_PythonDll)); + PyDict_DelItem = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyDict_DelItem), GetUnmanagedDll(_PythonDll)); + PyDict_DelItemString = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyDict_DelItemString), GetUnmanagedDll(_PythonDll)); + PyMapping_HasKey = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyMapping_HasKey), GetUnmanagedDll(_PythonDll)); + PyDict_Keys = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyDict_Keys), GetUnmanagedDll(_PythonDll)); + PyDict_Values = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyDict_Values), GetUnmanagedDll(_PythonDll)); + PyDict_Items = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyDict_Items), GetUnmanagedDll(_PythonDll)); + PyDict_Copy = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyDict_Copy), GetUnmanagedDll(_PythonDll)); + PyDict_Update = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyDict_Update), GetUnmanagedDll(_PythonDll)); + PyDict_Clear = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyDict_Clear), GetUnmanagedDll(_PythonDll)); + PyDict_Size = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyDict_Size), GetUnmanagedDll(_PythonDll)); + PySet_New = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PySet_New), GetUnmanagedDll(_PythonDll)); + PySet_Add = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PySet_Add), GetUnmanagedDll(_PythonDll)); + PySet_Contains = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PySet_Contains), GetUnmanagedDll(_PythonDll)); + PyList_New = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyList_New), GetUnmanagedDll(_PythonDll)); + PyList_GetItem = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyList_GetItem), GetUnmanagedDll(_PythonDll)); + PyList_SetItem = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyList_SetItem), GetUnmanagedDll(_PythonDll)); + PyList_Insert = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyList_Insert), GetUnmanagedDll(_PythonDll)); + PyList_Append = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyList_Append), GetUnmanagedDll(_PythonDll)); + PyList_Reverse = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyList_Reverse), GetUnmanagedDll(_PythonDll)); + PyList_Sort = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyList_Sort), GetUnmanagedDll(_PythonDll)); + PyList_GetSlice = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyList_GetSlice), GetUnmanagedDll(_PythonDll)); + PyList_SetSlice = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyList_SetSlice), GetUnmanagedDll(_PythonDll)); + PyList_Size = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyList_Size), GetUnmanagedDll(_PythonDll)); + PyTuple_New = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyTuple_New), GetUnmanagedDll(_PythonDll)); + PyTuple_GetItem = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyTuple_GetItem), GetUnmanagedDll(_PythonDll)); + PyTuple_SetItem = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyTuple_SetItem), GetUnmanagedDll(_PythonDll)); + PyTuple_GetSlice = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyTuple_GetSlice), GetUnmanagedDll(_PythonDll)); + PyTuple_Size = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyTuple_Size), GetUnmanagedDll(_PythonDll)); + try + { + PyIter_Check = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyIter_Check), GetUnmanagedDll(_PythonDll)); + } + catch (MissingMethodException) { } + PyIter_Next = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyIter_Next), GetUnmanagedDll(_PythonDll)); + PyModule_New = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyModule_New), GetUnmanagedDll(_PythonDll)); + PyModule_GetDict = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyModule_GetDict), GetUnmanagedDll(_PythonDll)); + PyModule_AddObject = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyModule_AddObject), GetUnmanagedDll(_PythonDll)); + PyImport_Import = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyImport_Import), GetUnmanagedDll(_PythonDll)); + PyImport_ImportModule = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyImport_ImportModule), GetUnmanagedDll(_PythonDll)); + PyImport_ReloadModule = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyImport_ReloadModule), GetUnmanagedDll(_PythonDll)); + PyImport_AddModule = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyImport_AddModule), GetUnmanagedDll(_PythonDll)); + PyImport_GetModuleDict = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyImport_GetModuleDict), GetUnmanagedDll(_PythonDll)); + PySys_SetArgvEx = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PySys_SetArgvEx), GetUnmanagedDll(_PythonDll)); + PySys_GetObject = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PySys_GetObject), GetUnmanagedDll(_PythonDll)); + PySys_SetObject = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PySys_SetObject), GetUnmanagedDll(_PythonDll)); + PyType_Modified = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyType_Modified), GetUnmanagedDll(_PythonDll)); + PyType_IsSubtype = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyType_IsSubtype), GetUnmanagedDll(_PythonDll)); + PyType_GenericNew = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyType_GenericNew), GetUnmanagedDll(_PythonDll)); + PyType_GenericAlloc = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyType_GenericAlloc), GetUnmanagedDll(_PythonDll)); + PyType_Ready = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyType_Ready), GetUnmanagedDll(_PythonDll)); + _PyType_Lookup = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(_PyType_Lookup), GetUnmanagedDll(_PythonDll)); + PyObject_GenericGetAttr = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyObject_GenericGetAttr), GetUnmanagedDll(_PythonDll)); + PyObject_GenericGetDict = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyObject_GenericGetDict), GetUnmanagedDll(PythonDLL)); + PyObject_GenericSetAttr = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyObject_GenericSetAttr), GetUnmanagedDll(_PythonDll)); + PyObject_GC_Del = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyObject_GC_Del), GetUnmanagedDll(_PythonDll)); + try + { + PyObject_GC_IsTracked = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyObject_GC_IsTracked), GetUnmanagedDll(_PythonDll)); + } + catch (MissingMethodException) { } + PyObject_GC_Track = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyObject_GC_Track), GetUnmanagedDll(_PythonDll)); + PyObject_GC_UnTrack = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyObject_GC_UnTrack), GetUnmanagedDll(_PythonDll)); + _PyObject_Dump = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(_PyObject_Dump), GetUnmanagedDll(_PythonDll)); + PyMem_Malloc = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyMem_Malloc), GetUnmanagedDll(_PythonDll)); + PyMem_Realloc = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyMem_Realloc), GetUnmanagedDll(_PythonDll)); + PyMem_Free = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyMem_Free), GetUnmanagedDll(_PythonDll)); + PyErr_SetString = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyErr_SetString), GetUnmanagedDll(_PythonDll)); + PyErr_SetObject = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyErr_SetObject), GetUnmanagedDll(_PythonDll)); + PyErr_ExceptionMatches = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyErr_ExceptionMatches), GetUnmanagedDll(_PythonDll)); + PyErr_GivenExceptionMatches = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyErr_GivenExceptionMatches), GetUnmanagedDll(_PythonDll)); + PyErr_NormalizeException = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyErr_NormalizeException), GetUnmanagedDll(_PythonDll)); + PyErr_Occurred = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyErr_Occurred), GetUnmanagedDll(_PythonDll)); + PyErr_Fetch = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyErr_Fetch), GetUnmanagedDll(_PythonDll)); + PyErr_Restore = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyErr_Restore), GetUnmanagedDll(_PythonDll)); + PyErr_Clear = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyErr_Clear), GetUnmanagedDll(_PythonDll)); + PyErr_Print = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyErr_Print), GetUnmanagedDll(_PythonDll)); + PyCell_Get = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyCell_Get), GetUnmanagedDll(_PythonDll)); + PyCell_Set = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyCell_Set), GetUnmanagedDll(_PythonDll)); + PyGC_Collect = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyGC_Collect), GetUnmanagedDll(_PythonDll)); + PyCapsule_New = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyCapsule_New), GetUnmanagedDll(_PythonDll)); + PyCapsule_GetPointer = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyCapsule_GetPointer), GetUnmanagedDll(_PythonDll)); + PyCapsule_SetPointer = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyCapsule_SetPointer), GetUnmanagedDll(_PythonDll)); + PyLong_AsUnsignedSize_t = (delegate* unmanaged[Cdecl])GetFunctionByName("PyLong_AsSize_t", GetUnmanagedDll(_PythonDll)); + PyLong_AsSignedSize_t = (delegate* unmanaged[Cdecl])GetFunctionByName("PyLong_AsSsize_t", GetUnmanagedDll(_PythonDll)); + PyDict_GetItemWithError = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyDict_GetItemWithError), GetUnmanagedDll(_PythonDll)); + PyException_GetCause = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyException_GetCause), GetUnmanagedDll(_PythonDll)); + PyException_GetTraceback = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyException_GetTraceback), GetUnmanagedDll(_PythonDll)); + PyException_SetCause = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyException_SetCause), GetUnmanagedDll(_PythonDll)); + PyException_SetTraceback = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyException_SetTraceback), GetUnmanagedDll(_PythonDll)); + PyThreadState_SetAsyncExcLLP64 = (delegate* unmanaged[Cdecl])GetFunctionByName("PyThreadState_SetAsyncExc", GetUnmanagedDll(_PythonDll)); + PyThreadState_SetAsyncExcLP64 = (delegate* unmanaged[Cdecl])GetFunctionByName("PyThreadState_SetAsyncExc", GetUnmanagedDll(_PythonDll)); + PyType_GetSlot = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyType_GetSlot), GetUnmanagedDll(_PythonDll)); + PyType_FromSpecWithBases = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyType_FromSpecWithBases), GetUnmanagedDll(PythonDLL)); + + try + { + _Py_NewReference = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(_Py_NewReference), GetUnmanagedDll(_PythonDll)); + } + catch (MissingMethodException) { } + try + { + _Py_IsFinalizing = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(_Py_IsFinalizing), GetUnmanagedDll(_PythonDll)); + } + catch (MissingMethodException) { } + + PyType_Type = GetFunctionByName(nameof(PyType_Type), GetUnmanagedDll(_PythonDll)); + Py_NoSiteFlag = (int*)GetFunctionByName(nameof(Py_NoSiteFlag), GetUnmanagedDll(_PythonDll)); + } + + static global::System.IntPtr GetUnmanagedDll(string? libraryName) + { + if (libraryName is null) return IntPtr.Zero; + return libraryLoader.Load(libraryName); + } + + static global::System.IntPtr GetFunctionByName(string functionName, global::System.IntPtr libraryHandle) + { + try + { + return libraryLoader.GetFunction(libraryHandle, functionName); + } + catch (MissingMethodException e) when (libraryHandle == IntPtr.Zero) + { + throw new BadPythonDllException( + "Runtime.PythonDLL was not set or does not point to a supported Python runtime DLL." + + " See https://github.com/pythonnet/pythonnet#embedding-python-in-net", + e); + } + } + + internal static delegate* unmanaged[Cdecl] Py_IncRef { get; } + internal static delegate* unmanaged[Cdecl] Py_DecRef { get; } + internal static delegate* unmanaged[Cdecl] Py_Initialize { get; } + internal static delegate* unmanaged[Cdecl] Py_InitializeEx { get; } + internal static delegate* unmanaged[Cdecl] Py_IsInitialized { get; } + internal static delegate* unmanaged[Cdecl] Py_Finalize { get; } + internal static delegate* unmanaged[Cdecl] Py_NewInterpreter { get; } + internal static delegate* unmanaged[Cdecl] Py_EndInterpreter { get; } + internal static delegate* unmanaged[Cdecl] PyThreadState_New { get; } + internal static delegate* unmanaged[Cdecl] PyThreadState_Get { get; } + internal static delegate* unmanaged[Cdecl] _PyThreadState_UncheckedGet { get; } + internal static delegate* unmanaged[Cdecl] PyGILState_Check { get; } + internal static delegate* unmanaged[Cdecl] PyGILState_Ensure { get; } + internal static delegate* unmanaged[Cdecl] PyGILState_Release { get; } + internal static delegate* unmanaged[Cdecl] PyGILState_GetThisThreadState { get; } + internal static delegate* unmanaged[Cdecl] Py_Main { get; } + internal static delegate* unmanaged[Cdecl] PyEval_InitThreads { get; } + internal static delegate* unmanaged[Cdecl] PyEval_ThreadsInitialized { get; } + internal static delegate* unmanaged[Cdecl] PyEval_AcquireLock { get; } + internal static delegate* unmanaged[Cdecl] PyEval_ReleaseLock { get; } + internal static delegate* unmanaged[Cdecl] PyEval_AcquireThread { get; } + internal static delegate* unmanaged[Cdecl] PyEval_ReleaseThread { get; } + internal static delegate* unmanaged[Cdecl] PyEval_SaveThread { get; } + internal static delegate* unmanaged[Cdecl] PyEval_RestoreThread { get; } + internal static delegate* unmanaged[Cdecl] PyEval_GetBuiltins { get; } + internal static delegate* unmanaged[Cdecl] PyEval_GetGlobals { get; } + internal static delegate* unmanaged[Cdecl] PyEval_GetLocals { get; } + internal static delegate* unmanaged[Cdecl] Py_GetProgramName { get; } + internal static delegate* unmanaged[Cdecl] Py_SetProgramName { get; } + internal static delegate* unmanaged[Cdecl] Py_GetPythonHome { get; } + internal static delegate* unmanaged[Cdecl] Py_SetPythonHome { get; } + internal static delegate* unmanaged[Cdecl] Py_GetPath { get; } + internal static delegate* unmanaged[Cdecl] Py_SetPath { get; } + internal static delegate* unmanaged[Cdecl] Py_GetVersion { get; } + internal static delegate* unmanaged[Cdecl] Py_GetPlatform { get; } + internal static delegate* unmanaged[Cdecl] Py_GetCopyright { get; } + internal static delegate* unmanaged[Cdecl] Py_GetCompiler { get; } + internal static delegate* unmanaged[Cdecl] Py_GetBuildInfo { get; } + internal static delegate* unmanaged[Cdecl] PyRun_SimpleStringFlags { get; } + internal static delegate* unmanaged[Cdecl] PyRun_StringFlags { get; } + internal static delegate* unmanaged[Cdecl] PyEval_EvalCode { get; } + internal static delegate* unmanaged[Cdecl] Py_CompileStringObject { get; } + internal static delegate* unmanaged[Cdecl] PyImport_ExecCodeModule { get; } + internal static delegate* unmanaged[Cdecl] PyObject_HasAttrString { get; } + internal static delegate* unmanaged[Cdecl] PyObject_GetAttrString { get; } + internal static delegate* unmanaged[Cdecl] PyObject_SetAttrString { get; } + internal static delegate* unmanaged[Cdecl] PyObject_HasAttr { get; } + internal static delegate* unmanaged[Cdecl] PyObject_GetAttr { get; } + internal static delegate* unmanaged[Cdecl] PyObject_SetAttr { get; } + internal static delegate* unmanaged[Cdecl] PyObject_GetItem { get; } + internal static delegate* unmanaged[Cdecl] PyObject_SetItem { get; } + internal static delegate* unmanaged[Cdecl] PyObject_DelItem { get; } + internal static delegate* unmanaged[Cdecl] PyObject_GetIter { get; } + internal static delegate* unmanaged[Cdecl] PyObject_Call { get; } + internal static delegate* unmanaged[Cdecl] PyObject_CallObject { get; } + internal static delegate* unmanaged[Cdecl] PyObject_RichCompareBool { get; } + internal static delegate* unmanaged[Cdecl] PyObject_IsInstance { get; } + internal static delegate* unmanaged[Cdecl] PyObject_IsSubclass { get; } + internal static delegate* unmanaged[Cdecl] PyCallable_Check { get; } + internal static delegate* unmanaged[Cdecl] PyObject_IsTrue { get; } + internal static delegate* unmanaged[Cdecl] PyObject_Not { get; } + internal static delegate* unmanaged[Cdecl] PyObject_Size { get; } + internal static delegate* unmanaged[Cdecl] PyObject_Hash { get; } + internal static delegate* unmanaged[Cdecl] PyObject_Repr { get; } + internal static delegate* unmanaged[Cdecl] PyObject_Str { get; } + internal static delegate* unmanaged[Cdecl] PyObject_Type { get; } + internal static delegate* unmanaged[Cdecl] PyObject_Dir { get; } + internal static delegate* unmanaged[Cdecl] PyObject_GetBuffer { get; } + internal static delegate* unmanaged[Cdecl] PyBuffer_Release { get; } + internal static delegate* unmanaged[Cdecl] PyBuffer_SizeFromFormat { get; } + internal static delegate* unmanaged[Cdecl] PyBuffer_IsContiguous { get; } + internal static delegate* unmanaged[Cdecl] PyBuffer_GetPointer { get; } + internal static delegate* unmanaged[Cdecl] PyBuffer_FromContiguous { get; } + internal static delegate* unmanaged[Cdecl] PyBuffer_ToContiguous { get; } + internal static delegate* unmanaged[Cdecl] PyBuffer_FillContiguousStrides { get; } + internal static delegate* unmanaged[Cdecl] PyBuffer_FillInfo { get; } + internal static delegate* unmanaged[Cdecl] PyNumber_Long { get; } + internal static delegate* unmanaged[Cdecl] PyNumber_Float { get; } + internal static delegate* unmanaged[Cdecl] PyNumber_Check { get; } + internal static delegate* unmanaged[Cdecl] PyLong_FromLongLong { get; } + internal static delegate* unmanaged[Cdecl] PyLong_FromUnsignedLongLong { get; } + internal static delegate* unmanaged[Cdecl] PyLong_FromString { get; } + internal static delegate* unmanaged[Cdecl] PyLong_AsLongLong { get; } + internal static delegate* unmanaged[Cdecl] PyLong_AsUnsignedLongLong { get; } + internal static delegate* unmanaged[Cdecl] PyLong_FromVoidPtr { get; } + internal static delegate* unmanaged[Cdecl] PyLong_AsVoidPtr { get; } + internal static delegate* unmanaged[Cdecl] PyFloat_FromDouble { get; } + internal static delegate* unmanaged[Cdecl] PyFloat_FromString { get; } + internal static delegate* unmanaged[Cdecl] PyFloat_AsDouble { get; } + internal static delegate* unmanaged[Cdecl] PyNumber_Add { get; } + internal static delegate* unmanaged[Cdecl] PyNumber_Subtract { get; } + internal static delegate* unmanaged[Cdecl] PyNumber_Multiply { get; } + internal static delegate* unmanaged[Cdecl] PyNumber_TrueDivide { get; } + internal static delegate* unmanaged[Cdecl] PyNumber_And { get; } + internal static delegate* unmanaged[Cdecl] PyNumber_Xor { get; } + internal static delegate* unmanaged[Cdecl] PyNumber_Or { get; } + internal static delegate* unmanaged[Cdecl] PyNumber_Lshift { get; } + internal static delegate* unmanaged[Cdecl] PyNumber_Rshift { get; } + internal static delegate* unmanaged[Cdecl] PyNumber_Power { get; } + internal static delegate* unmanaged[Cdecl] PyNumber_Remainder { get; } + internal static delegate* unmanaged[Cdecl] PyNumber_InPlaceAdd { get; } + internal static delegate* unmanaged[Cdecl] PyNumber_InPlaceSubtract { get; } + internal static delegate* unmanaged[Cdecl] PyNumber_InPlaceMultiply { get; } + internal static delegate* unmanaged[Cdecl] PyNumber_InPlaceTrueDivide { get; } + internal static delegate* unmanaged[Cdecl] PyNumber_InPlaceAnd { get; } + internal static delegate* unmanaged[Cdecl] PyNumber_InPlaceXor { get; } + internal static delegate* unmanaged[Cdecl] PyNumber_InPlaceOr { get; } + internal static delegate* unmanaged[Cdecl] PyNumber_InPlaceLshift { get; } + internal static delegate* unmanaged[Cdecl] PyNumber_InPlaceRshift { get; } + internal static delegate* unmanaged[Cdecl] PyNumber_InPlacePower { get; } + internal static delegate* unmanaged[Cdecl] PyNumber_InPlaceRemainder { get; } + internal static delegate* unmanaged[Cdecl] PyNumber_Negative { get; } + internal static delegate* unmanaged[Cdecl] PyNumber_Positive { get; } + internal static delegate* unmanaged[Cdecl] PyNumber_Invert { get; } + internal static delegate* unmanaged[Cdecl] PySequence_Check { get; } + internal static delegate* unmanaged[Cdecl] PySequence_GetItem { get; } + internal static delegate* unmanaged[Cdecl] PySequence_SetItem { get; } + internal static delegate* unmanaged[Cdecl] PySequence_DelItem { get; } + internal static delegate* unmanaged[Cdecl] PySequence_GetSlice { get; } + internal static delegate* unmanaged[Cdecl] PySequence_SetSlice { get; } + internal static delegate* unmanaged[Cdecl] PySequence_DelSlice { get; } + internal static delegate* unmanaged[Cdecl] PySequence_Size { get; } + internal static delegate* unmanaged[Cdecl] PySequence_Contains { get; } + internal static delegate* unmanaged[Cdecl] PySequence_Concat { get; } + internal static delegate* unmanaged[Cdecl] PySequence_Repeat { get; } + internal static delegate* unmanaged[Cdecl] PySequence_Index { get; } + internal static delegate* unmanaged[Cdecl] PySequence_Count { get; } + internal static delegate* unmanaged[Cdecl] PySequence_Tuple { get; } + internal static delegate* unmanaged[Cdecl] PySequence_List { get; } + internal static delegate* unmanaged[Cdecl] PyBytes_AsString { get; } + internal static delegate* unmanaged[Cdecl] PyBytes_FromString { get; } + internal static delegate* unmanaged[Cdecl] PyByteArray_FromStringAndSize { get; } + internal static delegate* unmanaged[Cdecl] PyBytes_Size { get; } + internal static delegate* unmanaged[Cdecl] PyUnicode_AsUTF8 { get; } + internal static delegate* unmanaged[Cdecl] PyUnicode_DecodeUTF16 { get; } + internal static delegate* unmanaged[Cdecl] PyUnicode_GetLength { get; } + internal static delegate* unmanaged[Cdecl] PyUnicode_AsUnicode { get; } + internal static delegate* unmanaged[Cdecl] PyUnicode_AsUTF16String { get; } + internal static delegate* unmanaged[Cdecl] PyUnicode_FromOrdinal { get; } + internal static delegate* unmanaged[Cdecl] PyUnicode_InternFromString { get; } + internal static delegate* unmanaged[Cdecl] PyUnicode_Compare { get; } + internal static delegate* unmanaged[Cdecl] PyDict_New { get; } + internal static delegate* unmanaged[Cdecl] PyDict_GetItem { get; } + internal static delegate* unmanaged[Cdecl] PyDict_GetItemString { get; } + internal static delegate* unmanaged[Cdecl] PyDict_SetItem { get; } + internal static delegate* unmanaged[Cdecl] PyDict_SetItemString { get; } + internal static delegate* unmanaged[Cdecl] PyDict_DelItem { get; } + internal static delegate* unmanaged[Cdecl] PyDict_DelItemString { get; } + internal static delegate* unmanaged[Cdecl] PyMapping_HasKey { get; } + internal static delegate* unmanaged[Cdecl] PyDict_Keys { get; } + internal static delegate* unmanaged[Cdecl] PyDict_Values { get; } + internal static delegate* unmanaged[Cdecl] PyDict_Items { get; } + internal static delegate* unmanaged[Cdecl] PyDict_Copy { get; } + internal static delegate* unmanaged[Cdecl] PyDict_Update { get; } + internal static delegate* unmanaged[Cdecl] PyDict_Clear { get; } + internal static delegate* unmanaged[Cdecl] PyDict_Size { get; } + internal static delegate* unmanaged[Cdecl] PySet_New { get; } + internal static delegate* unmanaged[Cdecl] PySet_Add { get; } + internal static delegate* unmanaged[Cdecl] PySet_Contains { get; } + internal static delegate* unmanaged[Cdecl] PyList_New { get; } + internal static delegate* unmanaged[Cdecl] PyList_GetItem { get; } + internal static delegate* unmanaged[Cdecl] PyList_SetItem { get; } + internal static delegate* unmanaged[Cdecl] PyList_Insert { get; } + internal static delegate* unmanaged[Cdecl] PyList_Append { get; } + internal static delegate* unmanaged[Cdecl] PyList_Reverse { get; } + internal static delegate* unmanaged[Cdecl] PyList_Sort { get; } + internal static delegate* unmanaged[Cdecl] PyList_GetSlice { get; } + internal static delegate* unmanaged[Cdecl] PyList_SetSlice { get; } + internal static delegate* unmanaged[Cdecl] PyList_Size { get; } + internal static delegate* unmanaged[Cdecl] PyTuple_New { get; } + internal static delegate* unmanaged[Cdecl] PyTuple_GetItem { get; } + internal static delegate* unmanaged[Cdecl] PyTuple_SetItem { get; } + internal static delegate* unmanaged[Cdecl] PyTuple_GetSlice { get; } + internal static delegate* unmanaged[Cdecl] PyTuple_Size { get; } + internal static delegate* unmanaged[Cdecl] PyIter_Check { get; } + internal static delegate* unmanaged[Cdecl] PyIter_Next { get; } + internal static delegate* unmanaged[Cdecl] PyModule_New { get; } + internal static delegate* unmanaged[Cdecl] PyModule_GetDict { get; } + internal static delegate* unmanaged[Cdecl] PyModule_AddObject { get; } + internal static delegate* unmanaged[Cdecl] PyImport_Import { get; } + internal static delegate* unmanaged[Cdecl] PyImport_ImportModule { get; } + internal static delegate* unmanaged[Cdecl] PyImport_ReloadModule { get; } + internal static delegate* unmanaged[Cdecl] PyImport_AddModule { get; } + internal static delegate* unmanaged[Cdecl] PyImport_GetModuleDict { get; } + internal static delegate* unmanaged[Cdecl] PySys_SetArgvEx { get; } + internal static delegate* unmanaged[Cdecl] PySys_GetObject { get; } + internal static delegate* unmanaged[Cdecl] PySys_SetObject { get; } + internal static delegate* unmanaged[Cdecl] PyType_Modified { get; } + internal static delegate* unmanaged[Cdecl] PyType_IsSubtype { get; } + internal static delegate* unmanaged[Cdecl] PyType_GenericNew { get; } + internal static delegate* unmanaged[Cdecl] PyType_GenericAlloc { get; } + internal static delegate* unmanaged[Cdecl] PyType_Ready { get; } + internal static delegate* unmanaged[Cdecl] _PyType_Lookup { get; } + internal static delegate* unmanaged[Cdecl] PyObject_GenericGetAttr { get; } + internal static delegate* unmanaged[Cdecl] PyObject_GenericSetAttr { get; } + internal static delegate* unmanaged[Cdecl] PyObject_GC_Del { get; } + internal static delegate* unmanaged[Cdecl] PyObject_GC_IsTracked { get; } + internal static delegate* unmanaged[Cdecl] PyObject_GC_Track { get; } + internal static delegate* unmanaged[Cdecl] PyObject_GC_UnTrack { get; } + internal static delegate* unmanaged[Cdecl] _PyObject_Dump { get; } + internal static delegate* unmanaged[Cdecl] PyMem_Malloc { get; } + internal static delegate* unmanaged[Cdecl] PyMem_Realloc { get; } + internal static delegate* unmanaged[Cdecl] PyMem_Free { get; } + internal static delegate* unmanaged[Cdecl] PyErr_SetString { get; } + internal static delegate* unmanaged[Cdecl] PyErr_SetObject { get; } + internal static delegate* unmanaged[Cdecl] PyErr_ExceptionMatches { get; } + internal static delegate* unmanaged[Cdecl] PyErr_GivenExceptionMatches { get; } + internal static delegate* unmanaged[Cdecl] PyErr_NormalizeException { get; } + internal static delegate* unmanaged[Cdecl] PyErr_Occurred { get; } + internal static delegate* unmanaged[Cdecl] PyErr_Fetch { get; } + internal static delegate* unmanaged[Cdecl] PyErr_Restore { get; } + internal static delegate* unmanaged[Cdecl] PyErr_Clear { get; } + internal static delegate* unmanaged[Cdecl] PyErr_Print { get; } + internal static delegate* unmanaged[Cdecl] PyCell_Get { get; } + internal static delegate* unmanaged[Cdecl] PyCell_Set { get; } + internal static delegate* unmanaged[Cdecl] PyGC_Collect { get; } + internal static delegate* unmanaged[Cdecl] PyCapsule_New { get; } + internal static delegate* unmanaged[Cdecl] PyCapsule_GetPointer { get; } + internal static delegate* unmanaged[Cdecl] PyCapsule_SetPointer { get; } + internal static delegate* unmanaged[Cdecl] PyLong_AsUnsignedSize_t { get; } + internal static delegate* unmanaged[Cdecl] PyLong_AsSignedSize_t { get; } + internal static delegate* unmanaged[Cdecl] PyDict_GetItemWithError { get; } + internal static delegate* unmanaged[Cdecl] PyException_GetCause { get; } + internal static delegate* unmanaged[Cdecl] PyException_GetTraceback { get; } + internal static delegate* unmanaged[Cdecl] PyException_SetCause { get; } + internal static delegate* unmanaged[Cdecl] PyException_SetTraceback { get; } + internal static delegate* unmanaged[Cdecl] PyThreadState_SetAsyncExcLLP64 { get; } + internal static delegate* unmanaged[Cdecl] PyThreadState_SetAsyncExcLP64 { get; } + internal static delegate* unmanaged[Cdecl] PyObject_GenericGetDict { get; } + internal static delegate* unmanaged[Cdecl] PyType_GetSlot { get; } + internal static delegate* unmanaged[Cdecl] PyType_FromSpecWithBases { get; } + internal static delegate* unmanaged[Cdecl] _Py_NewReference { get; } + internal static delegate* unmanaged[Cdecl] _Py_IsFinalizing { get; } + internal static IntPtr PyType_Type { get; } + internal static int* Py_NoSiteFlag { get; } + } +} diff --git a/src/runtime/runtime.cs b/src/runtime/Runtime.cs similarity index 51% rename from src/runtime/runtime.cs rename to src/runtime/Runtime.cs index c8489f7cf..7806c3156 100644 --- a/src/runtime/runtime.cs +++ b/src/runtime/Runtime.cs @@ -6,7 +6,6 @@ using System.Threading; using System.Collections.Generic; using Python.Runtime.Native; -using Python.Runtime.Platform; using System.Linq; using static System.FormattableString; @@ -17,7 +16,7 @@ namespace Python.Runtime /// the responsibility of the caller to have acquired the GIL /// before calling any of these methods. /// - public unsafe class Runtime + public unsafe partial class Runtime { public static string? PythonDLL { @@ -538,26 +537,6 @@ internal static void CheckExceptionOccurred() } } - internal static NewReference ExtendTuple(BorrowedReference t, params PyObject[] args) - { - var size = PyTuple_Size(t); - int add = args.Length; - - NewReference items = PyTuple_New(size + add); - for (var i = 0; i < size; i++) - { - var item = PyTuple_GetItem(t, i); - PyTuple_SetItem(items.Borrow(), i, item); - } - - for (var n = 0; n < add; n++) - { - PyTuple_SetItem(items.Borrow(), size + n, args[n]); - } - - return items; - } - internal static Type[]? PythonArgsToTypeArray(BorrowedReference arg) { return PythonArgsToTypeArray(arg, false); @@ -1832,17 +1811,6 @@ internal static int PyException_SetTraceback(BorrowedReference ex, BorrowedRefer internal static int PyCell_Set(BorrowedReference cell, BorrowedReference value) => Delegates.PyCell_Set(cell, value); - //==================================================================== - // Python GC API - //==================================================================== - - internal const int _PyGC_REFS_SHIFT = 1; - internal const long _PyGC_REFS_UNTRACKED = -2; - internal const long _PyGC_REFS_REACHABLE = -3; - internal const long _PyGC_REFS_TENTATIVELY_UNREACHABLE = -4; - - - internal static nint PyGC_Collect() => Delegates.PyGC_Collect(); internal static void Py_CLEAR(BorrowedReference ob, int offset) => ReplaceReference(ob, offset, default); internal static void Py_CLEAR(ref T? ob) @@ -1892,537 +1860,6 @@ internal static void SetNoSiteFlag() return *Delegates.Py_NoSiteFlag; }); } - - internal static class Delegates - { - static readonly ILibraryLoader libraryLoader = LibraryLoader.Instance; - - static Delegates() - { - Py_IncRef = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(Py_IncRef), GetUnmanagedDll(_PythonDll)); - Py_DecRef = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(Py_DecRef), GetUnmanagedDll(_PythonDll)); - Py_Initialize = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(Py_Initialize), GetUnmanagedDll(_PythonDll)); - Py_InitializeEx = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(Py_InitializeEx), GetUnmanagedDll(_PythonDll)); - Py_IsInitialized = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(Py_IsInitialized), GetUnmanagedDll(_PythonDll)); - Py_Finalize = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(Py_Finalize), GetUnmanagedDll(_PythonDll)); - Py_NewInterpreter = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(Py_NewInterpreter), GetUnmanagedDll(_PythonDll)); - Py_EndInterpreter = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(Py_EndInterpreter), GetUnmanagedDll(_PythonDll)); - PyThreadState_New = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyThreadState_New), GetUnmanagedDll(_PythonDll)); - PyThreadState_Get = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyThreadState_Get), GetUnmanagedDll(_PythonDll)); - _PyThreadState_UncheckedGet = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(_PyThreadState_UncheckedGet), GetUnmanagedDll(_PythonDll)); - try - { - PyGILState_Check = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyGILState_Check), GetUnmanagedDll(_PythonDll)); - } - catch (MissingMethodException e) - { - throw new NotSupportedException(Util.MinimalPythonVersionRequired, innerException: e); - } - PyGILState_Ensure = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyGILState_Ensure), GetUnmanagedDll(_PythonDll)); - PyGILState_Release = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyGILState_Release), GetUnmanagedDll(_PythonDll)); - PyGILState_GetThisThreadState = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyGILState_GetThisThreadState), GetUnmanagedDll(_PythonDll)); - Py_Main = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(Py_Main), GetUnmanagedDll(_PythonDll)); - PyEval_InitThreads = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyEval_InitThreads), GetUnmanagedDll(_PythonDll)); - PyEval_ThreadsInitialized = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyEval_ThreadsInitialized), GetUnmanagedDll(_PythonDll)); - PyEval_AcquireLock = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyEval_AcquireLock), GetUnmanagedDll(_PythonDll)); - PyEval_ReleaseLock = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyEval_ReleaseLock), GetUnmanagedDll(_PythonDll)); - PyEval_AcquireThread = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyEval_AcquireThread), GetUnmanagedDll(_PythonDll)); - PyEval_ReleaseThread = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyEval_ReleaseThread), GetUnmanagedDll(_PythonDll)); - PyEval_SaveThread = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyEval_SaveThread), GetUnmanagedDll(_PythonDll)); - PyEval_RestoreThread = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyEval_RestoreThread), GetUnmanagedDll(_PythonDll)); - PyEval_GetBuiltins = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyEval_GetBuiltins), GetUnmanagedDll(_PythonDll)); - PyEval_GetGlobals = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyEval_GetGlobals), GetUnmanagedDll(_PythonDll)); - PyEval_GetLocals = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyEval_GetLocals), GetUnmanagedDll(_PythonDll)); - Py_GetProgramName = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(Py_GetProgramName), GetUnmanagedDll(_PythonDll)); - Py_SetProgramName = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(Py_SetProgramName), GetUnmanagedDll(_PythonDll)); - Py_GetPythonHome = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(Py_GetPythonHome), GetUnmanagedDll(_PythonDll)); - Py_SetPythonHome = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(Py_SetPythonHome), GetUnmanagedDll(_PythonDll)); - Py_GetPath = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(Py_GetPath), GetUnmanagedDll(_PythonDll)); - Py_SetPath = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(Py_SetPath), GetUnmanagedDll(_PythonDll)); - Py_GetVersion = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(Py_GetVersion), GetUnmanagedDll(_PythonDll)); - Py_GetPlatform = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(Py_GetPlatform), GetUnmanagedDll(_PythonDll)); - Py_GetCopyright = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(Py_GetCopyright), GetUnmanagedDll(_PythonDll)); - Py_GetCompiler = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(Py_GetCompiler), GetUnmanagedDll(_PythonDll)); - Py_GetBuildInfo = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(Py_GetBuildInfo), GetUnmanagedDll(_PythonDll)); - PyRun_SimpleStringFlags = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyRun_SimpleStringFlags), GetUnmanagedDll(_PythonDll)); - PyRun_StringFlags = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyRun_StringFlags), GetUnmanagedDll(_PythonDll)); - PyEval_EvalCode = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyEval_EvalCode), GetUnmanagedDll(_PythonDll)); - Py_CompileStringObject = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(Py_CompileStringObject), GetUnmanagedDll(_PythonDll)); - PyImport_ExecCodeModule = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyImport_ExecCodeModule), GetUnmanagedDll(_PythonDll)); - PyObject_HasAttrString = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyObject_HasAttrString), GetUnmanagedDll(_PythonDll)); - PyObject_GetAttrString = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyObject_GetAttrString), GetUnmanagedDll(_PythonDll)); - PyObject_SetAttrString = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyObject_SetAttrString), GetUnmanagedDll(_PythonDll)); - PyObject_HasAttr = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyObject_HasAttr), GetUnmanagedDll(_PythonDll)); - PyObject_GetAttr = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyObject_GetAttr), GetUnmanagedDll(_PythonDll)); - PyObject_SetAttr = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyObject_SetAttr), GetUnmanagedDll(_PythonDll)); - PyObject_GetItem = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyObject_GetItem), GetUnmanagedDll(_PythonDll)); - PyObject_SetItem = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyObject_SetItem), GetUnmanagedDll(_PythonDll)); - PyObject_DelItem = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyObject_DelItem), GetUnmanagedDll(_PythonDll)); - PyObject_GetIter = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyObject_GetIter), GetUnmanagedDll(_PythonDll)); - PyObject_Call = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyObject_Call), GetUnmanagedDll(_PythonDll)); - PyObject_CallObject = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyObject_CallObject), GetUnmanagedDll(_PythonDll)); - PyObject_RichCompareBool = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyObject_RichCompareBool), GetUnmanagedDll(_PythonDll)); - PyObject_IsInstance = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyObject_IsInstance), GetUnmanagedDll(_PythonDll)); - PyObject_IsSubclass = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyObject_IsSubclass), GetUnmanagedDll(_PythonDll)); - PyCallable_Check = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyCallable_Check), GetUnmanagedDll(_PythonDll)); - PyObject_IsTrue = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyObject_IsTrue), GetUnmanagedDll(_PythonDll)); - PyObject_Not = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyObject_Not), GetUnmanagedDll(_PythonDll)); - PyObject_Size = (delegate* unmanaged[Cdecl])GetFunctionByName("PyObject_Size", GetUnmanagedDll(_PythonDll)); - PyObject_Hash = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyObject_Hash), GetUnmanagedDll(_PythonDll)); - PyObject_Repr = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyObject_Repr), GetUnmanagedDll(_PythonDll)); - PyObject_Str = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyObject_Str), GetUnmanagedDll(_PythonDll)); - PyObject_Type = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyObject_Type), GetUnmanagedDll(_PythonDll)); - PyObject_Dir = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyObject_Dir), GetUnmanagedDll(_PythonDll)); - PyObject_GetBuffer = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyObject_GetBuffer), GetUnmanagedDll(_PythonDll)); - PyBuffer_Release = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyBuffer_Release), GetUnmanagedDll(_PythonDll)); - try - { - PyBuffer_SizeFromFormat = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyBuffer_SizeFromFormat), GetUnmanagedDll(_PythonDll)); - } - catch (MissingMethodException) - { - // only in 3.9+ - } - PyBuffer_IsContiguous = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyBuffer_IsContiguous), GetUnmanagedDll(_PythonDll)); - PyBuffer_GetPointer = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyBuffer_GetPointer), GetUnmanagedDll(_PythonDll)); - PyBuffer_FromContiguous = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyBuffer_FromContiguous), GetUnmanagedDll(_PythonDll)); - PyBuffer_ToContiguous = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyBuffer_ToContiguous), GetUnmanagedDll(_PythonDll)); - PyBuffer_FillContiguousStrides = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyBuffer_FillContiguousStrides), GetUnmanagedDll(_PythonDll)); - PyBuffer_FillInfo = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyBuffer_FillInfo), GetUnmanagedDll(_PythonDll)); - PyNumber_Long = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyNumber_Long), GetUnmanagedDll(_PythonDll)); - PyNumber_Float = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyNumber_Float), GetUnmanagedDll(_PythonDll)); - PyNumber_Check = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyNumber_Check), GetUnmanagedDll(_PythonDll)); - PyLong_FromLongLong = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyLong_FromLongLong), GetUnmanagedDll(_PythonDll)); - PyLong_FromUnsignedLongLong = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyLong_FromUnsignedLongLong), GetUnmanagedDll(_PythonDll)); - PyLong_FromString = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyLong_FromString), GetUnmanagedDll(_PythonDll)); - PyLong_AsLongLong = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyLong_AsLongLong), GetUnmanagedDll(_PythonDll)); - PyLong_AsUnsignedLongLong = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyLong_AsUnsignedLongLong), GetUnmanagedDll(_PythonDll)); - PyLong_FromVoidPtr = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyLong_FromVoidPtr), GetUnmanagedDll(_PythonDll)); - PyLong_AsVoidPtr = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyLong_AsVoidPtr), GetUnmanagedDll(_PythonDll)); - PyFloat_FromDouble = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyFloat_FromDouble), GetUnmanagedDll(_PythonDll)); - PyFloat_FromString = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyFloat_FromString), GetUnmanagedDll(_PythonDll)); - PyFloat_AsDouble = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyFloat_AsDouble), GetUnmanagedDll(_PythonDll)); - PyNumber_Add = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyNumber_Add), GetUnmanagedDll(_PythonDll)); - PyNumber_Subtract = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyNumber_Subtract), GetUnmanagedDll(_PythonDll)); - PyNumber_Multiply = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyNumber_Multiply), GetUnmanagedDll(_PythonDll)); - PyNumber_TrueDivide = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyNumber_TrueDivide), GetUnmanagedDll(_PythonDll)); - PyNumber_And = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyNumber_And), GetUnmanagedDll(_PythonDll)); - PyNumber_Xor = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyNumber_Xor), GetUnmanagedDll(_PythonDll)); - PyNumber_Or = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyNumber_Or), GetUnmanagedDll(_PythonDll)); - PyNumber_Lshift = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyNumber_Lshift), GetUnmanagedDll(_PythonDll)); - PyNumber_Rshift = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyNumber_Rshift), GetUnmanagedDll(_PythonDll)); - PyNumber_Power = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyNumber_Power), GetUnmanagedDll(_PythonDll)); - PyNumber_Remainder = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyNumber_Remainder), GetUnmanagedDll(_PythonDll)); - PyNumber_InPlaceAdd = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyNumber_InPlaceAdd), GetUnmanagedDll(_PythonDll)); - PyNumber_InPlaceSubtract = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyNumber_InPlaceSubtract), GetUnmanagedDll(_PythonDll)); - PyNumber_InPlaceMultiply = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyNumber_InPlaceMultiply), GetUnmanagedDll(_PythonDll)); - PyNumber_InPlaceTrueDivide = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyNumber_InPlaceTrueDivide), GetUnmanagedDll(_PythonDll)); - PyNumber_InPlaceAnd = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyNumber_InPlaceAnd), GetUnmanagedDll(_PythonDll)); - PyNumber_InPlaceXor = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyNumber_InPlaceXor), GetUnmanagedDll(_PythonDll)); - PyNumber_InPlaceOr = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyNumber_InPlaceOr), GetUnmanagedDll(_PythonDll)); - PyNumber_InPlaceLshift = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyNumber_InPlaceLshift), GetUnmanagedDll(_PythonDll)); - PyNumber_InPlaceRshift = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyNumber_InPlaceRshift), GetUnmanagedDll(_PythonDll)); - PyNumber_InPlacePower = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyNumber_InPlacePower), GetUnmanagedDll(_PythonDll)); - PyNumber_InPlaceRemainder = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyNumber_InPlaceRemainder), GetUnmanagedDll(_PythonDll)); - PyNumber_Negative = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyNumber_Negative), GetUnmanagedDll(_PythonDll)); - PyNumber_Positive = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyNumber_Positive), GetUnmanagedDll(_PythonDll)); - PyNumber_Invert = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyNumber_Invert), GetUnmanagedDll(_PythonDll)); - PySequence_Check = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PySequence_Check), GetUnmanagedDll(_PythonDll)); - PySequence_GetItem = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PySequence_GetItem), GetUnmanagedDll(_PythonDll)); - PySequence_SetItem = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PySequence_SetItem), GetUnmanagedDll(_PythonDll)); - PySequence_DelItem = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PySequence_DelItem), GetUnmanagedDll(_PythonDll)); - PySequence_GetSlice = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PySequence_GetSlice), GetUnmanagedDll(_PythonDll)); - PySequence_SetSlice = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PySequence_SetSlice), GetUnmanagedDll(_PythonDll)); - PySequence_DelSlice = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PySequence_DelSlice), GetUnmanagedDll(_PythonDll)); - PySequence_Size = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PySequence_Size), GetUnmanagedDll(_PythonDll)); - PySequence_Contains = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PySequence_Contains), GetUnmanagedDll(_PythonDll)); - PySequence_Concat = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PySequence_Concat), GetUnmanagedDll(_PythonDll)); - PySequence_Repeat = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PySequence_Repeat), GetUnmanagedDll(_PythonDll)); - PySequence_Index = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PySequence_Index), GetUnmanagedDll(_PythonDll)); - PySequence_Count = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PySequence_Count), GetUnmanagedDll(_PythonDll)); - PySequence_Tuple = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PySequence_Tuple), GetUnmanagedDll(_PythonDll)); - PySequence_List = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PySequence_List), GetUnmanagedDll(_PythonDll)); - PyBytes_AsString = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyBytes_AsString), GetUnmanagedDll(_PythonDll)); - PyBytes_FromString = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyBytes_FromString), GetUnmanagedDll(_PythonDll)); - PyByteArray_FromStringAndSize = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyByteArray_FromStringAndSize), GetUnmanagedDll(_PythonDll)); - PyBytes_Size = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyBytes_Size), GetUnmanagedDll(_PythonDll)); - PyUnicode_AsUTF8 = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyUnicode_AsUTF8), GetUnmanagedDll(_PythonDll)); - PyUnicode_DecodeUTF16 = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyUnicode_DecodeUTF16), GetUnmanagedDll(_PythonDll)); - PyUnicode_GetLength = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyUnicode_GetLength), GetUnmanagedDll(_PythonDll)); - PyUnicode_AsUnicode = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyUnicode_AsUnicode), GetUnmanagedDll(_PythonDll)); - PyUnicode_AsUTF16String = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyUnicode_AsUTF16String), GetUnmanagedDll(_PythonDll)); - PyUnicode_FromOrdinal = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyUnicode_FromOrdinal), GetUnmanagedDll(_PythonDll)); - PyUnicode_InternFromString = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyUnicode_InternFromString), GetUnmanagedDll(_PythonDll)); - PyUnicode_Compare = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyUnicode_Compare), GetUnmanagedDll(_PythonDll)); - PyDict_New = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyDict_New), GetUnmanagedDll(_PythonDll)); - PyDict_GetItem = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyDict_GetItem), GetUnmanagedDll(_PythonDll)); - PyDict_GetItemString = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyDict_GetItemString), GetUnmanagedDll(_PythonDll)); - PyDict_SetItem = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyDict_SetItem), GetUnmanagedDll(_PythonDll)); - PyDict_SetItemString = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyDict_SetItemString), GetUnmanagedDll(_PythonDll)); - PyDict_DelItem = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyDict_DelItem), GetUnmanagedDll(_PythonDll)); - PyDict_DelItemString = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyDict_DelItemString), GetUnmanagedDll(_PythonDll)); - PyMapping_HasKey = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyMapping_HasKey), GetUnmanagedDll(_PythonDll)); - PyDict_Keys = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyDict_Keys), GetUnmanagedDll(_PythonDll)); - PyDict_Values = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyDict_Values), GetUnmanagedDll(_PythonDll)); - PyDict_Items = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyDict_Items), GetUnmanagedDll(_PythonDll)); - PyDict_Copy = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyDict_Copy), GetUnmanagedDll(_PythonDll)); - PyDict_Update = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyDict_Update), GetUnmanagedDll(_PythonDll)); - PyDict_Clear = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyDict_Clear), GetUnmanagedDll(_PythonDll)); - PyDict_Size = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyDict_Size), GetUnmanagedDll(_PythonDll)); - PySet_New = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PySet_New), GetUnmanagedDll(_PythonDll)); - PySet_Add = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PySet_Add), GetUnmanagedDll(_PythonDll)); - PySet_Contains = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PySet_Contains), GetUnmanagedDll(_PythonDll)); - PyList_New = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyList_New), GetUnmanagedDll(_PythonDll)); - PyList_GetItem = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyList_GetItem), GetUnmanagedDll(_PythonDll)); - PyList_SetItem = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyList_SetItem), GetUnmanagedDll(_PythonDll)); - PyList_Insert = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyList_Insert), GetUnmanagedDll(_PythonDll)); - PyList_Append = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyList_Append), GetUnmanagedDll(_PythonDll)); - PyList_Reverse = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyList_Reverse), GetUnmanagedDll(_PythonDll)); - PyList_Sort = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyList_Sort), GetUnmanagedDll(_PythonDll)); - PyList_GetSlice = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyList_GetSlice), GetUnmanagedDll(_PythonDll)); - PyList_SetSlice = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyList_SetSlice), GetUnmanagedDll(_PythonDll)); - PyList_Size = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyList_Size), GetUnmanagedDll(_PythonDll)); - PyTuple_New = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyTuple_New), GetUnmanagedDll(_PythonDll)); - PyTuple_GetItem = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyTuple_GetItem), GetUnmanagedDll(_PythonDll)); - PyTuple_SetItem = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyTuple_SetItem), GetUnmanagedDll(_PythonDll)); - PyTuple_GetSlice = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyTuple_GetSlice), GetUnmanagedDll(_PythonDll)); - PyTuple_Size = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyTuple_Size), GetUnmanagedDll(_PythonDll)); - try - { - PyIter_Check = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyIter_Check), GetUnmanagedDll(_PythonDll)); - } catch (MissingMethodException) { } - PyIter_Next = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyIter_Next), GetUnmanagedDll(_PythonDll)); - PyModule_New = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyModule_New), GetUnmanagedDll(_PythonDll)); - PyModule_GetDict = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyModule_GetDict), GetUnmanagedDll(_PythonDll)); - PyModule_AddObject = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyModule_AddObject), GetUnmanagedDll(_PythonDll)); - PyImport_Import = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyImport_Import), GetUnmanagedDll(_PythonDll)); - PyImport_ImportModule = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyImport_ImportModule), GetUnmanagedDll(_PythonDll)); - PyImport_ReloadModule = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyImport_ReloadModule), GetUnmanagedDll(_PythonDll)); - PyImport_AddModule = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyImport_AddModule), GetUnmanagedDll(_PythonDll)); - PyImport_GetModuleDict = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyImport_GetModuleDict), GetUnmanagedDll(_PythonDll)); - PySys_SetArgvEx = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PySys_SetArgvEx), GetUnmanagedDll(_PythonDll)); - PySys_GetObject = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PySys_GetObject), GetUnmanagedDll(_PythonDll)); - PySys_SetObject = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PySys_SetObject), GetUnmanagedDll(_PythonDll)); - PyType_Modified = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyType_Modified), GetUnmanagedDll(_PythonDll)); - PyType_IsSubtype = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyType_IsSubtype), GetUnmanagedDll(_PythonDll)); - PyType_GenericNew = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyType_GenericNew), GetUnmanagedDll(_PythonDll)); - PyType_GenericAlloc = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyType_GenericAlloc), GetUnmanagedDll(_PythonDll)); - PyType_Ready = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyType_Ready), GetUnmanagedDll(_PythonDll)); - _PyType_Lookup = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(_PyType_Lookup), GetUnmanagedDll(_PythonDll)); - PyObject_GenericGetAttr = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyObject_GenericGetAttr), GetUnmanagedDll(_PythonDll)); - PyObject_GenericGetDict = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyObject_GenericGetDict), GetUnmanagedDll(PythonDLL)); - PyObject_GenericSetAttr = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyObject_GenericSetAttr), GetUnmanagedDll(_PythonDll)); - PyObject_GC_Del = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyObject_GC_Del), GetUnmanagedDll(_PythonDll)); - try - { - PyObject_GC_IsTracked = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyObject_GC_IsTracked), GetUnmanagedDll(_PythonDll)); - } catch (MissingMethodException) { } - PyObject_GC_Track = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyObject_GC_Track), GetUnmanagedDll(_PythonDll)); - PyObject_GC_UnTrack = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyObject_GC_UnTrack), GetUnmanagedDll(_PythonDll)); - _PyObject_Dump = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(_PyObject_Dump), GetUnmanagedDll(_PythonDll)); - PyMem_Malloc = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyMem_Malloc), GetUnmanagedDll(_PythonDll)); - PyMem_Realloc = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyMem_Realloc), GetUnmanagedDll(_PythonDll)); - PyMem_Free = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyMem_Free), GetUnmanagedDll(_PythonDll)); - PyErr_SetString = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyErr_SetString), GetUnmanagedDll(_PythonDll)); - PyErr_SetObject = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyErr_SetObject), GetUnmanagedDll(_PythonDll)); - PyErr_ExceptionMatches = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyErr_ExceptionMatches), GetUnmanagedDll(_PythonDll)); - PyErr_GivenExceptionMatches = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyErr_GivenExceptionMatches), GetUnmanagedDll(_PythonDll)); - PyErr_NormalizeException = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyErr_NormalizeException), GetUnmanagedDll(_PythonDll)); - PyErr_Occurred = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyErr_Occurred), GetUnmanagedDll(_PythonDll)); - PyErr_Fetch = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyErr_Fetch), GetUnmanagedDll(_PythonDll)); - PyErr_Restore = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyErr_Restore), GetUnmanagedDll(_PythonDll)); - PyErr_Clear = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyErr_Clear), GetUnmanagedDll(_PythonDll)); - PyErr_Print = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyErr_Print), GetUnmanagedDll(_PythonDll)); - PyCell_Get = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyCell_Get), GetUnmanagedDll(_PythonDll)); - PyCell_Set = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyCell_Set), GetUnmanagedDll(_PythonDll)); - PyGC_Collect = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyGC_Collect), GetUnmanagedDll(_PythonDll)); - PyCapsule_New = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyCapsule_New), GetUnmanagedDll(_PythonDll)); - PyCapsule_GetPointer = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyCapsule_GetPointer), GetUnmanagedDll(_PythonDll)); - PyCapsule_SetPointer = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyCapsule_SetPointer), GetUnmanagedDll(_PythonDll)); - PyLong_AsUnsignedSize_t = (delegate* unmanaged[Cdecl])GetFunctionByName("PyLong_AsSize_t", GetUnmanagedDll(_PythonDll)); - PyLong_AsSignedSize_t = (delegate* unmanaged[Cdecl])GetFunctionByName("PyLong_AsSsize_t", GetUnmanagedDll(_PythonDll)); - PyDict_GetItemWithError = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyDict_GetItemWithError), GetUnmanagedDll(_PythonDll)); - PyException_GetCause = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyException_GetCause), GetUnmanagedDll(_PythonDll)); - PyException_GetTraceback = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyException_GetTraceback), GetUnmanagedDll(_PythonDll)); - PyException_SetCause = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyException_SetCause), GetUnmanagedDll(_PythonDll)); - PyException_SetTraceback = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyException_SetTraceback), GetUnmanagedDll(_PythonDll)); - PyThreadState_SetAsyncExcLLP64 = (delegate* unmanaged[Cdecl])GetFunctionByName("PyThreadState_SetAsyncExc", GetUnmanagedDll(_PythonDll)); - PyThreadState_SetAsyncExcLP64 = (delegate* unmanaged[Cdecl])GetFunctionByName("PyThreadState_SetAsyncExc", GetUnmanagedDll(_PythonDll)); - PyType_GetSlot = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyType_GetSlot), GetUnmanagedDll(_PythonDll)); - PyType_FromSpecWithBases = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyType_FromSpecWithBases), GetUnmanagedDll(PythonDLL)); - - try - { - _Py_NewReference = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(_Py_NewReference), GetUnmanagedDll(_PythonDll)); - } - catch (MissingMethodException) { } - try - { - _Py_IsFinalizing = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(_Py_IsFinalizing), GetUnmanagedDll(_PythonDll)); - } - catch (MissingMethodException) { } - - PyType_Type = GetFunctionByName(nameof(PyType_Type), GetUnmanagedDll(_PythonDll)); - Py_NoSiteFlag = (int*)GetFunctionByName(nameof(Py_NoSiteFlag), GetUnmanagedDll(_PythonDll)); - } - - static global::System.IntPtr GetUnmanagedDll(string? libraryName) - { - if (libraryName is null) return IntPtr.Zero; - return libraryLoader.Load(libraryName); - } - - static global::System.IntPtr GetFunctionByName(string functionName, global::System.IntPtr libraryHandle) - { - try - { - return libraryLoader.GetFunction(libraryHandle, functionName); - } - catch (MissingMethodException e) when (libraryHandle == IntPtr.Zero) - { - throw new BadPythonDllException( - "Runtime.PythonDLL was not set or does not point to a supported Python runtime DLL." + - " See https://github.com/pythonnet/pythonnet#embedding-python-in-net", - e); - } - } - - internal static delegate* unmanaged[Cdecl] Py_IncRef { get; } - internal static delegate* unmanaged[Cdecl] Py_DecRef { get; } - internal static delegate* unmanaged[Cdecl] Py_Initialize { get; } - internal static delegate* unmanaged[Cdecl] Py_InitializeEx { get; } - internal static delegate* unmanaged[Cdecl] Py_IsInitialized { get; } - internal static delegate* unmanaged[Cdecl] Py_Finalize { get; } - internal static delegate* unmanaged[Cdecl] Py_NewInterpreter { get; } - internal static delegate* unmanaged[Cdecl] Py_EndInterpreter { get; } - internal static delegate* unmanaged[Cdecl] PyThreadState_New { get; } - internal static delegate* unmanaged[Cdecl] PyThreadState_Get { get; } - internal static delegate* unmanaged[Cdecl] _PyThreadState_UncheckedGet { get; } - internal static delegate* unmanaged[Cdecl] PyGILState_Check { get; } - internal static delegate* unmanaged[Cdecl] PyGILState_Ensure { get; } - internal static delegate* unmanaged[Cdecl] PyGILState_Release { get; } - internal static delegate* unmanaged[Cdecl] PyGILState_GetThisThreadState { get; } - internal static delegate* unmanaged[Cdecl] Py_Main { get; } - internal static delegate* unmanaged[Cdecl] PyEval_InitThreads { get; } - internal static delegate* unmanaged[Cdecl] PyEval_ThreadsInitialized { get; } - internal static delegate* unmanaged[Cdecl] PyEval_AcquireLock { get; } - internal static delegate* unmanaged[Cdecl] PyEval_ReleaseLock { get; } - internal static delegate* unmanaged[Cdecl] PyEval_AcquireThread { get; } - internal static delegate* unmanaged[Cdecl] PyEval_ReleaseThread { get; } - internal static delegate* unmanaged[Cdecl] PyEval_SaveThread { get; } - internal static delegate* unmanaged[Cdecl] PyEval_RestoreThread { get; } - internal static delegate* unmanaged[Cdecl] PyEval_GetBuiltins { get; } - internal static delegate* unmanaged[Cdecl] PyEval_GetGlobals { get; } - internal static delegate* unmanaged[Cdecl] PyEval_GetLocals { get; } - internal static delegate* unmanaged[Cdecl] Py_GetProgramName { get; } - internal static delegate* unmanaged[Cdecl] Py_SetProgramName { get; } - internal static delegate* unmanaged[Cdecl] Py_GetPythonHome { get; } - internal static delegate* unmanaged[Cdecl] Py_SetPythonHome { get; } - internal static delegate* unmanaged[Cdecl] Py_GetPath { get; } - internal static delegate* unmanaged[Cdecl] Py_SetPath { get; } - internal static delegate* unmanaged[Cdecl] Py_GetVersion { get; } - internal static delegate* unmanaged[Cdecl] Py_GetPlatform { get; } - internal static delegate* unmanaged[Cdecl] Py_GetCopyright { get; } - internal static delegate* unmanaged[Cdecl] Py_GetCompiler { get; } - internal static delegate* unmanaged[Cdecl] Py_GetBuildInfo { get; } - internal static delegate* unmanaged[Cdecl] PyRun_SimpleStringFlags { get; } - internal static delegate* unmanaged[Cdecl] PyRun_StringFlags { get; } - internal static delegate* unmanaged[Cdecl] PyEval_EvalCode { get; } - internal static delegate* unmanaged[Cdecl] Py_CompileStringObject { get; } - internal static delegate* unmanaged[Cdecl] PyImport_ExecCodeModule { get; } - internal static delegate* unmanaged[Cdecl] PyObject_HasAttrString { get; } - internal static delegate* unmanaged[Cdecl] PyObject_GetAttrString { get; } - internal static delegate* unmanaged[Cdecl] PyObject_SetAttrString { get; } - internal static delegate* unmanaged[Cdecl] PyObject_HasAttr { get; } - internal static delegate* unmanaged[Cdecl] PyObject_GetAttr { get; } - internal static delegate* unmanaged[Cdecl] PyObject_SetAttr { get; } - internal static delegate* unmanaged[Cdecl] PyObject_GetItem { get; } - internal static delegate* unmanaged[Cdecl] PyObject_SetItem { get; } - internal static delegate* unmanaged[Cdecl] PyObject_DelItem { get; } - internal static delegate* unmanaged[Cdecl] PyObject_GetIter { get; } - internal static delegate* unmanaged[Cdecl] PyObject_Call { get; } - internal static delegate* unmanaged[Cdecl] PyObject_CallObject { get; } - internal static delegate* unmanaged[Cdecl] PyObject_RichCompareBool { get; } - internal static delegate* unmanaged[Cdecl] PyObject_IsInstance { get; } - internal static delegate* unmanaged[Cdecl] PyObject_IsSubclass { get; } - internal static delegate* unmanaged[Cdecl] PyCallable_Check { get; } - internal static delegate* unmanaged[Cdecl] PyObject_IsTrue { get; } - internal static delegate* unmanaged[Cdecl] PyObject_Not { get; } - internal static delegate* unmanaged[Cdecl] PyObject_Size { get; } - internal static delegate* unmanaged[Cdecl] PyObject_Hash { get; } - internal static delegate* unmanaged[Cdecl] PyObject_Repr { get; } - internal static delegate* unmanaged[Cdecl] PyObject_Str { get; } - internal static delegate* unmanaged[Cdecl] PyObject_Type { get; } - internal static delegate* unmanaged[Cdecl] PyObject_Dir { get; } - internal static delegate* unmanaged[Cdecl] PyObject_GetBuffer { get; } - internal static delegate* unmanaged[Cdecl] PyBuffer_Release { get; } - internal static delegate* unmanaged[Cdecl] PyBuffer_SizeFromFormat { get; } - internal static delegate* unmanaged[Cdecl] PyBuffer_IsContiguous { get; } - internal static delegate* unmanaged[Cdecl] PyBuffer_GetPointer { get; } - internal static delegate* unmanaged[Cdecl] PyBuffer_FromContiguous { get; } - internal static delegate* unmanaged[Cdecl] PyBuffer_ToContiguous { get; } - internal static delegate* unmanaged[Cdecl] PyBuffer_FillContiguousStrides { get; } - internal static delegate* unmanaged[Cdecl] PyBuffer_FillInfo { get; } - internal static delegate* unmanaged[Cdecl] PyNumber_Long { get; } - internal static delegate* unmanaged[Cdecl] PyNumber_Float { get; } - internal static delegate* unmanaged[Cdecl] PyNumber_Check { get; } - internal static delegate* unmanaged[Cdecl] PyLong_FromLongLong { get; } - internal static delegate* unmanaged[Cdecl] PyLong_FromUnsignedLongLong { get; } - internal static delegate* unmanaged[Cdecl] PyLong_FromString { get; } - internal static delegate* unmanaged[Cdecl] PyLong_AsLongLong { get; } - internal static delegate* unmanaged[Cdecl] PyLong_AsUnsignedLongLong { get; } - internal static delegate* unmanaged[Cdecl] PyLong_FromVoidPtr { get; } - internal static delegate* unmanaged[Cdecl] PyLong_AsVoidPtr { get; } - internal static delegate* unmanaged[Cdecl] PyFloat_FromDouble { get; } - internal static delegate* unmanaged[Cdecl] PyFloat_FromString { get; } - internal static delegate* unmanaged[Cdecl] PyFloat_AsDouble { get; } - internal static delegate* unmanaged[Cdecl] PyNumber_Add { get; } - internal static delegate* unmanaged[Cdecl] PyNumber_Subtract { get; } - internal static delegate* unmanaged[Cdecl] PyNumber_Multiply { get; } - internal static delegate* unmanaged[Cdecl] PyNumber_TrueDivide { get; } - internal static delegate* unmanaged[Cdecl] PyNumber_And { get; } - internal static delegate* unmanaged[Cdecl] PyNumber_Xor { get; } - internal static delegate* unmanaged[Cdecl] PyNumber_Or { get; } - internal static delegate* unmanaged[Cdecl] PyNumber_Lshift { get; } - internal static delegate* unmanaged[Cdecl] PyNumber_Rshift { get; } - internal static delegate* unmanaged[Cdecl] PyNumber_Power { get; } - internal static delegate* unmanaged[Cdecl] PyNumber_Remainder { get; } - internal static delegate* unmanaged[Cdecl] PyNumber_InPlaceAdd { get; } - internal static delegate* unmanaged[Cdecl] PyNumber_InPlaceSubtract { get; } - internal static delegate* unmanaged[Cdecl] PyNumber_InPlaceMultiply { get; } - internal static delegate* unmanaged[Cdecl] PyNumber_InPlaceTrueDivide { get; } - internal static delegate* unmanaged[Cdecl] PyNumber_InPlaceAnd { get; } - internal static delegate* unmanaged[Cdecl] PyNumber_InPlaceXor { get; } - internal static delegate* unmanaged[Cdecl] PyNumber_InPlaceOr { get; } - internal static delegate* unmanaged[Cdecl] PyNumber_InPlaceLshift { get; } - internal static delegate* unmanaged[Cdecl] PyNumber_InPlaceRshift { get; } - internal static delegate* unmanaged[Cdecl] PyNumber_InPlacePower { get; } - internal static delegate* unmanaged[Cdecl] PyNumber_InPlaceRemainder { get; } - internal static delegate* unmanaged[Cdecl] PyNumber_Negative { get; } - internal static delegate* unmanaged[Cdecl] PyNumber_Positive { get; } - internal static delegate* unmanaged[Cdecl] PyNumber_Invert { get; } - internal static delegate* unmanaged[Cdecl] PySequence_Check { get; } - internal static delegate* unmanaged[Cdecl] PySequence_GetItem { get; } - internal static delegate* unmanaged[Cdecl] PySequence_SetItem { get; } - internal static delegate* unmanaged[Cdecl] PySequence_DelItem { get; } - internal static delegate* unmanaged[Cdecl] PySequence_GetSlice { get; } - internal static delegate* unmanaged[Cdecl] PySequence_SetSlice { get; } - internal static delegate* unmanaged[Cdecl] PySequence_DelSlice { get; } - internal static delegate* unmanaged[Cdecl] PySequence_Size { get; } - internal static delegate* unmanaged[Cdecl] PySequence_Contains { get; } - internal static delegate* unmanaged[Cdecl] PySequence_Concat { get; } - internal static delegate* unmanaged[Cdecl] PySequence_Repeat { get; } - internal static delegate* unmanaged[Cdecl] PySequence_Index { get; } - internal static delegate* unmanaged[Cdecl] PySequence_Count { get; } - internal static delegate* unmanaged[Cdecl] PySequence_Tuple { get; } - internal static delegate* unmanaged[Cdecl] PySequence_List { get; } - internal static delegate* unmanaged[Cdecl] PyBytes_AsString { get; } - internal static delegate* unmanaged[Cdecl] PyBytes_FromString { get; } - internal static delegate* unmanaged[Cdecl] PyByteArray_FromStringAndSize { get; } - internal static delegate* unmanaged[Cdecl] PyBytes_Size { get; } - internal static delegate* unmanaged[Cdecl] PyUnicode_AsUTF8 { get; } - internal static delegate* unmanaged[Cdecl] PyUnicode_DecodeUTF16 { get; } - internal static delegate* unmanaged[Cdecl] PyUnicode_GetLength { get; } - internal static delegate* unmanaged[Cdecl] PyUnicode_AsUnicode { get; } - internal static delegate* unmanaged[Cdecl] PyUnicode_AsUTF16String { get; } - internal static delegate* unmanaged[Cdecl] PyUnicode_FromOrdinal { get; } - internal static delegate* unmanaged[Cdecl] PyUnicode_InternFromString { get; } - internal static delegate* unmanaged[Cdecl] PyUnicode_Compare { get; } - internal static delegate* unmanaged[Cdecl] PyDict_New { get; } - internal static delegate* unmanaged[Cdecl] PyDict_GetItem { get; } - internal static delegate* unmanaged[Cdecl] PyDict_GetItemString { get; } - internal static delegate* unmanaged[Cdecl] PyDict_SetItem { get; } - internal static delegate* unmanaged[Cdecl] PyDict_SetItemString { get; } - internal static delegate* unmanaged[Cdecl] PyDict_DelItem { get; } - internal static delegate* unmanaged[Cdecl] PyDict_DelItemString { get; } - internal static delegate* unmanaged[Cdecl] PyMapping_HasKey { get; } - internal static delegate* unmanaged[Cdecl] PyDict_Keys { get; } - internal static delegate* unmanaged[Cdecl] PyDict_Values { get; } - internal static delegate* unmanaged[Cdecl] PyDict_Items { get; } - internal static delegate* unmanaged[Cdecl] PyDict_Copy { get; } - internal static delegate* unmanaged[Cdecl] PyDict_Update { get; } - internal static delegate* unmanaged[Cdecl] PyDict_Clear { get; } - internal static delegate* unmanaged[Cdecl] PyDict_Size { get; } - internal static delegate* unmanaged[Cdecl] PySet_New { get; } - internal static delegate* unmanaged[Cdecl] PySet_Add { get; } - internal static delegate* unmanaged[Cdecl] PySet_Contains { get; } - internal static delegate* unmanaged[Cdecl] PyList_New { get; } - internal static delegate* unmanaged[Cdecl] PyList_GetItem { get; } - internal static delegate* unmanaged[Cdecl] PyList_SetItem { get; } - internal static delegate* unmanaged[Cdecl] PyList_Insert { get; } - internal static delegate* unmanaged[Cdecl] PyList_Append { get; } - internal static delegate* unmanaged[Cdecl] PyList_Reverse { get; } - internal static delegate* unmanaged[Cdecl] PyList_Sort { get; } - internal static delegate* unmanaged[Cdecl] PyList_GetSlice { get; } - internal static delegate* unmanaged[Cdecl] PyList_SetSlice { get; } - internal static delegate* unmanaged[Cdecl] PyList_Size { get; } - internal static delegate* unmanaged[Cdecl] PyTuple_New { get; } - internal static delegate* unmanaged[Cdecl] PyTuple_GetItem { get; } - internal static delegate* unmanaged[Cdecl] PyTuple_SetItem { get; } - internal static delegate* unmanaged[Cdecl] PyTuple_GetSlice { get; } - internal static delegate* unmanaged[Cdecl] PyTuple_Size { get; } - internal static delegate* unmanaged[Cdecl] PyIter_Check { get; } - internal static delegate* unmanaged[Cdecl] PyIter_Next { get; } - internal static delegate* unmanaged[Cdecl] PyModule_New { get; } - internal static delegate* unmanaged[Cdecl] PyModule_GetDict { get; } - internal static delegate* unmanaged[Cdecl] PyModule_AddObject { get; } - internal static delegate* unmanaged[Cdecl] PyImport_Import { get; } - internal static delegate* unmanaged[Cdecl] PyImport_ImportModule { get; } - internal static delegate* unmanaged[Cdecl] PyImport_ReloadModule { get; } - internal static delegate* unmanaged[Cdecl] PyImport_AddModule { get; } - internal static delegate* unmanaged[Cdecl] PyImport_GetModuleDict { get; } - internal static delegate* unmanaged[Cdecl] PySys_SetArgvEx { get; } - internal static delegate* unmanaged[Cdecl] PySys_GetObject { get; } - internal static delegate* unmanaged[Cdecl] PySys_SetObject { get; } - internal static delegate* unmanaged[Cdecl] PyType_Modified { get; } - internal static delegate* unmanaged[Cdecl] PyType_IsSubtype { get; } - internal static delegate* unmanaged[Cdecl] PyType_GenericNew { get; } - internal static delegate* unmanaged[Cdecl] PyType_GenericAlloc { get; } - internal static delegate* unmanaged[Cdecl] PyType_Ready { get; } - internal static delegate* unmanaged[Cdecl] _PyType_Lookup { get; } - internal static delegate* unmanaged[Cdecl] PyObject_GenericGetAttr { get; } - internal static delegate* unmanaged[Cdecl] PyObject_GenericSetAttr { get; } - internal static delegate* unmanaged[Cdecl] PyObject_GC_Del { get; } - internal static delegate* unmanaged[Cdecl] PyObject_GC_IsTracked { get; } - internal static delegate* unmanaged[Cdecl] PyObject_GC_Track { get; } - internal static delegate* unmanaged[Cdecl] PyObject_GC_UnTrack { get; } - internal static delegate* unmanaged[Cdecl] _PyObject_Dump { get; } - internal static delegate* unmanaged[Cdecl] PyMem_Malloc { get; } - internal static delegate* unmanaged[Cdecl] PyMem_Realloc { get; } - internal static delegate* unmanaged[Cdecl] PyMem_Free { get; } - internal static delegate* unmanaged[Cdecl] PyErr_SetString { get; } - internal static delegate* unmanaged[Cdecl] PyErr_SetObject { get; } - internal static delegate* unmanaged[Cdecl] PyErr_ExceptionMatches { get; } - internal static delegate* unmanaged[Cdecl] PyErr_GivenExceptionMatches { get; } - internal static delegate* unmanaged[Cdecl] PyErr_NormalizeException { get; } - internal static delegate* unmanaged[Cdecl] PyErr_Occurred { get; } - internal static delegate* unmanaged[Cdecl] PyErr_Fetch { get; } - internal static delegate* unmanaged[Cdecl] PyErr_Restore { get; } - internal static delegate* unmanaged[Cdecl] PyErr_Clear { get; } - internal static delegate* unmanaged[Cdecl] PyErr_Print { get; } - internal static delegate* unmanaged[Cdecl] PyCell_Get { get; } - internal static delegate* unmanaged[Cdecl] PyCell_Set { get; } - internal static delegate* unmanaged[Cdecl] PyGC_Collect { get; } - internal static delegate* unmanaged[Cdecl] PyCapsule_New { get; } - internal static delegate* unmanaged[Cdecl] PyCapsule_GetPointer { get; } - internal static delegate* unmanaged[Cdecl] PyCapsule_SetPointer { get; } - internal static delegate* unmanaged[Cdecl] PyLong_AsUnsignedSize_t { get; } - internal static delegate* unmanaged[Cdecl] PyLong_AsSignedSize_t { get; } - internal static delegate* unmanaged[Cdecl] PyDict_GetItemWithError { get; } - internal static delegate* unmanaged[Cdecl] PyException_GetCause { get; } - internal static delegate* unmanaged[Cdecl] PyException_GetTraceback { get; } - internal static delegate* unmanaged[Cdecl] PyException_SetCause { get; } - internal static delegate* unmanaged[Cdecl] PyException_SetTraceback { get; } - internal static delegate* unmanaged[Cdecl] PyThreadState_SetAsyncExcLLP64 { get; } - internal static delegate* unmanaged[Cdecl] PyThreadState_SetAsyncExcLP64 { get; } - internal static delegate* unmanaged[Cdecl] PyObject_GenericGetDict { get; } - internal static delegate* unmanaged[Cdecl] PyType_GetSlot { get; } - internal static delegate* unmanaged[Cdecl] PyType_FromSpecWithBases { get; } - internal static delegate* unmanaged[Cdecl] _Py_NewReference { get; } - internal static delegate* unmanaged[Cdecl] _Py_IsFinalizing { get; } - internal static IntPtr PyType_Type { get; } - internal static int* Py_NoSiteFlag { get; } - } } internal class BadPythonDllException : MissingMethodException diff --git a/src/runtime/runtime_state.cs b/src/runtime/RuntimeState.cs similarity index 96% rename from src/runtime/runtime_state.cs rename to src/runtime/RuntimeState.cs index 2bb78094a..8defe6e64 100644 --- a/src/runtime/runtime_state.cs +++ b/src/runtime/RuntimeState.cs @@ -28,10 +28,10 @@ public static void Save() public static void Restore() { - ResotreModules(); + RestoreModules(); } - private static void ResotreModules() + private static void RestoreModules() { var intialModules = PySys_GetObject("initial_modules"); Debug.Assert(!intialModules.IsNull); diff --git a/src/runtime/runtime_data.cs b/src/runtime/StateSerialization/RuntimeData.cs similarity index 100% rename from src/runtime/runtime_data.cs rename to src/runtime/StateSerialization/RuntimeData.cs diff --git a/src/runtime/UnloadedClass.cs b/src/runtime/StateSerialization/UnloadedClass.cs similarity index 100% rename from src/runtime/UnloadedClass.cs rename to src/runtime/StateSerialization/UnloadedClass.cs diff --git a/src/runtime/typemanager.cs b/src/runtime/TypeManager.cs similarity index 99% rename from src/runtime/typemanager.cs rename to src/runtime/TypeManager.cs index cc2874c96..6057ca830 100644 --- a/src/runtime/typemanager.cs +++ b/src/runtime/TypeManager.cs @@ -609,7 +609,6 @@ internal static PyType AllocateTypeObject(string name, PyType metatype) /// static void InheritSubstructs(IntPtr type) { - #warning dead code? IntPtr substructAddress = type + TypeOffset.nb_add; Marshal.WriteIntPtr(type, TypeOffset.tp_as_number, substructAddress); diff --git a/src/runtime/arrayobject.cs b/src/runtime/Types/ArrayObject.cs similarity index 100% rename from src/runtime/arrayobject.cs rename to src/runtime/Types/ArrayObject.cs diff --git a/src/runtime/classbase.cs b/src/runtime/Types/ClassBase.cs similarity index 99% rename from src/runtime/classbase.cs rename to src/runtime/Types/ClassBase.cs index 028788742..2493fd970 100644 --- a/src/runtime/classbase.cs +++ b/src/runtime/Types/ClassBase.cs @@ -550,9 +550,9 @@ public virtual void InitializeSlots(BorrowedReference pyType, SlotsHolder slotsH TypeManager.InitializeSlotIfEmpty(pyType, TypeOffset.tp_iter, new Interop.B_N(tp_iter_impl), slotsHolder); } - if (mp_length_slot.CanAssign(type.Value)) + if (MpLengthSlot.CanAssign(type.Value)) { - TypeManager.InitializeSlotIfEmpty(pyType, TypeOffset.mp_length, new Interop.B_P(mp_length_slot.impl), slotsHolder); + TypeManager.InitializeSlotIfEmpty(pyType, TypeOffset.mp_length, new Interop.B_P(MpLengthSlot.impl), slotsHolder); } } diff --git a/src/runtime/classderived.cs b/src/runtime/Types/ClassDerived.cs similarity index 100% rename from src/runtime/classderived.cs rename to src/runtime/Types/ClassDerived.cs diff --git a/src/runtime/classobject.cs b/src/runtime/Types/ClassObject.cs similarity index 100% rename from src/runtime/classobject.cs rename to src/runtime/Types/ClassObject.cs diff --git a/src/runtime/clrobject.cs b/src/runtime/Types/ClrObject.cs similarity index 100% rename from src/runtime/clrobject.cs rename to src/runtime/Types/ClrObject.cs diff --git a/src/runtime/delegateobject.cs b/src/runtime/Types/DelegateObject.cs similarity index 100% rename from src/runtime/delegateobject.cs rename to src/runtime/Types/DelegateObject.cs diff --git a/src/runtime/eventbinding.cs b/src/runtime/Types/EventBinding.cs similarity index 100% rename from src/runtime/eventbinding.cs rename to src/runtime/Types/EventBinding.cs diff --git a/src/runtime/eventobject.cs b/src/runtime/Types/EventObject.cs similarity index 100% rename from src/runtime/eventobject.cs rename to src/runtime/Types/EventObject.cs diff --git a/src/runtime/Types/ExceptionClassObject.cs b/src/runtime/Types/ExceptionClassObject.cs new file mode 100644 index 000000000..ce0c0ff77 --- /dev/null +++ b/src/runtime/Types/ExceptionClassObject.cs @@ -0,0 +1,85 @@ +using System; + +namespace Python.Runtime; + +/// +/// Base class for Python types that reflect managed exceptions based on +/// System.Exception +/// +/// +/// The Python wrapper for managed exceptions LIES about its inheritance +/// tree. Although the real System.Exception is a subclass of +/// System.Object the Python type for System.Exception does NOT claim that +/// it subclasses System.Object. Instead TypeManager.CreateType() uses +/// Python's exception.Exception class as base class for System.Exception. +/// +[Serializable] +internal class ExceptionClassObject : ClassObject +{ + internal ExceptionClassObject(Type tp) : base(tp) + { + } + + internal static Exception? ToException(BorrowedReference ob) + { + var co = GetManagedObject(ob) as CLRObject; + return co?.inst as Exception; + } + + /// + /// Exception __repr__ implementation + /// + public new static NewReference tp_repr(BorrowedReference ob) + { + Exception? e = ToException(ob); + if (e == null) + { + return Exceptions.RaiseTypeError("invalid object"); + } + string name = e.GetType().Name; + string message; + if (e.Message != String.Empty) + { + message = String.Format("{0}('{1}')", name, e.Message); + } + else + { + message = String.Format("{0}()", name); + } + return Runtime.PyString_FromString(message); + } + + /// + /// Exception __str__ implementation + /// + public new static NewReference tp_str(BorrowedReference ob) + { + Exception? e = ToException(ob); + if (e == null) + { + return Exceptions.RaiseTypeError("invalid object"); + } + + string message = e.ToString(); + string fullTypeName = e.GetType().FullName; + string prefix = fullTypeName + ": "; + if (message.StartsWith(prefix)) + { + message = message.Substring(prefix.Length); + } + else if (message.StartsWith(fullTypeName)) + { + message = message.Substring(fullTypeName.Length); + } + return Runtime.PyString_FromString(message); + } + + public override bool Init(BorrowedReference obj, BorrowedReference args, BorrowedReference kw) + { + if (!base.Init(obj, args, kw)) return false; + + var e = (CLRObject)GetManagedObject(obj)!; + + return Exceptions.SetArgsAndCause(obj, (Exception)e.inst); + } +} diff --git a/src/runtime/extensiontype.cs b/src/runtime/Types/ExtensionType.cs similarity index 100% rename from src/runtime/extensiontype.cs rename to src/runtime/Types/ExtensionType.cs diff --git a/src/runtime/fieldobject.cs b/src/runtime/Types/FieldObject.cs similarity index 100% rename from src/runtime/fieldobject.cs rename to src/runtime/Types/FieldObject.cs diff --git a/src/runtime/generictype.cs b/src/runtime/Types/GenericType.cs similarity index 100% rename from src/runtime/generictype.cs rename to src/runtime/Types/GenericType.cs diff --git a/src/runtime/indexer.cs b/src/runtime/Types/Indexer.cs similarity index 100% rename from src/runtime/indexer.cs rename to src/runtime/Types/Indexer.cs diff --git a/src/runtime/interfaceobject.cs b/src/runtime/Types/InterfaceObject.cs similarity index 100% rename from src/runtime/interfaceobject.cs rename to src/runtime/Types/InterfaceObject.cs diff --git a/src/runtime/iterator.cs b/src/runtime/Types/Iterator.cs similarity index 100% rename from src/runtime/iterator.cs rename to src/runtime/Types/Iterator.cs diff --git a/src/runtime/managedtype.cs b/src/runtime/Types/ManagedType.cs similarity index 100% rename from src/runtime/managedtype.cs rename to src/runtime/Types/ManagedType.cs diff --git a/src/runtime/ManagedTypes.cd b/src/runtime/Types/ManagedTypes.cd similarity index 100% rename from src/runtime/ManagedTypes.cd rename to src/runtime/Types/ManagedTypes.cd diff --git a/src/runtime/metatype.cs b/src/runtime/Types/MetaType.cs similarity index 100% rename from src/runtime/metatype.cs rename to src/runtime/Types/MetaType.cs diff --git a/src/runtime/methodbinding.cs b/src/runtime/Types/MethodBinding.cs similarity index 100% rename from src/runtime/methodbinding.cs rename to src/runtime/Types/MethodBinding.cs diff --git a/src/runtime/methodobject.cs b/src/runtime/Types/MethodObject.cs similarity index 100% rename from src/runtime/methodobject.cs rename to src/runtime/Types/MethodObject.cs diff --git a/src/runtime/modulefunctionobject.cs b/src/runtime/Types/ModuleFunctionObject.cs similarity index 100% rename from src/runtime/modulefunctionobject.cs rename to src/runtime/Types/ModuleFunctionObject.cs diff --git a/src/runtime/moduleobject.cs b/src/runtime/Types/ModuleObject.cs similarity index 100% rename from src/runtime/moduleobject.cs rename to src/runtime/Types/ModuleObject.cs diff --git a/src/runtime/modulepropertyobject.cs b/src/runtime/Types/ModulePropertyObject.cs similarity index 100% rename from src/runtime/modulepropertyobject.cs rename to src/runtime/Types/ModulePropertyObject.cs diff --git a/src/runtime/slots/mp_length.cs b/src/runtime/Types/MpLengthSlot.cs similarity index 98% rename from src/runtime/slots/mp_length.cs rename to src/runtime/Types/MpLengthSlot.cs index 669285fe1..9e4865fe0 100644 --- a/src/runtime/slots/mp_length.cs +++ b/src/runtime/Types/MpLengthSlot.cs @@ -7,7 +7,7 @@ namespace Python.Runtime.Slots { - internal static class mp_length_slot + internal static class MpLengthSlot { public static bool CanAssign(Type clrType) { diff --git a/src/runtime/operatormethod.cs b/src/runtime/Types/OperatorMethod.cs similarity index 100% rename from src/runtime/operatormethod.cs rename to src/runtime/Types/OperatorMethod.cs diff --git a/src/runtime/overload.cs b/src/runtime/Types/OverloadMapper.cs similarity index 100% rename from src/runtime/overload.cs rename to src/runtime/Types/OverloadMapper.cs diff --git a/src/runtime/propertyobject.cs b/src/runtime/Types/PropertyObject.cs similarity index 100% rename from src/runtime/propertyobject.cs rename to src/runtime/Types/PropertyObject.cs diff --git a/src/runtime/ReflectedClrType.cs b/src/runtime/Types/ReflectedClrType.cs similarity index 100% rename from src/runtime/ReflectedClrType.cs rename to src/runtime/Types/ReflectedClrType.cs diff --git a/src/runtime/UnsafeReferenceWithRun.cs b/src/runtime/Types/UnsafeReferenceWithRun.cs similarity index 100% rename from src/runtime/UnsafeReferenceWithRun.cs rename to src/runtime/Types/UnsafeReferenceWithRun.cs diff --git a/src/runtime/codegenerator.cs b/src/runtime/Util/CodeGenerator.cs similarity index 100% rename from src/runtime/codegenerator.cs rename to src/runtime/Util/CodeGenerator.cs diff --git a/src/runtime/debughelper.cs b/src/runtime/Util/DebugUtil.cs similarity index 100% rename from src/runtime/debughelper.cs rename to src/runtime/Util/DebugUtil.cs diff --git a/src/runtime/EventHandlerCollection.cs b/src/runtime/Util/EventHandlerCollection.cs similarity index 100% rename from src/runtime/EventHandlerCollection.cs rename to src/runtime/Util/EventHandlerCollection.cs diff --git a/src/runtime/genericutil.cs b/src/runtime/Util/GenericUtil.cs similarity index 100% rename from src/runtime/genericutil.cs rename to src/runtime/Util/GenericUtil.cs diff --git a/src/runtime/tricks/InitOnly.cs b/src/runtime/Util/InitOnly.cs similarity index 100% rename from src/runtime/tricks/InitOnly.cs rename to src/runtime/Util/InitOnly.cs diff --git a/src/runtime/NonCopyableAttribute.cs b/src/runtime/Util/NonCopyableAttribute.cs similarity index 100% rename from src/runtime/NonCopyableAttribute.cs rename to src/runtime/Util/NonCopyableAttribute.cs diff --git a/src/runtime/tricks/NullOnly.cs b/src/runtime/Util/NullOnly.cs similarity index 100% rename from src/runtime/tricks/NullOnly.cs rename to src/runtime/Util/NullOnly.cs diff --git a/src/runtime/opshelper.cs b/src/runtime/Util/OpsHelper.cs similarity index 100% rename from src/runtime/opshelper.cs rename to src/runtime/Util/OpsHelper.cs diff --git a/src/runtime/Reflection/ParameterHelper.cs b/src/runtime/Util/ParameterHelper.cs similarity index 100% rename from src/runtime/Reflection/ParameterHelper.cs rename to src/runtime/Util/ParameterHelper.cs diff --git a/src/runtime/PythonReferenceComparer.cs b/src/runtime/Util/PythonReferenceComparer.cs similarity index 100% rename from src/runtime/PythonReferenceComparer.cs rename to src/runtime/Util/PythonReferenceComparer.cs diff --git a/src/runtime/polyfill/ReflectionPolyfills.cs b/src/runtime/Util/ReflectionPolyfills.cs similarity index 100% rename from src/runtime/polyfill/ReflectionPolyfills.cs rename to src/runtime/Util/ReflectionPolyfills.cs diff --git a/src/runtime/ReflectionUtil.cs b/src/runtime/Util/ReflectionUtil.cs similarity index 100% rename from src/runtime/ReflectionUtil.cs rename to src/runtime/Util/ReflectionUtil.cs diff --git a/src/runtime/Util.cs b/src/runtime/Util/Util.cs similarity index 100% rename from src/runtime/Util.cs rename to src/runtime/Util/Util.cs From c2945828a539ae427466b0785ea815eb76f52cd0 Mon Sep 17 00:00:00 2001 From: Benedikt Reinartz Date: Sat, 8 Jan 2022 22:30:08 +0100 Subject: [PATCH 24/75] Remove obsolete documented remark on Exception types --- src/runtime/Types/ExceptionClassObject.cs | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/runtime/Types/ExceptionClassObject.cs b/src/runtime/Types/ExceptionClassObject.cs index ce0c0ff77..762c9255a 100644 --- a/src/runtime/Types/ExceptionClassObject.cs +++ b/src/runtime/Types/ExceptionClassObject.cs @@ -6,13 +6,6 @@ namespace Python.Runtime; /// Base class for Python types that reflect managed exceptions based on /// System.Exception /// -/// -/// The Python wrapper for managed exceptions LIES about its inheritance -/// tree. Although the real System.Exception is a subclass of -/// System.Object the Python type for System.Exception does NOT claim that -/// it subclasses System.Object. Instead TypeManager.CreateType() uses -/// Python's exception.Exception class as base class for System.Exception. -/// [Serializable] internal class ExceptionClassObject : ClassObject { From f81b1c6b4bf6734fa2b1fe88ed9ed56d919b2a9f Mon Sep 17 00:00:00 2001 From: Victor Date: Sun, 9 Jan 2022 12:05:12 -0800 Subject: [PATCH 25/75] Add ARM64 CI action (#1669) --- .github/workflows/ARM.yml | 52 +++++++++++++++++++++ src/embed_tests/Python.EmbeddingTest.csproj | 4 +- 2 files changed, 54 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/ARM.yml diff --git a/.github/workflows/ARM.yml b/.github/workflows/ARM.yml new file mode 100644 index 000000000..cc56b7466 --- /dev/null +++ b/.github/workflows/ARM.yml @@ -0,0 +1,52 @@ +name: GitHub Actions + +on: [ push, pull_request ] + +jobs: + build-test-oracle: + name: Build and Test ARM64 + runs-on: [self-hosted, linux, ARM64] + timeout-minutes: 15 + + steps: + - name: Checkout code + uses: actions/checkout@v2 + + - name: Setup .NET + uses: actions/setup-dotnet@v1 + with: + dotnet-version: '6.0.x' + + - name: Clean previous install + run: | + pip uninstall -y pythonnet + + - name: Install dependencies + run: | + pip install --upgrade -r requirements.txt + pip install pytest numpy # for tests + + - name: Build and Install + run: | + pip install -v . + + - name: Set Python DLL path (non Windows) + run: | + python -m pythonnet.find_libpython --export >> $GITHUB_ENV + + - name: Embedding tests + run: dotnet test --logger "console;verbosity=detailed" src/embed_tests/ + + - name: Python Tests (Mono) + run: python -m pytest --runtime mono + + - name: Python Tests (.NET Core) + run: python -m pytest --runtime netcore + + - name: Python tests run from .NET + run: dotnet test src/python_tests_runner/ + + #- name: Perf tests + # run: | + # pip install --force --no-deps --target src/perf_tests/baseline/ pythonnet==2.5.2 + # dotnet test --configuration Release --logger "console;verbosity=detailed" src/perf_tests/ diff --git a/src/embed_tests/Python.EmbeddingTest.csproj b/src/embed_tests/Python.EmbeddingTest.csproj index a9c271f91..4993994d3 100644 --- a/src/embed_tests/Python.EmbeddingTest.csproj +++ b/src/embed_tests/Python.EmbeddingTest.csproj @@ -20,11 +20,11 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive - + 1.0.0 all From 75f4398174274e44a7ef8b2538fe3aa7e20406d1 Mon Sep 17 00:00:00 2001 From: Benedikt Reinartz Date: Sun, 9 Jan 2022 22:14:03 +0100 Subject: [PATCH 26/75] Only run CI on pushes to master and on all pull requests (#1670) Fixes #1668 --- .github/workflows/ARM.yml | 10 +++++++--- .github/workflows/main.yml | 8 ++++++-- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ARM.yml b/.github/workflows/ARM.yml index cc56b7466..66f68366d 100644 --- a/.github/workflows/ARM.yml +++ b/.github/workflows/ARM.yml @@ -1,9 +1,13 @@ -name: GitHub Actions +name: Main (ARM) -on: [ push, pull_request ] +on: + push: + branches: + - master + pull_request: jobs: - build-test-oracle: + build-test-arm: name: Build and Test ARM64 runs-on: [self-hosted, linux, ARM64] timeout-minutes: 15 diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index fc9312f58..218796192 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -1,6 +1,10 @@ -name: GitHub Actions +name: Main (x64) -on: [ pull_request, push ] +on: + push: + branches: + - master + pull_request: jobs: build-test: From f69753c701ea44ba465c2a43c194663270349573 Mon Sep 17 00:00:00 2001 From: Ehsan Iran-Nejad Date: Thu, 13 Jan 2022 18:55:42 -0800 Subject: [PATCH 27/75] allow Python not passing fake arguments for out parameters (#1672) --- AUTHORS.md | 1 + CHANGELOG.md | 2 ++ src/runtime/MethodBinder.cs | 8 ++++++++ tests/test_method.py | 38 +++++++++++++++++++++++++++++++++++++ 4 files changed, 49 insertions(+) diff --git a/AUTHORS.md b/AUTHORS.md index 912831836..92f1a4a97 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -84,3 +84,4 @@ - ([@DanBarzilian](https://github.com/DanBarzilian)) - ([@alxnull](https://github.com/alxnull)) - ([@gpetrou](https://github.com/gpetrou)) +- Ehsan Iran-Nejad ([@eirannejad](https://github.com/eirannejad)) diff --git a/CHANGELOG.md b/CHANGELOG.md index cf64c3a64..60548ae10 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -109,6 +109,7 @@ Instead, `PyIterable` does that. - Empty parameter names (as can be generated from F#) do not cause crashes - Unicode strings with surrogates were truncated when converting from Python - `Reload` mode now supports generic methods (previously Python would stop seeing them after reload) +- Temporarily fixed issue resolving method overload when method signature has `out` parameters ([#1672](i1672)) ### Removed @@ -881,3 +882,4 @@ This version improves performance on benchmarks significantly compared to 2.3. [i1342]: https://github.com/pythonnet/pythonnet/issues/1342 [i238]: https://github.com/pythonnet/pythonnet/issues/238 [i1481]: https://github.com/pythonnet/pythonnet/issues/1481 +[i1672]: https://github.com/pythonnet/pythonnet/pull/1672 \ No newline at end of file diff --git a/src/runtime/MethodBinder.cs b/src/runtime/MethodBinder.cs index 42d3822fc..8b9ee9c00 100644 --- a/src/runtime/MethodBinder.cs +++ b/src/runtime/MethodBinder.cs @@ -632,6 +632,11 @@ static BorrowedReference HandleParamsArray(BorrowedReference args, int arrayStar margs[paramIndex] = defaultArgList[paramIndex - pyArgCount]; } + if (parameter.ParameterType.IsByRef) + { + outs++; + } + continue; } @@ -817,6 +822,9 @@ static bool MatchesArgumentCount(int positionalArgumentCount, ParameterInfo[] pa defaultArgList.Add(parameters[v].GetDefaultValue()); defaultsNeeded++; } + else if (parameters[v].IsOut) { + defaultArgList.Add(null); + } else if (!paramsArray) { match = false; diff --git a/tests/test_method.py b/tests/test_method.py index e81652b54..e2d8d5b06 100644 --- a/tests/test_method.py +++ b/tests/test_method.py @@ -280,6 +280,16 @@ def test_string_out_params(): assert result[1] == "output string" +def test_string_out_params_without_passing_string_value(): + """Test use of string out-parameters.""" + # @eirannejad 2022-01-13 + result = MethodTest.TestStringOutParams("hi") + assert isinstance(result, tuple) + assert len(result) == 2 + assert result[0] is True + assert result[1] == "output string" + + def test_string_ref_params(): """Test use of string byref parameters.""" result = MethodTest.TestStringRefParams("hi", "there") @@ -308,6 +318,16 @@ def test_value_out_params(): MethodTest.TestValueOutParams("hi", None) +def test_value_out_params_without_passing_string_value(): + """Test use of string out-parameters.""" + # @eirannejad 2022-01-13 + result = MethodTest.TestValueOutParams("hi") + assert isinstance(result, tuple) + assert len(result) == 2 + assert result[0] is True + assert result[1] == 42 + + def test_value_ref_params(): """Test use of value type byref parameters.""" result = MethodTest.TestValueRefParams("hi", 1) @@ -336,6 +356,15 @@ def test_object_out_params(): assert isinstance(result[1], System.Exception) +def test_object_out_params_without_passing_string_value(): + """Test use of object out-parameters.""" + result = MethodTest.TestObjectOutParams("hi") + assert isinstance(result, tuple) + assert len(result) == 2 + assert result[0] is True + assert isinstance(result[1], System.Exception) + + def test_object_ref_params(): """Test use of object byref parameters.""" result = MethodTest.TestObjectRefParams("hi", MethodTest()) @@ -364,6 +393,15 @@ def test_struct_out_params(): MethodTest.TestValueRefParams("hi", None) +def test_struct_out_params_without_passing_string_value(): + """Test use of struct out-parameters.""" + result = MethodTest.TestStructOutParams("hi") + assert isinstance(result, tuple) + assert len(result) == 2 + assert result[0] is True + assert isinstance(result[1], System.Guid) + + def test_struct_ref_params(): """Test use of struct byref parameters.""" result = MethodTest.TestStructRefParams("hi", System.Guid.NewGuid()) From b7fb03a62b6b27ff3fd7a09be19ca11ef567c9ca Mon Sep 17 00:00:00 2001 From: Ehsan Iran-Nejad Date: Fri, 14 Jan 2022 18:09:01 -0800 Subject: [PATCH 28/75] added todo note to ensure CLA is signed --- .github/PULL_REQUEST_TEMPLATE.md | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 8b030e040..5cb8566e2 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -16,5 +16,6 @@ Check all those that are applicable and complete. - [ ] Make sure to include one or more tests for your change - [ ] If an enhancement PR, please create docs and at best an example +- [ ] Ensure you have signed the [.NET Foundation CLA](https://cla.dotnetfoundation.org/pythonnet/pythonnet) - [ ] Add yourself to [`AUTHORS`](../blob/master/AUTHORS.md) - [ ] Updated the [`CHANGELOG`](../blob/master/CHANGELOG.md) From ca1a72b1bd7ed9771189cf3dd99322bcb9420e81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20Bourbonnais?= Date: Mon, 24 Jan 2022 10:04:44 -0500 Subject: [PATCH 29/75] Add tests for exception leaking. Originally from PR #1402. The underlying bug is now fixed, but the tests are atill applicable. --- src/testing/exceptiontest.cs | 35 +++++++++++++++++ tests/test_exceptions.py | 75 ++++++++++++++++++++++++++++++++++++ 2 files changed, 110 insertions(+) diff --git a/src/testing/exceptiontest.cs b/src/testing/exceptiontest.cs index 45acaa2cc..0b72e0014 100644 --- a/src/testing/exceptiontest.cs +++ b/src/testing/exceptiontest.cs @@ -1,3 +1,4 @@ +using Python.Runtime; using System; using System.Collections; using System.Collections.Generic; @@ -81,6 +82,40 @@ public static void ThrowChainedExceptions() throw new Exception("Outer exception", exc2); } } + + public static IntPtr DoThrowSimple() + { + using (Py.GIL()) + { + dynamic builtins = Py.Import("builtins"); + var typeErrorType = new PyType(builtins.TypeError); + var pyerr = new PythonException(typeErrorType, value:null, traceback:null, "Type error, the first", innerException:null); + throw new ArgumentException("Bogus bad parameter", pyerr); + + } + } + + public static void DoThrowWithInner() + { + using(Py.GIL()) + { + // create a TypeError + dynamic builtins = Py.Import("builtins"); + var pyerrFirst = new PythonException(new PyType(builtins.TypeError), value:null, traceback:null, "Type error, the first", innerException:null); + + // Create an ArgumentException, but as a python exception, with the previous type error as the inner exception + var argExc = new ArgumentException("Bogus bad parameter", pyerrFirst); + var argExcPyObj = argExc.ToPython(); + var pyArgExc = new PythonException(argExcPyObj.GetPythonType(), value:null, traceback:null, argExc.Message, innerException:argExc.InnerException); + // This object must be disposed explicitly or else we get a false-positive leak. + argExcPyObj.Dispose(); + + // Then throw a TypeError with the ArgumentException-as-python-error exception as inner. + var pyerrSecond = new PythonException(new PyType(builtins.TypeError), value:null, traceback:null, "Type error, the second", innerException:pyArgExc); + throw pyerrSecond; + + } + } } diff --git a/tests/test_exceptions.py b/tests/test_exceptions.py index 7fafeebcb..469934fe5 100644 --- a/tests/test_exceptions.py +++ b/tests/test_exceptions.py @@ -8,6 +8,51 @@ import pytest import pickle +# begin code from https://utcc.utoronto.ca/~cks/space/blog/python/GetAllObjects +import gc +# Recursively expand slist's objects +# into olist, using seen to track +# already processed objects. + +def _getr(slist, olist, seen): + for e in slist: + if id(e) in seen: + continue + seen[id(e)] = None + olist.append(e) + tl = gc.get_referents(e) + if tl: + _getr(tl, olist, seen) + +# The public function. +def get_all_objects(): + gcl = gc.get_objects() + olist = [] + seen = {} + # Just in case: + seen[id(gcl)] = None + seen[id(olist)] = None + seen[id(seen)] = None + # _getr does the real work. + _getr(gcl, olist, seen) + return olist +# end code from https://utcc.utoronto.ca/~cks/space/blog/python/GetAllObjects + +def leak_check(func): + def do_leak_check(): + func() + gc.collect() + exc = {x for x in get_all_objects() if isinstance(x, Exception) and not isinstance(x, pytest.PytestDeprecationWarning)} + print(len(exc)) + if len(exc): + for x in exc: + print('-------') + print(repr(x)) + print(gc.get_referrers(x)) + print(len(gc.get_referrers(x))) + assert False + gc.collect() + return do_leak_check def test_unified_exception_semantics(): """Test unified exception semantics.""" @@ -375,3 +420,33 @@ def test_iteration_innerexception(): # after exception is thrown iterator is no longer valid with pytest.raises(StopIteration): next(val) + +def leak_test(func): + def do_test_leak(): + # PyTest leaks things, gather the current state + orig_exc = {x for x in get_all_objects() if isinstance(x, Exception)} + func() + exc = {x for x in get_all_objects() if isinstance(x, Exception)} + possibly_leaked = exc - orig_exc + assert not possibly_leaked + + return do_test_leak + +@leak_test +def test_dont_leak_exceptions_simple(): + from Python.Test import ExceptionTest + + try: + ExceptionTest.DoThrowSimple() + except System.ArgumentException: + print('type error, as expected') + +@leak_test +def test_dont_leak_exceptions_inner(): + from Python.Test import ExceptionTest + try: + ExceptionTest.DoThrowWithInner() + except TypeError: + print('type error, as expected') + except System.ArgumentException: + print('type error, also expected') \ No newline at end of file From dd5a8de82fe259a28047bf52b7237f2f6326feef Mon Sep 17 00:00:00 2001 From: Benedikt Reinartz Date: Fri, 28 Jan 2022 12:04:26 +0100 Subject: [PATCH 30/75] We can drop the import hack as we are now using the newer import mechanics --- src/runtime/PythonEngine.cs | 31 ------------------------------- 1 file changed, 31 deletions(-) diff --git a/src/runtime/PythonEngine.cs b/src/runtime/PythonEngine.cs index 5223bb089..1e82446cb 100644 --- a/src/runtime/PythonEngine.cs +++ b/src/runtime/PythonEngine.cs @@ -321,37 +321,6 @@ public static IntPtr InitExt() Initialize(setSysArgv: false); Finalizer.Instance.ErrorHandler += AllowLeaksDuringShutdown; - - // Trickery - when the import hook is installed into an already - // running Python, the standard import machinery is still in - // control for the duration of the import that caused bootstrap. - // - // That is problematic because the std machinery tries to get - // sub-names directly from the module __dict__ rather than going - // through our module object's getattr hook. This workaround is - // evil ;) We essentially climb up the stack looking for the - // import that caused the bootstrap to happen, then re-execute - // the import explicitly after our hook has been installed. By - // doing this, the original outer import should work correctly. - // - // Note that this is only needed during the execution of the - // first import that installs the CLR import hook. This hack - // still doesn't work if you use the interactive interpreter, - // since there is no line info to get the import line ;( - - string code = - "import traceback\n" + - "for item in traceback.extract_stack():\n" + - " line = item[3]\n" + - " if line is not None:\n" + - " if line.startswith('import CLR') or \\\n" + - " line.startswith('import clr') or \\\n" + - " line.startswith('from clr') or \\\n" + - " line.startswith('from CLR'):\n" + - " exec(line)\n" + - " break\n"; - - PythonEngine.Exec(code); } catch (PythonException e) { From dd3302a90e4915b96f213fd2095a73e141548602 Mon Sep 17 00:00:00 2001 From: nobbi1991 <48419518+nobbi1991@users.noreply.github.com> Date: Mon, 31 Jan 2022 12:32:35 +0100 Subject: [PATCH 31/75] Update CHANGELOG.md (#1690) added missing backticks --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 60548ae10..382f9ab57 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -38,7 +38,7 @@ details about the cause of the failure - BREAKING: Return values from .NET methods that return an interface are now automatically wrapped in that interface. This is a breaking change for users that rely on being able to access members that are part of the implementation class, but not the - interface. Use the new __implementation__ or __raw_implementation__ properties to + interface. Use the new `__implementation__` or `__raw_implementation__` properties to if you need to "downcast" to the implementation class. - BREAKING: Parameters marked with `ParameterAttributes.Out` are no longer returned in addition to the regular method return value (unless they are passed with `ref` or `out` keyword). @@ -882,4 +882,4 @@ This version improves performance on benchmarks significantly compared to 2.3. [i1342]: https://github.com/pythonnet/pythonnet/issues/1342 [i238]: https://github.com/pythonnet/pythonnet/issues/238 [i1481]: https://github.com/pythonnet/pythonnet/issues/1481 -[i1672]: https://github.com/pythonnet/pythonnet/pull/1672 \ No newline at end of file +[i1672]: https://github.com/pythonnet/pythonnet/pull/1672 From ad0d4e7940b43882bcbdc3d683b7d3d8610ed05a Mon Sep 17 00:00:00 2001 From: Victor Nova Date: Thu, 17 Feb 2022 10:17:56 -0800 Subject: [PATCH 32/75] allow dynamic PyObject conversion to IEnumerable when the object is a Python iterable this enables foreach loops over dynamic PyObject instances closes https://github.com/pythonnet/pythonnet/issues/1680 --- src/embed_tests/dynamic.cs | 11 +++++++++++ src/runtime/PythonTypes/PyObject.cs | 6 ++++++ 2 files changed, 17 insertions(+) diff --git a/src/embed_tests/dynamic.cs b/src/embed_tests/dynamic.cs index 827782a5c..0a181231c 100644 --- a/src/embed_tests/dynamic.cs +++ b/src/embed_tests/dynamic.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Text; using NUnit.Framework; using Python.Runtime; @@ -126,5 +127,15 @@ public void PassPyObjectInNet() // Compare in .NET Assert.IsTrue(sys.testattr1.Equals(sys.testattr2)); } + + // regression test for https://github.com/pythonnet/pythonnet/issues/1680 + [Test] + public void ForEach() + { + dynamic pyList = PythonEngine.Eval("[1,2,3]"); + var list = new List(); + foreach (int item in pyList) + list.Add(item); + } } } diff --git a/src/runtime/PythonTypes/PyObject.cs b/src/runtime/PythonTypes/PyObject.cs index 373751cf6..e26831490 100644 --- a/src/runtime/PythonTypes/PyObject.cs +++ b/src/runtime/PythonTypes/PyObject.cs @@ -1296,6 +1296,12 @@ public override bool TryConvert(ConvertBinder binder, out object? result) return converted; } + if (binder.Type == typeof(System.Collections.IEnumerable) && this.IsIterable()) + { + result = new PyIterable(this.Reference); + return true; + } + return false; } From 76378845d6749a31ea9de187db634c48c2e82a9f Mon Sep 17 00:00:00 2001 From: Victor Nova Date: Mon, 28 Feb 2022 15:38:31 -0800 Subject: [PATCH 33/75] removed dead code --- src/runtime/Runtime.cs | 25 ------------------------- 1 file changed, 25 deletions(-) diff --git a/src/runtime/Runtime.cs b/src/runtime/Runtime.cs index 7806c3156..e33c4624c 100644 --- a/src/runtime/Runtime.cs +++ b/src/runtime/Runtime.cs @@ -365,31 +365,6 @@ private static Lazy GetModuleLazy(string moduleName) ? throw new ArgumentNullException(nameof(moduleName)) : new Lazy(() => PyModule.Import(moduleName), isThreadSafe: false); - private static void RunExitFuncs() - { - PyObject atexit; - try - { - atexit = Py.Import("atexit"); - } - catch (PythonException e) when (e.Is(Exceptions.ImportError)) - { - // The runtime may not provided `atexit` module. - return; - } - using (atexit) - { - try - { - atexit.InvokeMethod("_run_exitfuncs").Dispose(); - } - catch (PythonException e) - { - Console.Error.WriteLine(e); - } - } - } - private static void SetPyMember(out PyObject obj, StolenReference value) { // XXX: For current usages, value should not be null. From 303378a657d8f45a7a896d1ad485450959c8dd5f Mon Sep 17 00:00:00 2001 From: Andrii Oriekhov Date: Thu, 3 Mar 2022 15:42:39 +0200 Subject: [PATCH 34/75] add GitHub URL for PyPi (#1708) --- setup.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/setup.py b/setup.py index 58e177262..527bcc893 100644 --- a/setup.py +++ b/setup.py @@ -147,6 +147,9 @@ def finalize_options(self): version="3.0.0.dev1", description=".Net and Mono integration for Python", url="https://pythonnet.github.io/", + project_urls={ + "Source": "https://github.com/pythonnet/pythonnet", + }, license="MIT", author="The Contributors of the Python.NET Project", author_email="pythonnet@python.org", From eec30c062f93c8555bcff9ec3bb14ccc832f36df Mon Sep 17 00:00:00 2001 From: Victor Nova Date: Thu, 3 Mar 2022 14:18:17 -0800 Subject: [PATCH 35/75] make methods of PyObject inherited from its base C# classes GIL-safe fixes https://github.com/pythonnet/pythonnet/issues/1642 --- CHANGELOG.md | 2 ++ src/embed_tests/Codecs.cs | 2 +- src/embed_tests/TestPyObject.cs | 7 +++++++ src/runtime/InternString.cs | 2 +- src/runtime/Py.cs | 10 +--------- src/runtime/PythonTypes/PyObject.cs | 21 ++++++++++++++++----- 6 files changed, 28 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 382f9ab57..20b303dc6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -47,6 +47,7 @@ details about the cause of the failure - floating point values passed from Python are no longer silently truncated when .NET expects an integer [#1342][i1342] - More specific error messages for method argument mismatch +- members of `PyObject` inherited from `System.Object and `DynamicObject` now autoacquire GIL - BREAKING: when inheriting from .NET types in Python if you override `__init__` you must explicitly call base constructor using `super().__init__(.....)`. Not doing so will lead to undefined behavior. @@ -69,6 +70,7 @@ One must now either use enum members (e.g. `MyEnum.Option`), or use enum constru - BREAKING: Names of .NET types (e.g. `str(__class__)`) changed to better support generic types - BREAKING: overload resolution will no longer prefer basic types. Instead, first matching overload will be chosen. +- BREAKING: acquiring GIL using `Py.GIL` no longer forces `PythonEngine` to initialize - BREAKING: `Exec` and `Eval` from `PythonEngine` no longer accept raw pointers. - BREAKING: .NET collections and arrays are no longer automatically converted to Python collections. Instead, they implement standard Python diff --git a/src/embed_tests/Codecs.cs b/src/embed_tests/Codecs.cs index a87b287bc..c9e83f03a 100644 --- a/src/embed_tests/Codecs.cs +++ b/src/embed_tests/Codecs.cs @@ -473,7 +473,7 @@ public DecoderReturningPredefinedValue(PyObject objectType, TTarget decodeResult } public bool CanDecode(PyType objectType, Type targetType) - => objectType.Handle == TheOnlySupportedSourceType.Handle + => PythonReferenceComparer.Instance.Equals(objectType, TheOnlySupportedSourceType) && targetType == typeof(TTarget); public bool TryDecode(PyObject pyObj, out T value) { diff --git a/src/embed_tests/TestPyObject.cs b/src/embed_tests/TestPyObject.cs index fa5fa38c7..2f27eba1b 100644 --- a/src/embed_tests/TestPyObject.cs +++ b/src/embed_tests/TestPyObject.cs @@ -94,6 +94,13 @@ public void GetAttrDefault_IgnoresAttributeErrorOnly() ); Assert.AreEqual(Exceptions.TypeError, typeErrResult.Type); } + + // regression test from https://github.com/pythonnet/pythonnet/issues/1642 + [Test] + public void InheritedMethodsAutoacquireGIL() + { + PythonEngine.Exec("from System import String\nString.Format('{0},{1}', 1, 2)"); + } } public class PyObjectTestMethods diff --git a/src/runtime/InternString.cs b/src/runtime/InternString.cs index a479f3732..0780a0bb8 100644 --- a/src/runtime/InternString.cs +++ b/src/runtime/InternString.cs @@ -39,7 +39,7 @@ public static void Initialize() { NewReference pyStr = Runtime.PyUnicode_InternFromString(name); var op = new PyString(pyStr.StealOrThrow()); - Debug.Assert(name == op.ToString()); + Debug.Assert(name == op.As()); SetIntern(name, op); var field = type.GetField("f" + name, PyIdentifierFieldFlags)!; field.SetValue(null, op.rawPtr); diff --git a/src/runtime/Py.cs b/src/runtime/Py.cs index 7a2369413..4f3fbf6d4 100644 --- a/src/runtime/Py.cs +++ b/src/runtime/Py.cs @@ -10,15 +10,7 @@ namespace Python.Runtime; public static class Py { - public static GILState GIL() - { - if (!PythonEngine.IsInitialized) - { - PythonEngine.Initialize(); - } - - return PythonEngine.DebugGIL ? new DebugGILState() : new GILState(); - } + public static GILState GIL() => PythonEngine.DebugGIL ? new DebugGILState() : new GILState(); public static PyModule CreateScope() => new(); public static PyModule CreateScope(string name) diff --git a/src/runtime/PythonTypes/PyObject.cs b/src/runtime/PythonTypes/PyObject.cs index e26831490..e0a17bed5 100644 --- a/src/runtime/PythonTypes/PyObject.cs +++ b/src/runtime/PythonTypes/PyObject.cs @@ -1056,6 +1056,7 @@ public PyList Dir() /// public override string? ToString() { + using var _ = Py.GIL(); using var strval = Runtime.PyObject_Str(obj); return Runtime.GetManagedString(strval.BorrowOrThrow()); } @@ -1072,7 +1073,11 @@ public PyList Dir() /// Return true if this object is equal to the given object. This /// method is based on Python equality semantics. /// - public override bool Equals(object o) => Equals(o as PyObject); + public override bool Equals(object o) + { + using var _ = Py.GIL(); + return Equals(o as PyObject); + } public virtual bool Equals(PyObject? other) { @@ -1101,6 +1106,7 @@ public virtual bool Equals(PyObject? other) /// public override int GetHashCode() { + using var _ = Py.GIL(); nint pyHash = Runtime.PyObject_Hash(obj); if (pyHash == -1 && Exceptions.ErrorOccurred()) { @@ -1135,12 +1141,14 @@ public long Refcount public override bool TryGetMember(GetMemberBinder binder, out object? result) { + using var _ = Py.GIL(); result = CheckNone(this.GetAttr(binder.Name)); return true; } public override bool TrySetMember(SetMemberBinder binder, object? value) { + using var _ = Py.GIL(); using var newVal = Converter.ToPythonDetectType(value); int r = Runtime.PyObject_SetAttrString(obj, binder.Name, newVal.Borrow()); if (r < 0) @@ -1234,6 +1242,7 @@ private static NewReference GetPythonObject(object? target) public override bool TryInvokeMember(InvokeMemberBinder binder, object?[] args, out object? result) { + using var _ = Py.GIL(); if (this.HasAttr(binder.Name) && this.GetAttr(binder.Name).IsCallable()) { PyTuple? pyargs = null; @@ -1258,6 +1267,7 @@ public override bool TryInvokeMember(InvokeMemberBinder binder, object?[] args, public override bool TryInvoke(InvokeBinder binder, object?[] args, out object? result) { + using var _ = Py.GIL(); if (this.IsCallable()) { PyTuple? pyargs = null; @@ -1282,6 +1292,7 @@ public override bool TryInvoke(InvokeBinder binder, object?[] args, out object? public override bool TryConvert(ConvertBinder binder, out object? result) { + using var _ = Py.GIL(); // always try implicit conversion first if (Converter.ToManaged(this.obj, binder.Type, out result, false)) { @@ -1307,6 +1318,7 @@ public override bool TryConvert(ConvertBinder binder, out object? result) public override bool TryBinaryOperation(BinaryOperationBinder binder, object arg, out object? result) { + using var _ = Py.GIL(); NewReference res; if (!(arg is PyObject)) { @@ -1419,6 +1431,7 @@ public override bool TryBinaryOperation(BinaryOperationBinder binder, object arg public override bool TryUnaryOperation(UnaryOperationBinder binder, out object? result) { + using var _ = Py.GIL(); int r; NewReference res; switch (binder.Operation) @@ -1463,10 +1476,8 @@ public override bool TryUnaryOperation(UnaryOperationBinder binder, out object? /// A sequence that contains dynamic member names. public override IEnumerable GetDynamicMemberNames() { - foreach (PyObject pyObj in Dir()) - { - yield return pyObj.ToString()!; - } + using var _ = Py.GIL(); + return Dir().Select(pyObj => pyObj.ToString()!).ToArray(); } void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context) From 5e041afadbb119bd2816bd483adf62a347b9c639 Mon Sep 17 00:00:00 2001 From: Victor Nova Date: Fri, 4 Mar 2022 10:09:25 -0800 Subject: [PATCH 36/75] on runtime shutdown from Python release all slot holders, not only the ones, that belong to managed types --- src/runtime/TypeManager.cs | 16 ++++++++++------ src/runtime/Types/ModuleObject.cs | 1 - tests/test_engine.py | 4 ++++ 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/src/runtime/TypeManager.cs b/src/runtime/TypeManager.cs index 6057ca830..4b1e28fc2 100644 --- a/src/runtime/TypeManager.cs +++ b/src/runtime/TypeManager.cs @@ -51,18 +51,21 @@ internal static void Initialize() internal static void RemoveTypes() { - foreach (var type in cache.Values) + if (Runtime.HostedInPython) { - if (Runtime.HostedInPython - && _slotsHolders.TryGetValue(type, out var holder)) + foreach (var holder in _slotsHolders) { // If refcount > 1, it needs to reset the managed slot, // otherwise it can dealloc without any trick. - if (Runtime.Refcount(type) > 1) + if (holder.Key.Refcount > 1) { - holder.ResetSlots(); + holder.Value.ResetSlots(); } } + } + + foreach (var type in cache.Values) + { type.Dispose(); } cache.Clear(); @@ -507,7 +510,7 @@ internal static PyType CreateMetaType(Type impl, out SlotsHolder slotsHolder) { throw PythonException.ThrowLastAsClrException(); } - + BorrowedReference dict = Util.ReadRef(type, TypeOffset.tp_dict); using (var mod = Runtime.PyString_FromString("clr._internal")) Runtime.PyDict_SetItemString(dict, "__module__", mod.Borrow()); @@ -726,6 +729,7 @@ internal static void CopySlot(BorrowedReference from, BorrowedReference to, int internal static SlotsHolder CreateSlotsHolder(PyType type) { + type = new PyType(type); var holder = new SlotsHolder(type); _slotsHolders.Add(type, holder); return holder; diff --git a/src/runtime/Types/ModuleObject.cs b/src/runtime/Types/ModuleObject.cs index 1e86d4472..1cc9f04b2 100644 --- a/src/runtime/Types/ModuleObject.cs +++ b/src/runtime/Types/ModuleObject.cs @@ -542,7 +542,6 @@ public static Assembly AddReference(string name) /// The Type object [ModuleFunction] - [ForbidPythonThreads] public static Type GetClrType(Type type) { return type; diff --git a/tests/test_engine.py b/tests/test_engine.py index 60fdbf45d..06a44d561 100644 --- a/tests/test_engine.py +++ b/tests/test_engine.py @@ -41,3 +41,7 @@ def test_run_string(): assert sys.multiline_worked == 1 PythonEngine.ReleaseLock() + +def test_leak_type(): + import clr + sys._leaked_intptr = clr.GetClrType(System.IntPtr) From e9cbb87073e75500b3392ad057c06a132fb5ed3c Mon Sep 17 00:00:00 2001 From: Victor Nova Date: Thu, 7 Apr 2022 15:55:58 -0700 Subject: [PATCH 37/75] do not attempt to manually delete derived class instances after TypeManager is terminated and Python is shutting down --- pythonnet.sln | 1 + src/runtime/Runtime.cs | 9 ++++++--- src/runtime/TypeManager.cs | 1 + src/runtime/Types/ClassDerived.cs | 20 ++++++++++---------- 4 files changed, 18 insertions(+), 13 deletions(-) diff --git a/pythonnet.sln b/pythonnet.sln index 3b509518f..eb97cfbd0 100644 --- a/pythonnet.sln +++ b/pythonnet.sln @@ -25,6 +25,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Repo", "Repo", "{441A0123-F EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "CI", "CI", "{D301657F-5EAF-4534-B280-B858D651B2E5}" ProjectSection(SolutionItems) = preProject + .github\workflows\ARM.yml = .github\workflows\ARM.yml .github\workflows\main.yml = .github\workflows\main.yml .github\workflows\nuget-preview.yml = .github\workflows\nuget-preview.yml EndProjectSection diff --git a/src/runtime/Runtime.cs b/src/runtime/Runtime.cs index e33c4624c..e358c0135 100644 --- a/src/runtime/Runtime.cs +++ b/src/runtime/Runtime.cs @@ -54,8 +54,9 @@ private static string GetDefaultDllName(Version version) } private static bool _isInitialized = false; - internal static bool IsInitialized => _isInitialized; + private static bool _typesInitialized = false; + internal static bool TypeManagerInitialized => _typesInitialized; internal static readonly bool Is32Bit = IntPtr.Size == 4; // .NET core: System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(OSPlatform.Windows) @@ -151,6 +152,7 @@ internal static void Initialize(bool initSigs = false) ClassManager.Reset(); ClassDerivedObject.Reset(); TypeManager.Initialize(); + _typesInitialized = true; // Initialize modules that depend on the runtime class. AssemblyManager.Initialize(); @@ -272,6 +274,7 @@ internal static void Shutdown() NullGCHandles(ExtensionType.loadedExtensions); ClassManager.RemoveClasses(); TypeManager.RemoveTypes(); + _typesInitialized = false; MetaType.Release(); PyCLRMetaType.Dispose(); @@ -291,9 +294,10 @@ internal static void Shutdown() Finalizer.Shutdown(); InternString.Shutdown(); + ResetPyMembers(); + if (!HostedInPython) { - ResetPyMembers(); GC.Collect(); GC.WaitForPendingFinalizers(); PyGILState_Release(state); @@ -310,7 +314,6 @@ internal static void Shutdown() } else { - ResetPyMembers(); PyGILState_Release(state); } } diff --git a/src/runtime/TypeManager.cs b/src/runtime/TypeManager.cs index 4b1e28fc2..84618df64 100644 --- a/src/runtime/TypeManager.cs +++ b/src/runtime/TypeManager.cs @@ -832,6 +832,7 @@ public void ResetSlots() var metatype = Runtime.PyObject_TYPE(Type); ManagedType.TryFreeGCHandle(Type, metatype); } + Runtime.PyType_Modified(Type); } public static IntPtr GetDefaultSlot(int offset) diff --git a/src/runtime/Types/ClassDerived.cs b/src/runtime/Types/ClassDerived.cs index da1bf0f9a..6c2c81b13 100644 --- a/src/runtime/Types/ClassDerived.cs +++ b/src/runtime/Types/ClassDerived.cs @@ -59,10 +59,7 @@ protected override NewReference NewObjectToPython(object obj, BorrowedReference // Decrement the python object's reference count. // This doesn't actually destroy the object, it just sets the reference to this object // to be a weak reference and it will be destroyed when the C# object is destroyed. - if (!self.IsNull()) - { - Runtime.XDecref(self.Steal()); - } + Runtime.XDecref(self.Steal()); return Converter.ToPython(obj, type.Value); } @@ -942,13 +939,16 @@ internal static void Finalize(IntPtr derived) var type = Runtime.PyObject_TYPE(@ref.Borrow()); - // rare case when it's needed - // matches correspdonging PyObject_GC_UnTrack - // in ClassDerivedObject.tp_dealloc - Runtime.PyObject_GC_Del(@ref.Steal()); + if (!Runtime.HostedInPython || Runtime.TypeManagerInitialized) + { + // rare case when it's needed + // matches correspdonging PyObject_GC_UnTrack + // in ClassDerivedObject.tp_dealloc + Runtime.PyObject_GC_Del(@ref.Steal()); - // must decref our type - Runtime.XDecref(StolenReference.DangerousFromPointer(type.DangerousGetAddress())); + // must decref our type + Runtime.XDecref(StolenReference.DangerousFromPointer(type.DangerousGetAddress())); + } } internal static FieldInfo? GetPyObjField(Type type) => type.GetField(PyObjName, PyObjFlags); From 0e57cdd4c5f0200523ec9130fd6d869ed84cfa72 Mon Sep 17 00:00:00 2001 From: Victor Date: Fri, 8 Apr 2022 03:21:22 -0700 Subject: [PATCH 38/75] support for BigInteger <-> PyInt (#1710) fixes https://github.com/pythonnet/pythonnet/issues/1642#issuecomment-1058412759 --- CHANGELOG.md | 1 + src/embed_tests/TestConverter.cs | 20 +++++++++++++++ src/embed_tests/TestPyInt.cs | 34 ++++++++++++++++++++++++ src/runtime/Converter.cs | 8 ++++++ src/runtime/PythonTypes/PyInt.cs | 44 +++++++++++++++++++++++++++----- src/runtime/Runtime.cs | 16 ++++++++---- src/runtime/Util/Util.cs | 7 +++++ 7 files changed, 119 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 20b303dc6..1e706b866 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ This document follows the conventions laid out in [Keep a CHANGELOG][]. - .NET collection types now implement standard Python collection interfaces from `collections.abc`. See [Mixins/collections.py](src/runtime/Mixins/collections.py). - .NET arrays implement Python buffer protocol +- Python integer interoperability with `System.Numerics.BigInteger` - Python.NET will correctly resolve .NET methods, that accept `PyList`, `PyInt`, and other `PyObject` derived types when called from Python. - .NET classes, that have `__call__` method are callable from Python diff --git a/src/embed_tests/TestConverter.cs b/src/embed_tests/TestConverter.cs index 8f7cd381d..e586eda1b 100644 --- a/src/embed_tests/TestConverter.cs +++ b/src/embed_tests/TestConverter.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Numerics; using NUnit.Framework; @@ -131,6 +132,25 @@ public void ToNullable() Assert.AreEqual(Const, ni); } + [Test] + public void BigIntExplicit() + { + BigInteger val = 42; + var i = new PyInt(val); + var ni = i.As(); + Assert.AreEqual(val, ni); + var nullable = i.As(); + Assert.AreEqual(val, nullable); + } + + [Test] + public void PyIntImplicit() + { + var i = new PyInt(1); + var ni = (PyObject)i.As(); + Assert.AreEqual(i.rawPtr, ni.rawPtr); + } + [Test] public void ToPyList() { diff --git a/src/embed_tests/TestPyInt.cs b/src/embed_tests/TestPyInt.cs index 03a368ed8..822fe0715 100644 --- a/src/embed_tests/TestPyInt.cs +++ b/src/embed_tests/TestPyInt.cs @@ -1,4 +1,8 @@ using System; +using System.Globalization; +using System.Linq; +using System.Numerics; + using NUnit.Framework; using Python.Runtime; @@ -179,5 +183,35 @@ public void TestConvertToInt64() Assert.IsInstanceOf(typeof(long), a.ToInt64()); Assert.AreEqual(val, a.ToInt64()); } + + [Test] + public void ToBigInteger() + { + int[] simpleValues = + { + 0, 1, 2, + 0x10, + 0x123, + 0x1234, + }; + simpleValues = simpleValues.Concat(simpleValues.Select(v => -v)).ToArray(); + + foreach (var val in simpleValues) + { + var pyInt = new PyInt(val); + Assert.AreEqual((BigInteger)val, pyInt.ToBigInteger()); + } + } + + [Test] + public void ToBigIntegerLarge() + { + BigInteger val = BigInteger.Pow(2, 1024) + 3; + var pyInt = new PyInt(val); + Assert.AreEqual(val, pyInt.ToBigInteger()); + val = -val; + pyInt = new PyInt(val); + Assert.AreEqual(val, pyInt.ToBigInteger()); + } } } diff --git a/src/runtime/Converter.cs b/src/runtime/Converter.cs index ff1f01a64..a90f31513 100644 --- a/src/runtime/Converter.cs +++ b/src/runtime/Converter.cs @@ -455,6 +455,14 @@ internal static bool ToManagedValue(BorrowedReference value, Type obType, } } + if (obType == typeof(System.Numerics.BigInteger) + && Runtime.PyInt_Check(value)) + { + using var pyInt = new PyInt(value); + result = pyInt.ToBigInteger(); + return true; + } + return ToPrimitive(value, obType, out result, setError); } diff --git a/src/runtime/PythonTypes/PyInt.cs b/src/runtime/PythonTypes/PyInt.cs index d503c15f3..3dcc6ddb2 100644 --- a/src/runtime/PythonTypes/PyInt.cs +++ b/src/runtime/PythonTypes/PyInt.cs @@ -1,21 +1,21 @@ using System; +using System.Globalization; +using System.Numerics; using System.Runtime.Serialization; namespace Python.Runtime { /// - /// Represents a Python integer object. See the documentation at - /// PY2: https://docs.python.org/2/c-api/int.html - /// PY3: No equivalent - /// for details. + /// Represents a Python integer object. + /// See the documentation at https://docs.python.org/3/c-api/long.html /// - public class PyInt : PyNumber + public class PyInt : PyNumber, IFormattable { internal PyInt(in StolenReference ptr) : base(ptr) { } - internal PyInt(BorrowedReference reference): base(reference) + internal PyInt(BorrowedReference reference) : base(reference) { if (!Runtime.PyInt_Check(reference)) throw new ArgumentException("object is not an int"); } @@ -135,6 +135,8 @@ public PyInt(string value) : base(Runtime.PyLong_FromString(value, 0).StealOrThr { } + public PyInt(BigInteger value) : this(value.ToString(CultureInfo.InvariantCulture)) { } + protected PyInt(SerializationInfo info, StreamingContext context) : base(info, context) { } @@ -198,5 +200,35 @@ public long ToInt64() } return val.Value; } + + public BigInteger ToBigInteger() + { + using var pyHex = Runtime.HexCallable.Invoke(this); + string hex = pyHex.As(); + int offset = 0; + bool neg = false; + if (hex[0] == '-') + { + offset++; + neg = true; + } + byte[] littleEndianBytes = new byte[(hex.Length - offset + 1) / 2]; + for (; offset < hex.Length; offset++) + { + int littleEndianHexIndex = hex.Length - 1 - offset; + int byteIndex = littleEndianHexIndex / 2; + int isByteTopHalf = littleEndianHexIndex & 1; + int valueShift = isByteTopHalf * 4; + littleEndianBytes[byteIndex] += (byte)(Util.HexToInt(hex[offset]) << valueShift); + } + var result = new BigInteger(littleEndianBytes); + return neg ? -result : result; + } + + public string ToString(string format, IFormatProvider formatProvider) + { + using var _ = Py.GIL(); + return ToBigInteger().ToString(format, formatProvider); + } } } diff --git a/src/runtime/Runtime.cs b/src/runtime/Runtime.cs index e33c4624c..b48ba92e3 100644 --- a/src/runtime/Runtime.cs +++ b/src/runtime/Runtime.cs @@ -179,6 +179,7 @@ internal static void Initialize(bool initSigs = false) clrInterop = GetModuleLazy("clr.interop"); inspect = GetModuleLazy("inspect"); + hexCallable = new(() => new PyString("%x").GetAttr("__mod__")); } static void NewRun() @@ -279,8 +280,9 @@ internal static void Shutdown() Exceptions.Shutdown(); PythonEngine.InteropConfiguration.Dispose(); - DisposeLazyModule(clrInterop); - DisposeLazyModule(inspect); + DisposeLazyObject(clrInterop); + DisposeLazyObject(inspect); + DisposeLazyObject(hexCallable); PyObjectConversions.Reset(); PyGC_Collect(); @@ -352,11 +354,11 @@ static bool TryCollectingGarbage(int runs, bool forceBreakLoops) public static bool TryCollectingGarbage(int runs) => TryCollectingGarbage(runs, forceBreakLoops: false); - static void DisposeLazyModule(Lazy module) + static void DisposeLazyObject(Lazy pyObject) { - if (module.IsValueCreated) + if (pyObject.IsValueCreated) { - module.Value.Dispose(); + pyObject.Value.Dispose(); } } @@ -489,8 +491,12 @@ private static void NullGCHandles(IEnumerable objects) private static Lazy inspect; internal static PyObject InspectModule => inspect.Value; + private static Lazy clrInterop; internal static PyObject InteropModule => clrInterop.Value; + + private static Lazy hexCallable; + internal static PyObject HexCallable => hexCallable.Value; #pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. internal static BorrowedReference CLRMetaType => PyCLRMetaType; diff --git a/src/runtime/Util/Util.cs b/src/runtime/Util/Util.cs index f5f0d2957..89f5bdf4c 100644 --- a/src/runtime/Util/Util.cs +++ b/src/runtime/Util/Util.cs @@ -141,6 +141,13 @@ internal static string ReadStringResource(this System.Reflection.Assembly assemb return reader.ReadToEnd(); } + public static int HexToInt(char hex) => hex switch + { + >= '0' and <= '9' => hex - '0', + >= 'a' and <= 'f' => hex - 'a' + 10, + _ => throw new ArgumentOutOfRangeException(nameof(hex)), + }; + public static IEnumerator GetEnumerator(this IEnumerator enumerator) => enumerator; public static IEnumerable WhereNotNull(this IEnumerable source) From 58bd58c01c3ebe917c274d56737495a98eecb265 Mon Sep 17 00:00:00 2001 From: Victor Nova Date: Fri, 8 Apr 2022 08:42:03 -0700 Subject: [PATCH 39/75] Ensure that shutdown is called from Python --- pythonnet/__init__.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/pythonnet/__init__.py b/pythonnet/__init__.py index 7eec90f27..10dc403e4 100644 --- a/pythonnet/__init__.py +++ b/pythonnet/__init__.py @@ -16,7 +16,7 @@ def set_runtime(runtime): def set_default_runtime() -> None: - if sys.platform == 'win32': + if sys.platform == "win32": set_runtime(clr_loader.get_netfx()) else: set_runtime(clr_loader.get_mono()) @@ -36,14 +36,15 @@ def load(): set_default_runtime() dll_path = join(dirname(__file__), "runtime", "Python.Runtime.dll") - + _LOADER_ASSEMBLY = _RUNTIME.get_assembly(dll_path) func = _LOADER_ASSEMBLY["Python.Runtime.Loader.Initialize"] - if func(''.encode("utf8")) != 0: + if func(b"") != 0: raise RuntimeError("Failed to initialize Python.Runtime.dll") import atexit + atexit.register(unload) @@ -51,7 +52,7 @@ def unload(): global _RUNTIME if _LOADER_ASSEMBLY is not None: func = _LOADER_ASSEMBLY["Python.Runtime.Loader.Shutdown"] - if func(b"") != 0: + if func(b"full_shutdown") != 0: raise RuntimeError("Failed to call Python.NET shutdown") if _RUNTIME is not None: From 50da522c3b9098ebe10dde456efa81319083d6ba Mon Sep 17 00:00:00 2001 From: Victor Date: Sat, 9 Apr 2022 07:10:34 -0700 Subject: [PATCH 40/75] clear weakref list when reflected object is destroyed (#1758) this was missed when https://github.com/pythonnet/pythonnet/pull/1267 was superseded --- src/embed_tests/TestInstanceWrapping.cs | 10 ++++++++++ src/runtime/Runtime.Delegates.cs | 2 ++ src/runtime/Runtime.cs | 12 ++++++++++++ src/runtime/Types/ClassBase.cs | 6 ++++++ 4 files changed, 30 insertions(+) diff --git a/src/embed_tests/TestInstanceWrapping.cs b/src/embed_tests/TestInstanceWrapping.cs index 8be207c00..0a441c823 100644 --- a/src/embed_tests/TestInstanceWrapping.cs +++ b/src/embed_tests/TestInstanceWrapping.cs @@ -34,6 +34,16 @@ public void OverloadResolution_UnknownToObject() } } + [Test] + public void WeakRefIsNone_AfterObjectIsGone() + { + using var makeref = Py.Import("weakref").GetAttr("ref"); + var ub = new UriBuilder().ToPython(); + using var weakref = makeref.Invoke(ub); + ub.Dispose(); + Assert.IsTrue(weakref.Invoke().IsNone()); + } + class Base {} class Derived: Base { } diff --git a/src/runtime/Runtime.Delegates.cs b/src/runtime/Runtime.Delegates.cs index 6388bde9f..0b6b75872 100644 --- a/src/runtime/Runtime.Delegates.cs +++ b/src/runtime/Runtime.Delegates.cs @@ -78,6 +78,7 @@ static Delegates() PyObject_RichCompareBool = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyObject_RichCompareBool), GetUnmanagedDll(_PythonDll)); PyObject_IsInstance = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyObject_IsInstance), GetUnmanagedDll(_PythonDll)); PyObject_IsSubclass = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyObject_IsSubclass), GetUnmanagedDll(_PythonDll)); + PyObject_ClearWeakRefs = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyObject_ClearWeakRefs), GetUnmanagedDll(_PythonDll)); PyCallable_Check = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyCallable_Check), GetUnmanagedDll(_PythonDll)); PyObject_IsTrue = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyObject_IsTrue), GetUnmanagedDll(_PythonDll)); PyObject_Not = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyObject_Not), GetUnmanagedDll(_PythonDll)); @@ -361,6 +362,7 @@ static Delegates() internal static delegate* unmanaged[Cdecl] PyObject_RichCompareBool { get; } internal static delegate* unmanaged[Cdecl] PyObject_IsInstance { get; } internal static delegate* unmanaged[Cdecl] PyObject_IsSubclass { get; } + internal static delegate* unmanaged[Cdecl] PyObject_ClearWeakRefs { get; } internal static delegate* unmanaged[Cdecl] PyCallable_Check { get; } internal static delegate* unmanaged[Cdecl] PyObject_IsTrue { get; } internal static delegate* unmanaged[Cdecl] PyObject_Not { get; } diff --git a/src/runtime/Runtime.cs b/src/runtime/Runtime.cs index aeead7915..d92f45afb 100644 --- a/src/runtime/Runtime.cs +++ b/src/runtime/Runtime.cs @@ -993,6 +993,18 @@ internal static int PyObject_Compare(BorrowedReference value1, BorrowedReference internal static int PyObject_IsSubclass(BorrowedReference ob, BorrowedReference type) => Delegates.PyObject_IsSubclass(ob, type); + internal static void PyObject_ClearWeakRefs(BorrowedReference ob) => Delegates.PyObject_ClearWeakRefs(ob); + + internal static BorrowedReference PyObject_GetWeakRefList(BorrowedReference ob) + { + Debug.Assert(ob != null); + var type = PyObject_TYPE(ob); + int offset = Util.ReadInt32(type, TypeOffset.tp_weaklistoffset); + if (offset == 0) return BorrowedReference.Null; + Debug.Assert(offset > 0); + return Util.ReadRef(ob, offset); + } + internal static int PyCallable_Check(BorrowedReference o) => Delegates.PyCallable_Check(o); diff --git a/src/runtime/Types/ClassBase.cs b/src/runtime/Types/ClassBase.cs index 2493fd970..6066e5fec 100644 --- a/src/runtime/Types/ClassBase.cs +++ b/src/runtime/Types/ClassBase.cs @@ -346,6 +346,12 @@ public static void tp_dealloc(NewReference lastRef) public static int tp_clear(BorrowedReference ob) { + var weakrefs = Runtime.PyObject_GetWeakRefList(ob); + if (weakrefs != null) + { + Runtime.PyObject_ClearWeakRefs(ob); + } + if (TryFreeGCHandle(ob)) { IntPtr addr = ob.DangerousGetAddress(); From 86c6a7f40c3f62bb1b085e2645ba9e2729439a39 Mon Sep 17 00:00:00 2001 From: Victor Date: Sun, 10 Apr 2022 00:24:38 -0700 Subject: [PATCH 41/75] clear weak reference list when an extension type is destroyed (#1761) According to official documentation for defining extension types, if they are to support weak references, they must clear weak reference list associated with the instance in tp_clear. Extension types were missed in https://github.com/pythonnet/pythonnet/pull/1758 ( 50da522c3b9098ebe10dde456efa81319083d6ba ) --- src/embed_tests/ExtensionTypes.cs | 32 ++++++++++++++++++++++++++++++ src/runtime/Types/ExtensionType.cs | 6 ++++++ src/runtime/Types/MetaType.cs | 6 ++++++ 3 files changed, 44 insertions(+) create mode 100644 src/embed_tests/ExtensionTypes.cs diff --git a/src/embed_tests/ExtensionTypes.cs b/src/embed_tests/ExtensionTypes.cs new file mode 100644 index 000000000..803845960 --- /dev/null +++ b/src/embed_tests/ExtensionTypes.cs @@ -0,0 +1,32 @@ +using System; + +using NUnit.Framework; + +using Python.Runtime; + +namespace Python.EmbeddingTest; + +public class ExtensionTypes +{ + [OneTimeSetUp] + public void SetUp() + { + PythonEngine.Initialize(); + } + + [OneTimeTearDown] + public void Dispose() + { + PythonEngine.Shutdown(); + } + + [Test] + public void WeakrefIsNone_AfterBoundMethodIsGone() + { + using var makeref = Py.Import("weakref").GetAttr("ref"); + var boundMethod = new UriBuilder().ToPython().GetAttr(nameof(UriBuilder.GetHashCode)); + var weakref = makeref.Invoke(boundMethod); + boundMethod.Dispose(); + Assert.IsTrue(weakref.Invoke().IsNone()); + } +} diff --git a/src/runtime/Types/ExtensionType.cs b/src/runtime/Types/ExtensionType.cs index d680067c2..439bd3314 100644 --- a/src/runtime/Types/ExtensionType.cs +++ b/src/runtime/Types/ExtensionType.cs @@ -86,6 +86,12 @@ public unsafe static void tp_dealloc(NewReference lastRef) public static int tp_clear(BorrowedReference ob) { + var weakrefs = Runtime.PyObject_GetWeakRefList(ob); + if (weakrefs != null) + { + Runtime.PyObject_ClearWeakRefs(ob); + } + if (TryFreeGCHandle(ob)) { bool deleted = loadedExtensions.Remove(ob.DangerousGetAddress()); diff --git a/src/runtime/Types/MetaType.cs b/src/runtime/Types/MetaType.cs index 7558269b4..1543711f6 100644 --- a/src/runtime/Types/MetaType.cs +++ b/src/runtime/Types/MetaType.cs @@ -273,6 +273,12 @@ public static NewReference mp_subscript(BorrowedReference tp, BorrowedReference /// public static void tp_dealloc(NewReference lastRef) { + var weakrefs = Runtime.PyObject_GetWeakRefList(lastRef.Borrow()); + if (weakrefs != null) + { + Runtime.PyObject_ClearWeakRefs(lastRef.Borrow()); + } + // Fix this when we dont cheat on the handle for subclasses! var flags = PyType.GetFlags(lastRef.Borrow()); From 5af19614d836fda853be922bde3d928d2a86eefc Mon Sep 17 00:00:00 2001 From: Victor Nova Date: Sun, 10 Apr 2022 15:32:08 -0700 Subject: [PATCH 42/75] fixed NativeTypeSpec on 32 bit platforms --- src/runtime/Native/NativeTypeSpec.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/runtime/Native/NativeTypeSpec.cs b/src/runtime/Native/NativeTypeSpec.cs index c57bd9363..3771061dd 100644 --- a/src/runtime/Native/NativeTypeSpec.cs +++ b/src/runtime/Native/NativeTypeSpec.cs @@ -10,7 +10,7 @@ struct NativeTypeSpec : IDisposable public readonly StrPtr Name; public readonly int BasicSize; public readonly int ItemSize; - public readonly TypeFlags Flags; + public readonly IntPtr Flags; public IntPtr Slots; public NativeTypeSpec(TypeSpec spec) @@ -20,7 +20,7 @@ public NativeTypeSpec(TypeSpec spec) this.Name = new StrPtr(spec.Name, Encoding.UTF8); this.BasicSize = spec.BasicSize; this.ItemSize = spec.ItemSize; - this.Flags = spec.Flags; + this.Flags = new IntPtr((long)spec.Flags); unsafe { From 85037267f87bb44d8e8e43784b3aa2e06dd0a469 Mon Sep 17 00:00:00 2001 From: Victor Nova Date: Sun, 10 Apr 2022 15:32:28 -0700 Subject: [PATCH 43/75] enable x86 testing on Windows in CI --- .github/workflows/main.yml | 10 ++++++++-- src/runtime/Native/NativeTypeSpec.cs | 4 ++-- tests/conftest.py | 6 +++++- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 218796192..11d8699e4 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -17,7 +17,12 @@ jobs: matrix: os: [windows, ubuntu, macos] python: ["3.6", "3.7", "3.8", "3.9", "3.10"] - platform: [x64] + platform: [x64, x86] + exclude: + - os: ubuntu + platform: x86 + - os: macos + platform: x86 steps: - name: Set Environment on macOS @@ -67,6 +72,7 @@ jobs: run: pytest --runtime mono - name: Python Tests (.NET Core) + if: ${{ matrix.platform == 'x64' }} run: pytest --runtime netcore - name: Python Tests (.NET Framework) @@ -77,7 +83,7 @@ jobs: run: dotnet test --runtime any-${{ matrix.platform }} src/python_tests_runner/ - name: Perf tests - if: ${{ matrix.python == '3.8' }} + if: ${{ (matrix.python == '3.8') && (matrix.platform == 'x64') }} run: | pip install --force --no-deps --target src/perf_tests/baseline/ pythonnet==2.5.2 dotnet test --configuration Release --runtime any-${{ matrix.platform }} --logger "console;verbosity=detailed" src/perf_tests/ diff --git a/src/runtime/Native/NativeTypeSpec.cs b/src/runtime/Native/NativeTypeSpec.cs index 3771061dd..8b84df536 100644 --- a/src/runtime/Native/NativeTypeSpec.cs +++ b/src/runtime/Native/NativeTypeSpec.cs @@ -10,7 +10,7 @@ struct NativeTypeSpec : IDisposable public readonly StrPtr Name; public readonly int BasicSize; public readonly int ItemSize; - public readonly IntPtr Flags; + public readonly int Flags; public IntPtr Slots; public NativeTypeSpec(TypeSpec spec) @@ -20,7 +20,7 @@ public NativeTypeSpec(TypeSpec spec) this.Name = new StrPtr(spec.Name, Encoding.UTF8); this.BasicSize = spec.BasicSize; this.ItemSize = spec.ItemSize; - this.Flags = new IntPtr((long)spec.Flags); + this.Flags = (int)spec.Flags; unsafe { diff --git a/tests/conftest.py b/tests/conftest.py index 99ee07f2f..89db46eca 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -86,7 +86,11 @@ def pytest_configure(config): else: domain_tests_dir = os.path.join(os.path.dirname(__file__), "domain_tests") bin_path = os.path.join(domain_tests_dir, "bin") - check_call(["dotnet", "build", domain_tests_dir, "-o", bin_path]) + build_cmd = ["dotnet", "build", domain_tests_dir, "-o", bin_path] + is_64bits = sys.maxsize > 2**32 + if not is_64bits: + build_cmd += ["/p:Prefer32Bit=True"] + check_call(build_cmd) From 80dc9f0ba061ed0dde51be0bce1a3d24c2b4f902 Mon Sep 17 00:00:00 2001 From: Benedikt Reinartz Date: Sun, 10 Apr 2022 18:12:51 +0200 Subject: [PATCH 44/75] Implement IConvertible interface on PyObject --- src/runtime/Converter.cs | 2 +- src/runtime/PythonTypes/PyFloat.cs | 2 + src/runtime/PythonTypes/PyInt.cs | 2 + .../PythonTypes/PyObject.IConvertible.cs | 53 +++++++++++++++++++ src/runtime/PythonTypes/PyString.cs | 2 + tests/test_conversion.py | 7 +++ 6 files changed, 67 insertions(+), 1 deletion(-) create mode 100644 src/runtime/PythonTypes/PyObject.IConvertible.cs diff --git a/src/runtime/Converter.cs b/src/runtime/Converter.cs index a90f31513..a99961aaa 100644 --- a/src/runtime/Converter.cs +++ b/src/runtime/Converter.cs @@ -550,7 +550,7 @@ internal static int ToInt32(BorrowedReference value) /// /// Convert a Python value to an instance of a primitive managed type. /// - private static bool ToPrimitive(BorrowedReference value, Type obType, out object? result, bool setError) + internal static bool ToPrimitive(BorrowedReference value, Type obType, out object? result, bool setError) { result = null; if (obType.IsEnum) diff --git a/src/runtime/PythonTypes/PyFloat.cs b/src/runtime/PythonTypes/PyFloat.cs index 7fb9e8f4d..10104c10f 100644 --- a/src/runtime/PythonTypes/PyFloat.cs +++ b/src/runtime/PythonTypes/PyFloat.cs @@ -101,5 +101,7 @@ public static PyFloat AsFloat(PyObject value) PythonException.ThrowIfIsNull(op); return new PyFloat(op.Steal()); } + + public override TypeCode GetTypeCode() => TypeCode.Double; } } diff --git a/src/runtime/PythonTypes/PyInt.cs b/src/runtime/PythonTypes/PyInt.cs index 3dcc6ddb2..6b3dbf210 100644 --- a/src/runtime/PythonTypes/PyInt.cs +++ b/src/runtime/PythonTypes/PyInt.cs @@ -230,5 +230,7 @@ public string ToString(string format, IFormatProvider formatProvider) using var _ = Py.GIL(); return ToBigInteger().ToString(format, formatProvider); } + + public override TypeCode GetTypeCode() => TypeCode.Int64; } } diff --git a/src/runtime/PythonTypes/PyObject.IConvertible.cs b/src/runtime/PythonTypes/PyObject.IConvertible.cs new file mode 100644 index 000000000..503d3cab4 --- /dev/null +++ b/src/runtime/PythonTypes/PyObject.IConvertible.cs @@ -0,0 +1,53 @@ +using System; + +namespace Python.Runtime; + +public partial class PyObject : IConvertible +{ + public virtual TypeCode GetTypeCode() => TypeCode.Object; + + private T DoConvert() + { + using var _ = Py.GIL(); + if (Converter.ToPrimitive(Reference, typeof(T), out object? result, setError: false)) + { + return (T)result!; + } + else + { + throw new InvalidCastException(); + } + } + + public bool ToBoolean(IFormatProvider provider) => DoConvert(); + public byte ToByte(IFormatProvider provider) => DoConvert(); + public char ToChar(IFormatProvider provider) => DoConvert(); + public short ToInt16(IFormatProvider provider) => DoConvert(); + public int ToInt32(IFormatProvider provider) => DoConvert(); + public long ToInt64(IFormatProvider provider) => DoConvert(); + public sbyte ToSByte(IFormatProvider provider) => DoConvert(); + public ushort ToUInt16(IFormatProvider provider) => DoConvert(); + public uint ToUInt32(IFormatProvider provider) => DoConvert(); + public ulong ToUInt64(IFormatProvider provider) => DoConvert(); + + public float ToSingle(IFormatProvider provider) => DoConvert(); + public double ToDouble(IFormatProvider provider) => DoConvert(); + + public string ToString(IFormatProvider provider) => DoConvert(); + + public DateTime ToDateTime(IFormatProvider provider) => throw new InvalidCastException(); + public decimal ToDecimal(IFormatProvider provider) => throw new InvalidCastException(); + + public object ToType(Type conversionType, IFormatProvider provider) + { + if (Converter.ToManaged(Reference, conversionType, out object? result, setError: false)) + { + return result!; + } + else + { + throw new InvalidCastException(); + } + } + +} \ No newline at end of file diff --git a/src/runtime/PythonTypes/PyString.cs b/src/runtime/PythonTypes/PyString.cs index cdd45e2c3..d54397fcf 100644 --- a/src/runtime/PythonTypes/PyString.cs +++ b/src/runtime/PythonTypes/PyString.cs @@ -59,5 +59,7 @@ public static bool IsStringType(PyObject value) { return Runtime.PyString_Check(value.obj); } + + public override TypeCode GetTypeCode() => TypeCode.String; } } diff --git a/tests/test_conversion.py b/tests/test_conversion.py index 341b11b90..4de286b14 100644 --- a/tests/test_conversion.py +++ b/tests/test_conversion.py @@ -670,3 +670,10 @@ def test_int_param_resolution_required(): data = list(mri.MethodA(0x100000000, 10)) assert len(data) == 10 assert data[0] == 0 + +def test_iconvertible_conversion(): + change_type = System.Convert.ChangeType + + assert 1024 == change_type(1024, System.Int32) + assert 1024 == change_type(1024, System.Int64) + assert 1024 == change_type(1024, System.Int16) From 090ff9f460016b006f49c328115cc47f80be3b1f Mon Sep 17 00:00:00 2001 From: Benedikt Reinartz Date: Mon, 25 Apr 2022 10:51:52 +0200 Subject: [PATCH 45/75] Ensure that codec tests are run (#1763) * Move test_codec.py to the right directory * Call PyObjectConversions.Reset from Python via a proxy to keep it internal * Sign Python.Test DLL and make Python.Runtime internals visible for it --- src/runtime/Properties/AssemblyInfo.cs | 2 + src/testing/CodecTest.cs | 9 ++++ src/testing/Python.Test.csproj | 2 + {src/tests => tests}/test_codec.py | 74 +++++++++++++------------- 4 files changed, 50 insertions(+), 37 deletions(-) rename {src/tests => tests}/test_codec.py (88%) diff --git a/src/runtime/Properties/AssemblyInfo.cs b/src/runtime/Properties/AssemblyInfo.cs index 3417bccc8..b05fcc8bf 100644 --- a/src/runtime/Properties/AssemblyInfo.cs +++ b/src/runtime/Properties/AssemblyInfo.cs @@ -1,3 +1,5 @@ using System.Runtime.CompilerServices; [assembly: InternalsVisibleTo("Python.EmbeddingTest, PublicKey=00240000048000009400000006020000002400005253413100040000110000005ffd8f49fb44ab0641b3fd8d55e749f716e6dd901032295db641eb98ee46063cbe0d4a1d121ef0bc2af95f8a7438d7a80a3531316e6b75c2dae92fb05a99f03bf7e0c03980e1c3cfb74ba690aca2f3339ef329313bcc5dccced125a4ffdc4531dcef914602cd5878dc5fbb4d4c73ddfbc133f840231343e013762884d6143189")] + +[assembly: InternalsVisibleTo("Python.Test, PublicKey=00240000048000009400000006020000002400005253413100040000110000005ffd8f49fb44ab0641b3fd8d55e749f716e6dd901032295db641eb98ee46063cbe0d4a1d121ef0bc2af95f8a7438d7a80a3531316e6b75c2dae92fb05a99f03bf7e0c03980e1c3cfb74ba690aca2f3339ef329313bcc5dccced125a4ffdc4531dcef914602cd5878dc5fbb4d4c73ddfbc133f840231343e013762884d6143189")] \ No newline at end of file diff --git a/src/testing/CodecTest.cs b/src/testing/CodecTest.cs index 74f77531b..f3c243dab 100644 --- a/src/testing/CodecTest.cs +++ b/src/testing/CodecTest.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Text; +using Python.Runtime; namespace Python.Test { @@ -44,4 +45,12 @@ public int GetLength2(IList o) return o.Count; } } + + public static class CodecResetter + { + public static void Reset() + { + PyObjectConversions.Reset(); + } + } } diff --git a/src/testing/Python.Test.csproj b/src/testing/Python.Test.csproj index 78f3a3169..1f40f4518 100644 --- a/src/testing/Python.Test.csproj +++ b/src/testing/Python.Test.csproj @@ -3,6 +3,8 @@ netstandard2.0;net6.0 true true + ..\pythonnet.snk + true diff --git a/src/tests/test_codec.py b/tests/test_codec.py similarity index 88% rename from src/tests/test_codec.py rename to tests/test_codec.py index 9fdbfbbf5..9744d3856 100644 --- a/src/tests/test_codec.py +++ b/tests/test_codec.py @@ -1,49 +1,49 @@ -# -*- coding: utf-8 -*- - -"""Test conversions using codecs from client python code""" -import clr -import System -import pytest -import Python.Runtime -from Python.Test import ListConversionTester, ListMember - -class int_iterable(): - def __init__(self): - self.counter = 0 - def __iter__(self): - return self - def __next__(self): - if self.counter == 3: - raise StopIteration - self.counter = self.counter + 1 - return self.counter +# -*- coding: utf-8 -*- + +"""Test conversions using codecs from client python code""" +import clr +import System +import pytest +import Python.Runtime +from Python.Test import ListConversionTester, ListMember, CodecResetter + +class int_iterable(): + def __init__(self): + self.counter = 0 + def __iter__(self): + return self + def __next__(self): + if self.counter == 3: + raise StopIteration + self.counter = self.counter + 1 + return self.counter class obj_iterable(): - def __init__(self): - self.counter = 0 - def __iter__(self): - return self - def __next__(self): - if self.counter == 3: - raise StopIteration + def __init__(self): + self.counter = 0 + def __iter__(self): + return self + def __next__(self): + if self.counter == 3: + raise StopIteration self.counter = self.counter + 1 return ListMember(self.counter, "Number " + str(self.counter)) - -def test_iterable(): - """Test that a python iterable can be passed into a function that takes an IEnumerable""" - - #Python.Runtime.Codecs.ListDecoder.Register() - #Python.Runtime.Codecs.SequenceDecoder.Register() - Python.Runtime.Codecs.IterableDecoder.Register() + +def test_iterable(): + """Test that a python iterable can be passed into a function that takes an IEnumerable""" + + #Python.Runtime.Codecs.ListDecoder.Register() + #Python.Runtime.Codecs.SequenceDecoder.Register() + Python.Runtime.Codecs.IterableDecoder.Register() ob = ListConversionTester() - iterable = int_iterable() + iterable = int_iterable() assert 3 == ob.GetLength(iterable) iterable2 = obj_iterable() assert 3 == ob.GetLength2(iterable2) - Python.Runtime.PyObjectConversions.Reset() + CodecResetter.Reset() def test_sequence(): Python.Runtime.Codecs.SequenceDecoder.Register() @@ -55,7 +55,7 @@ def test_sequence(): tup2 = (ListMember(1, "one"), ListMember(2, "two"), ListMember(3, "three")) assert 3 == ob.GetLength(tup2) - Python.Runtime.PyObjectConversions.Reset() + CodecResetter.Reset() def test_list(): Python.Runtime.Codecs.SequenceDecoder.Register() @@ -67,4 +67,4 @@ def test_list(): l2 = [ListMember(1, "one"), ListMember(2, "two"), ListMember(3, "three")] assert 3 == ob.GetLength(l2) - Python.Runtime.PyObjectConversions.Reset() + CodecResetter.Reset() From 7247da55c174be1b733e5f9fc4e1c356f6c42dc4 Mon Sep 17 00:00:00 2001 From: Benedikt Reinartz Date: Tue, 26 Apr 2022 18:11:58 +0200 Subject: [PATCH 46/75] Fix decimal default parameters (#1773) * Drop outdated work around to fix decimal default parameters * Add test for Decimal default parameter * Update changelog --- CHANGELOG.md | 1 + src/runtime/MethodBinder.cs | 6 +----- src/testing/Python.Test.csproj | 1 + src/testing/methodtest.cs | 5 +++++ tests/test_method.py | 6 ++++++ 5 files changed, 14 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1e706b866..766258c5d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -113,6 +113,7 @@ Instead, `PyIterable` does that. - Unicode strings with surrogates were truncated when converting from Python - `Reload` mode now supports generic methods (previously Python would stop seeing them after reload) - Temporarily fixed issue resolving method overload when method signature has `out` parameters ([#1672](i1672)) +- Decimal default parameters are now correctly taken into account ### Removed diff --git a/src/runtime/MethodBinder.cs b/src/runtime/MethodBinder.cs index 8b9ee9c00..be4e8d0e5 100644 --- a/src/runtime/MethodBinder.cs +++ b/src/runtime/MethodBinder.cs @@ -1072,11 +1072,7 @@ static internal class ParameterInfoExtensions { public static object? GetDefaultValue(this ParameterInfo parameterInfo) { - // parameterInfo.HasDefaultValue is preferable but doesn't exist in .NET 4.0 - bool hasDefaultValue = (parameterInfo.Attributes & ParameterAttributes.HasDefault) == - ParameterAttributes.HasDefault; - - if (hasDefaultValue) + if (parameterInfo.HasDefaultValue) { return parameterInfo.DefaultValue; } diff --git a/src/testing/Python.Test.csproj b/src/testing/Python.Test.csproj index 1f40f4518..3adc5c0c6 100644 --- a/src/testing/Python.Test.csproj +++ b/src/testing/Python.Test.csproj @@ -5,6 +5,7 @@ true ..\pythonnet.snk true + IDE0051;IDE0060 diff --git a/src/testing/methodtest.cs b/src/testing/methodtest.cs index fe49de88d..ec05fef72 100644 --- a/src/testing/methodtest.cs +++ b/src/testing/methodtest.cs @@ -258,6 +258,11 @@ public static int TestSingleDefaultParam(int i = 5) return i; } + public static decimal TestDecimalDefaultParam(decimal n = 1m) + { + return n; + } + public static int TestTwoDefaultParam(int i = 5, int j = 6) { return i + j; diff --git a/tests/test_method.py b/tests/test_method.py index e2d8d5b06..b24b525aa 100644 --- a/tests/test_method.py +++ b/tests/test_method.py @@ -441,6 +441,12 @@ def test_single_default_param(): assert result == 5 +def test_decimal_default_param(): + """Test that decimal default parameters work.""" + result = MethodTest.TestDecimalDefaultParam() + assert result == System.Decimal(1) + + def test_one_arg_and_two_default_param(): """Test void method with single ref-parameter.""" result = MethodTest.TestOneArgAndTwoDefaultParam(11) From bbfa25263f5aad0639c06c7716854c442b65255a Mon Sep 17 00:00:00 2001 From: Victor Date: Tue, 3 May 2022 22:33:38 -0700 Subject: [PATCH 47/75] workaround for potentially buggy threading in Mono (#1779) https://github.com/mono/mono/issues/21466 --- .github/workflows/main.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 11d8699e4..284658620 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -66,6 +66,8 @@ jobs: - name: Embedding tests run: dotnet test --runtime any-${{ matrix.platform }} --logger "console;verbosity=detailed" src/embed_tests/ + env: + MONO_THREADS_SUSPEND: preemptive # https://github.com/mono/mono/issues/21466 - name: Python Tests (Mono) if: ${{ matrix.os != 'windows' }} From 5eccd45a7ed9b4fddc9345fad1f7f63fa5d0adf3 Mon Sep 17 00:00:00 2001 From: Benedikt Reinartz Date: Thu, 10 Mar 2022 17:37:17 +0100 Subject: [PATCH 48/75] Use Py.GIL directly, now that it doesn't try to init anymore --- src/runtime/Loader.cs | 25 ++++--------------------- 1 file changed, 4 insertions(+), 21 deletions(-) diff --git a/src/runtime/Loader.cs b/src/runtime/Loader.cs index bfb6e0d6e..516b9ab9c 100644 --- a/src/runtime/Loader.cs +++ b/src/runtime/Loader.cs @@ -23,18 +23,8 @@ public unsafe static int Initialize(IntPtr data, int size) PythonDLL = null; } - var gs = PyGILState_Ensure(); - - try - { - // Console.WriteLine("Startup thread"); - PythonEngine.InitExt(); - // Console.WriteLine("Startup finished"); - } - finally - { - PyGILState_Release(gs); - } + using var _ = Py.GIL(); + PythonEngine.InitExt(); } catch (Exception exc) { @@ -55,15 +45,8 @@ public unsafe static int Shutdown(IntPtr data, int size) if (command == "full_shutdown") { - var gs = PyGILState_Ensure(); - try - { - PythonEngine.Shutdown(); - } - finally - { - PyGILState_Release(gs); - } + using var _ = Py.GIL(); + PythonEngine.Shutdown(); } } catch (Exception exc) From d2d3ba669f7f5a149d0077a7cc6b6e4872eb1bd7 Mon Sep 17 00:00:00 2001 From: Benedikt Reinartz Date: Wed, 30 Mar 2022 15:29:44 +0200 Subject: [PATCH 49/75] Modernise syntax a bit --- src/runtime/AssemblyManager.cs | 6 +- src/runtime/ClassManager.cs | 3 +- src/runtime/Codecs/DecoderGroup.cs | 2 +- src/runtime/Codecs/EncoderGroup.cs | 2 +- src/runtime/Codecs/PyObjectConversions.cs | 6 +- src/runtime/Converter.cs | 23 ++- src/runtime/DelegateManager.cs | 6 +- src/runtime/Exceptions.cs | 3 +- src/runtime/Finalizer.cs | 2 +- src/runtime/InternString.cs | 3 +- src/runtime/Interop.cs | 2 +- src/runtime/InteropConfiguration.cs | 2 +- src/runtime/MethodBinder.cs | 88 ++++-------- src/runtime/Native/BorrowedReference.cs | 2 +- src/runtime/Native/CustomMarshaler.cs | 8 +- src/runtime/Native/NewReference.cs | 2 +- src/runtime/Native/PyIdentifier_.cs | 132 +++++++++--------- src/runtime/Native/PyIdentifier_.tt | 120 ++++++++-------- src/runtime/Native/TypeOffset.cs | 2 +- src/runtime/Py.cs | 3 +- src/runtime/PythonEngine.cs | 4 +- src/runtime/PythonException.cs | 9 +- src/runtime/PythonTypes/PyDict.cs | 6 +- src/runtime/PythonTypes/PyFloat.cs | 10 +- src/runtime/PythonTypes/PyModule.cs | 40 +++--- src/runtime/PythonTypes/PyObject.cs | 38 ++--- src/runtime/Runtime.cs | 12 +- .../StateSerialization/MaybeMemberInfo.cs | 4 +- .../StateSerialization/MaybeMethodBase.cs | 6 +- src/runtime/StateSerialization/MaybeType.cs | 6 +- src/runtime/TypeManager.cs | 51 ++++--- src/runtime/Types/ArrayObject.cs | 21 +-- src/runtime/Types/ClassBase.cs | 36 ++--- src/runtime/Types/ClassDerived.cs | 59 ++++---- src/runtime/Types/ClassObject.cs | 15 +- src/runtime/Types/DelegateObject.cs | 19 +-- src/runtime/Types/EventBinding.cs | 2 +- src/runtime/Types/EventObject.cs | 4 +- src/runtime/Types/ExtensionType.cs | 2 +- src/runtime/Types/InterfaceObject.cs | 11 +- src/runtime/Types/Iterator.cs | 4 +- src/runtime/Types/MetaType.cs | 56 +++----- src/runtime/Types/MethodBinding.cs | 9 +- src/runtime/Types/MethodObject.cs | 3 +- src/runtime/Types/ModuleObject.cs | 3 +- src/runtime/Types/MpLengthSlot.cs | 3 +- src/runtime/Types/OperatorMethod.cs | 18 ++- src/runtime/Types/OverloadMapper.cs | 4 +- src/runtime/Util/CodeGenerator.cs | 4 +- src/runtime/Util/DebugUtil.cs | 7 +- src/runtime/Util/GenericUtil.cs | 62 +++----- src/runtime/Util/OpsHelper.cs | 2 + src/runtime/Util/ReflectionPolyfills.cs | 2 +- 53 files changed, 420 insertions(+), 529 deletions(-) diff --git a/src/runtime/AssemblyManager.cs b/src/runtime/AssemblyManager.cs index 56c70c13a..d09d2d76e 100644 --- a/src/runtime/AssemblyManager.cs +++ b/src/runtime/AssemblyManager.cs @@ -23,8 +23,8 @@ internal class AssemblyManager // than it can end up referring to assemblies that are already unloaded (default behavior after unload appDomain - // unless LoaderOptimization.MultiDomain is used); // So for multidomain support it is better to have the dict. recreated for each app-domain initialization - private static ConcurrentDictionary> namespaces = - new ConcurrentDictionary>(); + private static readonly ConcurrentDictionary> namespaces = + new(); #pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. // domain-level handlers are initialized in Initialize @@ -33,7 +33,7 @@ internal class AssemblyManager #pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. // updated only under GIL? - private static Dictionary probed = new Dictionary(32); + private static readonly Dictionary probed = new(32); // modified from event handlers below, potentially triggered from different .NET threads private static readonly ConcurrentQueue assemblies = new(); diff --git a/src/runtime/ClassManager.cs b/src/runtime/ClassManager.cs index 647cec3ed..6379f51de 100644 --- a/src/runtime/ClassManager.cs +++ b/src/runtime/ClassManager.cs @@ -247,10 +247,9 @@ internal static void InitClassBase(Type type, ClassBase impl, ReflectedClrType p Runtime.PyDict_SetItem(dict, PyIdentifier.__doc__, doc.Borrow()); } - var co = impl as ClassObject; // If this is a ClassObject AND it has constructors, generate a __doc__ attribute. // required that the ClassObject.ctors be changed to internal - if (co != null) + if (impl is ClassObject co) { if (co.NumCtors > 0 && !co.HasCustomNew()) { diff --git a/src/runtime/Codecs/DecoderGroup.cs b/src/runtime/Codecs/DecoderGroup.cs index 5b3d127ad..41e1f0494 100644 --- a/src/runtime/Codecs/DecoderGroup.cs +++ b/src/runtime/Codecs/DecoderGroup.cs @@ -10,7 +10,7 @@ namespace Python.Runtime.Codecs /// public sealed class DecoderGroup: IPyObjectDecoder, IEnumerable, IDisposable { - readonly List decoders = new List(); + readonly List decoders = new(); /// /// Add specified decoder to the group diff --git a/src/runtime/Codecs/EncoderGroup.cs b/src/runtime/Codecs/EncoderGroup.cs index 32b550eb9..63abf35a3 100644 --- a/src/runtime/Codecs/EncoderGroup.cs +++ b/src/runtime/Codecs/EncoderGroup.cs @@ -10,7 +10,7 @@ namespace Python.Runtime.Codecs /// public sealed class EncoderGroup: IPyObjectEncoder, IEnumerable, IDisposable { - readonly List encoders = new List(); + readonly List encoders = new(); /// /// Add specified encoder to the group diff --git a/src/runtime/Codecs/PyObjectConversions.cs b/src/runtime/Codecs/PyObjectConversions.cs index 94ed4cdc3..cde97c8c9 100644 --- a/src/runtime/Codecs/PyObjectConversions.cs +++ b/src/runtime/Codecs/PyObjectConversions.cs @@ -15,8 +15,8 @@ namespace Python.Runtime /// public static class PyObjectConversions { - static readonly DecoderGroup decoders = new DecoderGroup(); - static readonly EncoderGroup encoders = new EncoderGroup(); + static readonly DecoderGroup decoders = new(); + static readonly EncoderGroup encoders = new(); /// /// Registers specified encoder (marshaller) @@ -62,7 +62,7 @@ public static void RegisterDecoder(IPyObjectDecoder decoder) } static readonly ConcurrentDictionary - clrToPython = new ConcurrentDictionary(); + clrToPython = new(); static IPyObjectEncoder[] GetEncoders(Type type) { lock (encoders) diff --git a/src/runtime/Converter.cs b/src/runtime/Converter.cs index a99961aaa..f86ba7900 100644 --- a/src/runtime/Converter.cs +++ b/src/runtime/Converter.cs @@ -18,15 +18,15 @@ private Converter() { } - private static Type objectType; - private static Type stringType; - private static Type singleType; - private static Type doubleType; - private static Type int16Type; - private static Type int32Type; - private static Type int64Type; - private static Type boolType; - private static Type typeType; + private static readonly Type objectType; + private static readonly Type stringType; + private static readonly Type singleType; + private static readonly Type doubleType; + private static readonly Type int16Type; + private static readonly Type int32Type; + private static readonly Type int64Type; + private static readonly Type boolType; + private static readonly Type typeType; static Converter() { @@ -151,8 +151,7 @@ internal static NewReference ToPython(object? value, Type type) // it the type is a python subclass of a managed type then return the // underlying python object rather than construct a new wrapper object. - var pyderived = value as IPythonDerivedType; - if (null != pyderived) + if (value is IPythonDerivedType pyderived) { if (!IsTransparentProxy(pyderived)) return ClassDerivedObject.ToPython(pyderived); @@ -161,7 +160,7 @@ internal static NewReference ToPython(object? value, Type type) // ModuleObjects are created in a way that their wrapping them as // a CLRObject fails, the ClassObject has no tpHandle. Return the // pyHandle as is, do not convert. - if (value is ModuleObject modobj) + if (value is ModuleObject) { throw new NotImplementedException(); } diff --git a/src/runtime/DelegateManager.cs b/src/runtime/DelegateManager.cs index 092c9be1d..4343b9ab7 100644 --- a/src/runtime/DelegateManager.cs +++ b/src/runtime/DelegateManager.cs @@ -15,13 +15,13 @@ namespace Python.Runtime /// internal class DelegateManager { - private readonly Dictionary cache = new Dictionary(); + private readonly Dictionary cache = new(); private readonly Type basetype = typeof(Dispatcher); private readonly Type arrayType = typeof(object[]); private readonly Type voidtype = typeof(void); private readonly Type typetype = typeof(Type); private readonly Type pyobjType = typeof(PyObject); - private readonly CodeGenerator codeGenerator = new CodeGenerator(); + private readonly CodeGenerator codeGenerator = new(); private readonly ConstructorInfo arrayCtor; private readonly MethodInfo dispatch; @@ -309,7 +309,7 @@ protected Dispatcher(PyObject target, Type dtype) { tpName += $" of size {Runtime.PyTuple_Size(op)}"; } - StringBuilder sb = new StringBuilder(); + var sb = new StringBuilder(); if (!isVoid) sb.Append(rtype.FullName); for (int i = 0; i < pi.Length; i++) { diff --git a/src/runtime/Exceptions.cs b/src/runtime/Exceptions.cs index c3ac889ed..da095e030 100644 --- a/src/runtime/Exceptions.cs +++ b/src/runtime/Exceptions.cs @@ -196,8 +196,7 @@ public static bool SetError(Exception e) // might get a managed exception raised that is a wrapper for a // Python exception. In that case we'd rather have the real thing. - var pe = e as PythonException; - if (pe != null) + if (e is PythonException pe) { pe.Restore(); return true; diff --git a/src/runtime/Finalizer.cs b/src/runtime/Finalizer.cs index be17d62e3..e796cacd1 100644 --- a/src/runtime/Finalizer.cs +++ b/src/runtime/Finalizer.cs @@ -41,7 +41,7 @@ public ErrorArgs(Exception error) [DefaultValue(true)] public bool Enable { get; set; } = true; - private ConcurrentQueue _objQueue = new(); + private readonly ConcurrentQueue _objQueue = new(); private readonly ConcurrentQueue _derivedQueue = new(); private readonly ConcurrentQueue _bufferQueue = new(); private int _throttled; diff --git a/src/runtime/InternString.cs b/src/runtime/InternString.cs index 0780a0bb8..b6d9a0e4a 100644 --- a/src/runtime/InternString.cs +++ b/src/runtime/InternString.cs @@ -61,8 +61,7 @@ public static void Shutdown() public static string? GetManagedString(BorrowedReference op) { - string s; - if (TryGetInterned(op, out s)) + if (TryGetInterned(op, out string s)) { return s; } diff --git a/src/runtime/Interop.cs b/src/runtime/Interop.cs index bcf99bede..4aa4aa09b 100644 --- a/src/runtime/Interop.cs +++ b/src/runtime/Interop.cs @@ -139,7 +139,7 @@ internal static Type GetPrototype(MethodInfo method) } - internal static Dictionary allocatedThunks = new Dictionary(); + internal static Dictionary allocatedThunks = new(); internal static ThunkInfo GetThunk(MethodInfo method) { diff --git a/src/runtime/InteropConfiguration.cs b/src/runtime/InteropConfiguration.cs index 30c9a1c2c..781d0d01f 100644 --- a/src/runtime/InteropConfiguration.cs +++ b/src/runtime/InteropConfiguration.cs @@ -9,7 +9,7 @@ namespace Python.Runtime public sealed class InteropConfiguration: IDisposable { internal readonly PythonBaseTypeProviderGroup pythonBaseTypeProviders - = new PythonBaseTypeProviderGroup(); + = new(); /// Enables replacing base types of CLR types as seen from Python public IList PythonBaseTypeProviders => this.pythonBaseTypeProviders; diff --git a/src/runtime/MethodBinder.cs b/src/runtime/MethodBinder.cs index be4e8d0e5..ebbf4489e 100644 --- a/src/runtime/MethodBinder.cs +++ b/src/runtime/MethodBinder.cs @@ -242,52 +242,24 @@ internal static int ArgPrecedence(Type t) TypeCode tc = Type.GetTypeCode(t); // TODO: Clean up - switch (tc) + return tc switch { - case TypeCode.Object: - return 1; - - case TypeCode.UInt64: - return 10; - - case TypeCode.UInt32: - return 11; - - case TypeCode.UInt16: - return 12; - - case TypeCode.Int64: - return 13; - - case TypeCode.Int32: - return 14; - - case TypeCode.Int16: - return 15; - - case TypeCode.Char: - return 16; - - case TypeCode.SByte: - return 17; - - case TypeCode.Byte: - return 18; - - case TypeCode.Single: - return 20; - - case TypeCode.Double: - return 21; - - case TypeCode.String: - return 30; - - case TypeCode.Boolean: - return 40; - } - - return 2000; + TypeCode.Object => 1, + TypeCode.UInt64 => 10, + TypeCode.UInt32 => 11, + TypeCode.UInt16 => 12, + TypeCode.Int64 => 13, + TypeCode.Int32 => 14, + TypeCode.Int16 => 15, + TypeCode.Char => 16, + TypeCode.SByte => 17, + TypeCode.Byte => 18, + TypeCode.Single => 20, + TypeCode.Double => 21, + TypeCode.String => 30, + TypeCode.Boolean => 40, + _ => 2000, + }; } /// @@ -410,10 +382,6 @@ public MismatchedMethod(Exception exception, MethodBase mb) isGeneric = true; } ParameterInfo[] pi = mi.GetParameters(); - ArrayList? defaultArgList; - bool paramsArray; - int kwargsMatched; - int defaultsNeeded; bool isOperator = OperatorMethod.IsOperatorMethod(mi); // Binary operator methods will have 2 CLR args but only one Python arg // (unary operators will have 1 less each), since Python operator methods are bound. @@ -421,7 +389,7 @@ public MismatchedMethod(Exception exception, MethodBase mb) bool isReverse = isOperator && OperatorMethod.IsReverse((MethodInfo)mi); // Only cast if isOperator. if (isReverse && OperatorMethod.IsComparisonOp((MethodInfo)mi)) continue; // Comparison operators in Python have no reverse mode. - if (!MatchesArgumentCount(pynargs, pi, kwargDict, out paramsArray, out defaultArgList, out kwargsMatched, out defaultsNeeded) && !isOperator) + if (!MatchesArgumentCount(pynargs, pi, kwargDict, out bool paramsArray, out ArrayList? defaultArgList, out int kwargsMatched, out int defaultsNeeded) && !isOperator) { continue; } @@ -436,8 +404,7 @@ public MismatchedMethod(Exception exception, MethodBase mb) // We need to take the first CLR argument. pi = pi.Take(1).ToArray(); } - int outs; - var margs = TryConvertArguments(pi, paramsArray, args, pynargs, kwargDict, defaultArgList, outs: out outs); + var margs = TryConvertArguments(pi, paramsArray, args, pynargs, kwargDict, defaultArgList, outs: out int outs); if (margs == null) { var mismatchCause = PythonException.FetchCurrent(); @@ -495,7 +462,7 @@ public MismatchedMethod(Exception exception, MethodBase mb) { // Best effort for determining method to match on gives multiple possible // matches and we need at least one default argument - bail from this point - StringBuilder stringBuilder = new StringBuilder("Not enough arguments provided to disambiguate the method. Found:"); + var stringBuilder = new StringBuilder("Not enough arguments provided to disambiguate the method. Found:"); foreach (var matchedMethod in argMatchedMethods) { stringBuilder.AppendLine(); @@ -523,18 +490,20 @@ public MismatchedMethod(Exception exception, MethodBase mb) //CLRObject co = (CLRObject)ManagedType.GetManagedObject(inst); // InvalidCastException: Unable to cast object of type // 'Python.Runtime.ClassObject' to type 'Python.Runtime.CLRObject' - var co = ManagedType.GetManagedObject(inst) as CLRObject; // Sanity check: this ensures a graceful exit if someone does // something intentionally wrong like call a non-static method // on the class rather than on an instance of the class. // XXX maybe better to do this before all the other rigmarole. - if (co == null) + if (ManagedType.GetManagedObject(inst) is CLRObject co) + { + target = co.inst; + } + else { Exceptions.SetError(Exceptions.TypeError, "Invoked a non-static method with an invalid instance"); return null; } - target = co.inst; } return new Binding(mi, target, margs, outs); @@ -623,7 +592,7 @@ static BorrowedReference HandleParamsArray(BorrowedReference args, int arrayStar for (int paramIndex = 0; paramIndex < pi.Length; paramIndex++) { var parameter = pi[paramIndex]; - bool hasNamedParam = parameter.Name != null ? kwargDict.ContainsKey(parameter.Name) : false; + bool hasNamedParam = parameter.Name != null && kwargDict.ContainsKey(parameter.Name); if (paramIndex >= pyArgCount && !(hasNamedParam || (paramsArray && paramIndex == arrayStart))) { @@ -658,8 +627,7 @@ static BorrowedReference HandleParamsArray(BorrowedReference args, int arrayStar } } - bool isOut; - if (!TryConvertArgument(op, parameter.ParameterType, out margs[paramIndex], out isOut)) + if (!TryConvertArgument(op, parameter.ParameterType, out margs[paramIndex], out bool isOut)) { tempObject.Dispose(); return null; @@ -789,7 +757,7 @@ static bool MatchesArgumentCount(int positionalArgumentCount, ParameterInfo[] pa { defaultArgList = null; var match = false; - paramsArray = parameters.Length > 0 ? Attribute.IsDefined(parameters[parameters.Length - 1], typeof(ParamArrayAttribute)) : false; + paramsArray = parameters.Length > 0 && Attribute.IsDefined(parameters[parameters.Length - 1], typeof(ParamArrayAttribute)); kwargsMatched = 0; defaultsNeeded = 0; if (positionalArgumentCount == parameters.Length && kwargDict.Count == 0) diff --git a/src/runtime/Native/BorrowedReference.cs b/src/runtime/Native/BorrowedReference.cs index 98c151bab..fd1059a5f 100644 --- a/src/runtime/Native/BorrowedReference.cs +++ b/src/runtime/Native/BorrowedReference.cs @@ -19,7 +19,7 @@ public IntPtr DangerousGetAddress() /// Gets a raw pointer to the Python object public IntPtr DangerousGetAddressOrNull() => this.pointer; - public static BorrowedReference Null => new BorrowedReference(); + public static BorrowedReference Null => new(); /// /// Creates new instance of from raw pointer. Unsafe. diff --git a/src/runtime/Native/CustomMarshaler.cs b/src/runtime/Native/CustomMarshaler.cs index f544756d8..62c027150 100644 --- a/src/runtime/Native/CustomMarshaler.cs +++ b/src/runtime/Native/CustomMarshaler.cs @@ -47,9 +47,7 @@ internal class UcsMarshaler : MarshalerBase public override IntPtr MarshalManagedToNative(object managedObj) { - var s = managedObj as string; - - if (s == null) + if (managedObj is not string s) { return IntPtr.Zero; } @@ -152,9 +150,7 @@ internal class StrArrayMarshaler : MarshalerBase public override IntPtr MarshalManagedToNative(object managedObj) { - var argv = managedObj as string[]; - - if (argv == null) + if (managedObj is not string[] argv) { return IntPtr.Zero; } diff --git a/src/runtime/Native/NewReference.cs b/src/runtime/Native/NewReference.cs index 91ebbdb01..25145fc4f 100644 --- a/src/runtime/Native/NewReference.cs +++ b/src/runtime/Native/NewReference.cs @@ -124,7 +124,7 @@ public void Dispose() /// [Pure] public static NewReference DangerousFromPointer(IntPtr pointer) - => new NewReference {pointer = pointer}; + => new() { pointer = pointer}; [Pure] internal static IntPtr DangerousGetAddressOrNull(in NewReference reference) => reference.pointer; diff --git a/src/runtime/Native/PyIdentifier_.cs b/src/runtime/Native/PyIdentifier_.cs index 4884a81ad..1ea2b704c 100644 --- a/src/runtime/Native/PyIdentifier_.cs +++ b/src/runtime/Native/PyIdentifier_.cs @@ -1,69 +1,69 @@ -using System; - -namespace Python.Runtime -{ - static class PyIdentifier +using System; + +namespace Python.Runtime +{ + static class PyIdentifier { -#pragma warning disable CS0649 // indentifier is never assigned to (assigned with reflection) - static IntPtr f__name__; - public static BorrowedReference __name__ => new(f__name__); - static IntPtr f__dict__; - public static BorrowedReference __dict__ => new(f__dict__); - static IntPtr f__doc__; - public static BorrowedReference __doc__ => new(f__doc__); - static IntPtr f__class__; - public static BorrowedReference __class__ => new(f__class__); - static IntPtr f__clear_reentry_guard__; - public static BorrowedReference __clear_reentry_guard__ => new(f__clear_reentry_guard__); - static IntPtr f__module__; - public static BorrowedReference __module__ => new(f__module__); - static IntPtr f__file__; - public static BorrowedReference __file__ => new(f__file__); - static IntPtr f__slots__; - public static BorrowedReference __slots__ => new(f__slots__); - static IntPtr f__self__; - public static BorrowedReference __self__ => new(f__self__); - static IntPtr f__annotations__; - public static BorrowedReference __annotations__ => new(f__annotations__); - static IntPtr f__init__; - public static BorrowedReference __init__ => new(f__init__); - static IntPtr f__repr__; - public static BorrowedReference __repr__ => new(f__repr__); - static IntPtr f__import__; - public static BorrowedReference __import__ => new(f__import__); - static IntPtr f__builtins__; - public static BorrowedReference __builtins__ => new(f__builtins__); - static IntPtr fbuiltins; - public static BorrowedReference builtins => new(fbuiltins); - static IntPtr f__overloads__; - public static BorrowedReference __overloads__ => new(f__overloads__); - static IntPtr fOverloads; +#pragma warning disable CS0649 // indentifier is never assigned to (assigned with reflection) + static IntPtr f__name__; + public static BorrowedReference __name__ => new(f__name__); + static IntPtr f__dict__; + public static BorrowedReference __dict__ => new(f__dict__); + static IntPtr f__doc__; + public static BorrowedReference __doc__ => new(f__doc__); + static IntPtr f__class__; + public static BorrowedReference __class__ => new(f__class__); + static IntPtr f__clear_reentry_guard__; + public static BorrowedReference __clear_reentry_guard__ => new(f__clear_reentry_guard__); + static IntPtr f__module__; + public static BorrowedReference __module__ => new(f__module__); + static IntPtr f__file__; + public static BorrowedReference __file__ => new(f__file__); + static IntPtr f__slots__; + public static BorrowedReference __slots__ => new(f__slots__); + static IntPtr f__self__; + public static BorrowedReference __self__ => new(f__self__); + static IntPtr f__annotations__; + public static BorrowedReference __annotations__ => new(f__annotations__); + static IntPtr f__init__; + public static BorrowedReference __init__ => new(f__init__); + static IntPtr f__repr__; + public static BorrowedReference __repr__ => new(f__repr__); + static IntPtr f__import__; + public static BorrowedReference __import__ => new(f__import__); + static IntPtr f__builtins__; + public static BorrowedReference __builtins__ => new(f__builtins__); + static IntPtr fbuiltins; + public static BorrowedReference builtins => new(fbuiltins); + static IntPtr f__overloads__; + public static BorrowedReference __overloads__ => new(f__overloads__); + static IntPtr fOverloads; public static BorrowedReference Overloads => new(fOverloads); #pragma warning restore CS0649 // indentifier is never assigned to (assigned with reflection) - } - - - static partial class InternString - { - private static readonly string[] _builtinNames = new string[] - { - "__name__", - "__dict__", - "__doc__", - "__class__", - "__clear_reentry_guard__", - "__module__", - "__file__", - "__slots__", - "__self__", - "__annotations__", - "__init__", - "__repr__", - "__import__", - "__builtins__", - "builtins", - "__overloads__", - "Overloads", - }; - } -} + } + + + static partial class InternString + { + private static readonly string[] _builtinNames = new string[] + { + "__name__", + "__dict__", + "__doc__", + "__class__", + "__clear_reentry_guard__", + "__module__", + "__file__", + "__slots__", + "__self__", + "__annotations__", + "__init__", + "__repr__", + "__import__", + "__builtins__", + "builtins", + "__overloads__", + "Overloads", + }; + } +} diff --git a/src/runtime/Native/PyIdentifier_.tt b/src/runtime/Native/PyIdentifier_.tt index 03a26cb50..9350cde43 100644 --- a/src/runtime/Native/PyIdentifier_.tt +++ b/src/runtime/Native/PyIdentifier_.tt @@ -1,62 +1,62 @@ -<#@ template debug="true" hostSpecific="true" #> -<#@ output extension=".cs" #> -<# - string[] internNames = new string[] - { - "__name__", - "__dict__", - "__doc__", - "__class__", - "__clear_reentry_guard__", - "__module__", - "__file__", - "__slots__", - "__self__", - "__annotations__", - - "__init__", - "__repr__", - "__import__", - "__builtins__", - - "builtins", - - "__overloads__", - "Overloads", - }; -#> -using System; - -namespace Python.Runtime -{ - static class PyIdentifier +<#@ template debug="true" hostSpecific="true" #> +<#@ output extension=".cs" #> +<# + string[] internNames = new string[] { -#pragma warning disable CS0649 // indentifier is never assigned to (assigned with reflection) -<# - foreach (var name in internNames) - { -#> - static IntPtr f<#= name #>; - public static BorrowedReference <#= name #> => new(f<#= name #>); -<# - } + "__name__", + "__dict__", + "__doc__", + "__class__", + "__clear_reentry_guard__", + "__module__", + "__file__", + "__slots__", + "__self__", + "__annotations__", + + "__init__", + "__repr__", + "__import__", + "__builtins__", + + "builtins", + + "__overloads__", + "Overloads", + }; #> -#pragma warning restore CS0649 // indentifier is never assigned to (assigned with reflection) - } - - - static partial class InternString - { - private static readonly string[] _builtinNames = new string[] - { -<# - foreach (var name in internNames) - { -#> - "<#= name #>", -<# - } -#> - }; - } -} +using System; + +namespace Python.Runtime +{ + static class PyIdentifier + { +#pragma warning disable CS0649 // indentifier is never assigned to (assigned with reflection) +<# + foreach (var name in internNames) + { +#> + static IntPtr f<#= name #>; + public static BorrowedReference <#= name #> => new(f<#= name #>); +<# + } +#> +#pragma warning restore CS0649 // indentifier is never assigned to (assigned with reflection) + } + + + static partial class InternString + { + private static readonly string[] _builtinNames = new string[] + { +<# + foreach (var name in internNames) + { +#> + "<#= name #>", +<# + } +#> + }; + } +} diff --git a/src/runtime/Native/TypeOffset.cs b/src/runtime/Native/TypeOffset.cs index a1bae8253..59c5c3ee0 100644 --- a/src/runtime/Native/TypeOffset.cs +++ b/src/runtime/Native/TypeOffset.cs @@ -121,7 +121,7 @@ public static int GetSlotOffset(string slotName) public static string? GetSlotName(int offset) => SlotOffsets.FirstOrDefault(kv => kv.Value == offset).Key; - static readonly HashSet slotNames = new HashSet(); + static readonly HashSet slotNames = new(); internal static bool IsSupportedSlotName(string name) => slotNames.Contains(name); [Conditional("DEBUG")] diff --git a/src/runtime/Py.cs b/src/runtime/Py.cs index 4f3fbf6d4..ec991f971 100644 --- a/src/runtime/Py.cs +++ b/src/runtime/Py.cs @@ -77,8 +77,7 @@ public static KeywordArguments kw(params object?[] kv) } for (var i = 0; i < kv.Length; i += 2) { - var key = kv[i] as string; - if (key is null) + if (kv[i] is not string key) throw new ArgumentException("Keys must be non-null strings"); BorrowedReference value; diff --git a/src/runtime/PythonEngine.cs b/src/runtime/PythonEngine.cs index 1e82446cb..24e1bedeb 100644 --- a/src/runtime/PythonEngine.cs +++ b/src/runtime/PythonEngine.cs @@ -233,7 +233,7 @@ public static void Initialize(IEnumerable args, bool setSysArgv = true, // add the imported module to the clr module, and copy the API functions // and decorators into the main clr module. Runtime.PyDict_SetItemString(clr_dict, "_extras", module); - using (var keys = locals.Keys()) + using var keys = locals.Keys(); foreach (PyObject key in keys) { if (!key.ToString()!.StartsWith("_") || key.ToString()!.Equals("__version__")) @@ -374,7 +374,7 @@ public static void Shutdown() /// public delegate void ShutdownHandler(); - static List ShutdownHandlers = new List(); + static readonly List ShutdownHandlers = new(); /// /// Add a function to be called when the engine is shut down. diff --git a/src/runtime/PythonException.cs b/src/runtime/PythonException.cs index 813d0e586..8d3330c7b 100644 --- a/src/runtime/PythonException.cs +++ b/src/runtime/PythonException.cs @@ -397,8 +397,13 @@ public string Format() } public PythonException Clone() - => new PythonException(type: Type, value: Value, traceback: Traceback, - Message, InnerException); + => new( + type: Type, + value: Value, + traceback: Traceback, + Message, + InnerException + ); #region Serializable [SecurityPermission(SecurityAction.Demand, SerializationFormatter = true)] diff --git a/src/runtime/PythonTypes/PyDict.cs b/src/runtime/PythonTypes/PyDict.cs index 80b8c8c9f..272f7828f 100644 --- a/src/runtime/PythonTypes/PyDict.cs +++ b/src/runtime/PythonTypes/PyDict.cs @@ -72,10 +72,8 @@ public bool HasKey(PyObject key) /// public bool HasKey(string key) { - using (var str = new PyString(key)) - { - return HasKey(str); - } + using var str = new PyString(key); + return HasKey(str); } diff --git a/src/runtime/PythonTypes/PyFloat.cs b/src/runtime/PythonTypes/PyFloat.cs index 10104c10f..c09ec93ba 100644 --- a/src/runtime/PythonTypes/PyFloat.cs +++ b/src/runtime/PythonTypes/PyFloat.cs @@ -53,12 +53,10 @@ private static StolenReference FromString(string value) { if (value is null) throw new ArgumentNullException(nameof(value)); - using (var s = new PyString(value)) - { - NewReference val = Runtime.PyFloat_FromString(s.Reference); - PythonException.ThrowIfIsNull(val); - return val.Steal(); - } + using var s = new PyString(value); + NewReference val = Runtime.PyFloat_FromString(s.Reference); + PythonException.ThrowIfIsNull(val); + return val.Steal(); } /// diff --git a/src/runtime/PythonTypes/PyModule.cs b/src/runtime/PythonTypes/PyModule.cs index ada24c6cd..4549678ed 100644 --- a/src/runtime/PythonTypes/PyModule.cs +++ b/src/runtime/PythonTypes/PyModule.cs @@ -322,13 +322,11 @@ public PyModule Set(string name, object? value) private void SetPyValue(string name, BorrowedReference value) { Check(); - using (var pyKey = new PyString(name)) + using var pyKey = new PyString(name); + int r = Runtime.PyObject_SetItem(variables, pyKey.obj, value); + if (r < 0) { - int r = Runtime.PyObject_SetItem(variables, pyKey.obj, value); - if (r < 0) - { - throw PythonException.ThrowLastAsClrException(); - } + throw PythonException.ThrowLastAsClrException(); } } @@ -362,10 +360,8 @@ public bool Contains(string name) if (name is null) throw new ArgumentNullException(nameof(name)); Check(); - using (var pyKey = new PyString(name)) - { - return Runtime.PyMapping_HasKey(variables, pyKey.obj) != 0; - } + using var pyKey = new PyString(name); + return Runtime.PyMapping_HasKey(variables, pyKey.obj) != 0; } /// @@ -398,19 +394,17 @@ public bool TryGet(string name, out PyObject? value) if (name is null) throw new ArgumentNullException(nameof(name)); Check(); - using (var pyKey = new PyString(name)) + using var pyKey = new PyString(name); + if (Runtime.PyMapping_HasKey(variables, pyKey.obj) != 0) { - if (Runtime.PyMapping_HasKey(variables, pyKey.obj) != 0) - { - using var op = Runtime.PyObject_GetItem(variables, pyKey.obj); - value = new PyObject(op.StealOrThrow()); - return true; - } - else - { - value = null; - return false; - } + using var op = Runtime.PyObject_GetItem(variables, pyKey.obj); + value = new PyObject(op.StealOrThrow()); + return true; + } + else + { + value = null; + return false; } } @@ -445,7 +439,7 @@ public bool TryGet(string name, out T? value) var result = TryGet(name, out var pyObj); if (!result) { - value = default(T); + value = default; return false; } value = pyObj!.As(); diff --git a/src/runtime/PythonTypes/PyObject.cs b/src/runtime/PythonTypes/PyObject.cs index e0a17bed5..cfd3e7158 100644 --- a/src/runtime/PythonTypes/PyObject.cs +++ b/src/runtime/PythonTypes/PyObject.cs @@ -539,10 +539,8 @@ public virtual PyObject GetItem(string key) { if (key == null) throw new ArgumentNullException(nameof(key)); - using (var pyKey = new PyString(key)) - { - return GetItem(pyKey); - } + using var pyKey = new PyString(key); + return GetItem(pyKey); } @@ -556,10 +554,8 @@ public virtual PyObject GetItem(string key) /// public virtual PyObject GetItem(int index) { - using (var key = new PyInt(index)) - { - return GetItem(key); - } + using var key = new PyInt(index); + return GetItem(key); } @@ -597,10 +593,8 @@ public virtual void SetItem(string key, PyObject value) if (key == null) throw new ArgumentNullException(nameof(key)); if (value == null) throw new ArgumentNullException(nameof(value)); - using (var pyKey = new PyString(key)) - { - SetItem(pyKey, value); - } + using var pyKey = new PyString(key); + SetItem(pyKey, value); } @@ -616,10 +610,8 @@ public virtual void SetItem(int index, PyObject value) { if (value == null) throw new ArgumentNullException(nameof(value)); - using (var pyindex = new PyInt(index)) - { - SetItem(pyindex, value); - } + using var pyindex = new PyInt(index); + SetItem(pyindex, value); } @@ -655,10 +647,8 @@ public virtual void DelItem(string key) { if (key == null) throw new ArgumentNullException(nameof(key)); - using (var pyKey = new PyString(key)) - { - DelItem(pyKey); - } + using var pyKey = new PyString(key); + DelItem(pyKey); } @@ -672,10 +662,8 @@ public virtual void DelItem(string key) /// public virtual void DelItem(int index) { - using (var pyindex = new PyInt(index)) - { - DelItem(pyindex); - } + using var pyindex = new PyInt(index); + DelItem(pyindex); } @@ -1320,7 +1308,7 @@ public override bool TryBinaryOperation(BinaryOperationBinder binder, object arg { using var _ = Py.GIL(); NewReference res; - if (!(arg is PyObject)) + if (arg is not PyObject) { arg = arg.ToPython(); } diff --git a/src/runtime/Runtime.cs b/src/runtime/Runtime.cs index d92f45afb..6ad1d459f 100644 --- a/src/runtime/Runtime.cs +++ b/src/runtime/Runtime.cs @@ -554,17 +554,17 @@ internal static void CheckExceptionOccurred() } ManagedType? mt = ManagedType.GetManagedObject(op); - if (mt is ClassBase) + if (mt is ClassBase b) { - MaybeType _type = ((ClassBase)mt).type; + var _type = b.type; t = _type.Valid ? _type.Value : null; } - else if (mt is CLRObject) + else if (mt is CLRObject ob) { - object inst = ((CLRObject)mt).inst; - if (inst is Type) + var inst = ob.inst; + if (inst is Type ty) { - t = inst as Type; + t = ty; } } else diff --git a/src/runtime/StateSerialization/MaybeMemberInfo.cs b/src/runtime/StateSerialization/MaybeMemberInfo.cs index aa369a5ed..b734bb070 100644 --- a/src/runtime/StateSerialization/MaybeMemberInfo.cs +++ b/src/runtime/StateSerialization/MaybeMemberInfo.cs @@ -12,10 +12,10 @@ internal struct MaybeMemberInfo : ISerializable where T : MemberInfo // The ReflectedType of the object const string SerializationType = "t"; const string SerializationMemberName = "n"; - MemberInfo? info; + readonly MemberInfo? info; [NonSerialized] - Exception? deserializationException; + readonly Exception? deserializationException; public string DeletedMessage { diff --git a/src/runtime/StateSerialization/MaybeMethodBase.cs b/src/runtime/StateSerialization/MaybeMethodBase.cs index 9fb8ae047..d196d5e88 100644 --- a/src/runtime/StateSerialization/MaybeMethodBase.cs +++ b/src/runtime/StateSerialization/MaybeMethodBase.cs @@ -24,11 +24,11 @@ internal struct MaybeMethodBase : ISerializable where T: MethodBase public static implicit operator MaybeMethodBase (T? ob) => new (ob); - string? name; - MethodBase? info; + readonly string? name; + readonly MethodBase? info; [NonSerialized] - Exception? deserializationException; + readonly Exception? deserializationException; public string DeletedMessage { diff --git a/src/runtime/StateSerialization/MaybeType.cs b/src/runtime/StateSerialization/MaybeType.cs index abb3a8fb6..f3c96e369 100644 --- a/src/runtime/StateSerialization/MaybeType.cs +++ b/src/runtime/StateSerialization/MaybeType.cs @@ -9,12 +9,12 @@ namespace Python.Runtime [Serializable] internal struct MaybeType : ISerializable { - public static implicit operator MaybeType (Type ob) => new MaybeType(ob); + public static implicit operator MaybeType (Type ob) => new(ob); // The AssemblyQualifiedName of the serialized Type const string SerializationName = "n"; - string name; - Type type; + readonly string name; + readonly Type type; public string DeletedMessage { diff --git a/src/runtime/TypeManager.cs b/src/runtime/TypeManager.cs index 84618df64..d9ca184f6 100644 --- a/src/runtime/TypeManager.cs +++ b/src/runtime/TypeManager.cs @@ -26,9 +26,9 @@ internal class TypeManager private const BindingFlags tbFlags = BindingFlags.Public | BindingFlags.Static; - private static Dictionary cache = new(); + private static readonly Dictionary cache = new(); - static readonly Dictionary _slotsHolders = new Dictionary(PythonReferenceComparer.Instance); + static readonly Dictionary _slotsHolders = new(PythonReferenceComparer.Instance); // Slots which must be set private static readonly string[] _requiredSlots = new string[] @@ -84,7 +84,7 @@ internal static void RestoreRuntimeData(TypeManagerState storage) var typeCache = storage.Cache; foreach (var entry in typeCache) { - Type type = entry.Key.Value;; + var type = entry.Key.Value; cache![type] = entry.Value; SlotsHolder holder = CreateSlotsHolder(entry.Value); InitializeSlots(entry.Value, type, holder); @@ -385,31 +385,30 @@ internal static NewReference CreateSubType(BorrowedReference py_name, BorrowedRe return Exceptions.RaiseTypeError("Couldn't convert __assembly__ value to string"); } - using (var namespaceKey = new PyString("__namespace__")) + using var namespaceKey = new PyString("__namespace__"); + var pyNamespace = Runtime.PyDict_GetItemWithError(dictRef, namespaceKey.Reference); + if (pyNamespace.IsNull) { - var pyNamespace = Runtime.PyDict_GetItemWithError(dictRef, namespaceKey.Reference); - if (pyNamespace.IsNull) - { - if (Exceptions.ErrorOccurred()) return default; - } - else if (!Converter.ToManagedValue(pyNamespace, typeof(string), out namespaceStr, true)) - { - return Exceptions.RaiseTypeError("Couldn't convert __namespace__ value to string"); - } + if (Exceptions.ErrorOccurred()) return default; + } + else if (!Converter.ToManagedValue(pyNamespace, typeof(string), out namespaceStr, true)) + { + return Exceptions.RaiseTypeError("Couldn't convert __namespace__ value to string"); } } // create the new managed type subclassing the base managed type - var baseClass = ManagedType.GetManagedObject(py_base_type) as ClassBase; - if (null == baseClass) + if (ManagedType.GetManagedObject(py_base_type) is ClassBase baseClass) + { + return ReflectedClrType.CreateSubclass(baseClass, name, + ns: (string?)namespaceStr, + assembly: (string?)assembly, + dict: dictRef); + } + else { return Exceptions.RaiseTypeError("invalid base class, expected CLR class type"); } - - return ReflectedClrType.CreateSubclass(baseClass, name, - ns: (string?)namespaceStr, - assembly: (string?)assembly, - dict: dictRef); } internal static IntPtr WriteMethodDef(IntPtr mdef, IntPtr name, IntPtr func, PyMethodFlags flags, IntPtr doc) @@ -526,7 +525,7 @@ internal static PyType CreateMetaType(Type impl, out SlotsHolder slotsHolder) internal static SlotsHolder SetupMetaSlots(Type impl, PyType type) { // Override type slots with those of the managed implementation. - SlotsHolder slotsHolder = new SlotsHolder(type); + var slotsHolder = new SlotsHolder(type); InitializeSlots(type, impl, slotsHolder); // We need space for 3 PyMethodDef structs. @@ -683,7 +682,7 @@ private static void SetRequiredSlots(PyType type, HashSet seen) static void InitializeSlot(BorrowedReference type, ThunkInfo thunk, string name, SlotsHolder? slotsHolder) { - if (!Enum.TryParse(name, out var id)) + if (!Enum.TryParse(name, out _)) { throw new NotSupportedException("Bad slot name " + name); } @@ -741,10 +740,10 @@ class SlotsHolder { public delegate void Resetor(PyType type, int offset); - private Dictionary _slots = new Dictionary(); - private List _keepalive = new List(); - private Dictionary _customResetors = new Dictionary(); - private List _deallocators = new List(); + private readonly Dictionary _slots = new(); + private readonly List _keepalive = new(); + private readonly Dictionary _customResetors = new(); + private readonly List _deallocators = new(); private bool _alreadyReset = false; private readonly PyType Type; diff --git a/src/runtime/Types/ArrayObject.cs b/src/runtime/Types/ArrayObject.cs index 3ca09ddce..bda717e56 100644 --- a/src/runtime/Types/ArrayObject.cs +++ b/src/runtime/Types/ArrayObject.cs @@ -63,14 +63,16 @@ public static NewReference tp_new(BorrowedReference tp, BorrowedReference args, return NewInstance(arrType.GetElementType(), tp, dimensions); } } - object? result; // this implements casting to Array[T] - if (!Converter.ToManaged(op, arrType, out result, true)) + if (Converter.ToManaged(op, arrType, out object? result, true)) + { + return CLRObject.GetReference(result!, tp); + } + else { return default; } - return CLRObject.GetReference(result!, tp); } static NewReference CreateMultidimensional(Type elementType, long[] dimensions, BorrowedReference shapeTuple, BorrowedReference pyType) @@ -250,7 +252,6 @@ public static int mp_ass_subscript(BorrowedReference ob, BorrowedReference idx, Type itemType = obj.inst.GetType().GetElementType(); int rank = items.Rank; long index; - object? value; if (items.IsReadOnly) { @@ -258,7 +259,7 @@ public static int mp_ass_subscript(BorrowedReference ob, BorrowedReference idx, return -1; } - if (!Converter.ToManaged(v, itemType, out value, true)) + if (!Converter.ToManaged(v, itemType, out object? value, true)) { return -1; } @@ -353,9 +354,8 @@ public static int sq_contains(BorrowedReference ob, BorrowedReference v) var obj = (CLRObject)GetManagedObject(ob)!; Type itemType = obj.inst.GetType().GetElementType(); var items = (IList)obj.inst; - object? value; - if (!Converter.ToManaged(v, itemType, out value, false)) + if (!Converter.ToManaged(v, itemType, out object? value, false)) { return 0; } @@ -397,7 +397,8 @@ static int GetBuffer(BorrowedReference obj, out Py_buffer buffer, PyBUF flags) try { gcHandle = GCHandle.Alloc(self, GCHandleType.Pinned); - } catch (ArgumentException ex) + } + catch (ArgumentException ex) { Exceptions.SetError(Exceptions.BufferError, ex.Message); return -1; @@ -410,7 +411,7 @@ static int GetBuffer(BorrowedReference obj, out Py_buffer buffer, PyBUF flags) { buf = gcHandle.AddrOfPinnedObject(), obj = new NewReference(obj).DangerousMoveToPointer(), - len = (IntPtr)(self.LongLength*itemSize), + len = (IntPtr)(self.LongLength * itemSize), itemsize = (IntPtr)itemSize, _readonly = false, ndim = self.Rank, @@ -476,7 +477,7 @@ static unsafe IntPtr ToUnmanaged(T[] array) where T : unmanaged return result; } - static readonly Dictionary ItemFormats = new Dictionary + static readonly Dictionary ItemFormats = new() { [typeof(byte)] = "B", [typeof(sbyte)] = "b", diff --git a/src/runtime/Types/ClassBase.cs b/src/runtime/Types/ClassBase.cs index 6066e5fec..1e3c325cc 100644 --- a/src/runtime/Types/ClassBase.cs +++ b/src/runtime/Types/ClassBase.cs @@ -41,7 +41,7 @@ internal virtual bool CanSubclass() return !type.Value.IsEnum; } - public readonly static Dictionary CilToPyOpMap = new Dictionary + public readonly static Dictionary CilToPyOpMap = new() { ["op_Equality"] = Runtime.Py_EQ, ["op_Inequality"] = Runtime.Py_NE, @@ -153,8 +153,7 @@ public static NewReference tp_richcompare(BorrowedReference ob, BorrowedReferenc { return Exceptions.RaiseTypeError("Cannot get managed object"); } - var co1Comp = co1.inst as IComparable; - if (co1Comp == null) + if (co1.inst is not IComparable co1Comp) { Type co1Type = co1.GetType(); return Exceptions.RaiseTypeError($"Cannot convert object of type {co1Type} to IComparable"); @@ -215,15 +214,13 @@ public static NewReference tp_richcompare(BorrowedReference ob, BorrowedReferenc /// static NewReference tp_iter_impl(BorrowedReference ob) { - var co = GetManagedObject(ob) as CLRObject; - if (co == null) + if (GetManagedObject(ob) is not CLRObject co) { return Exceptions.RaiseTypeError("invalid object"); } - var e = co.inst as IEnumerable; IEnumerator? o; - if (e != null) + if (co.inst is IEnumerable e) { o = e.GetEnumerator(); } @@ -239,7 +236,7 @@ static NewReference tp_iter_impl(BorrowedReference ob) var elemType = typeof(object); var iterType = co.inst.GetType(); - foreach(var ifc in iterType.GetInterfaces()) + foreach (var ifc in iterType.GetInterfaces()) { if (ifc.IsGenericType) { @@ -261,13 +258,15 @@ static NewReference tp_iter_impl(BorrowedReference ob) /// public static nint tp_hash(BorrowedReference ob) { - var co = GetManagedObject(ob) as CLRObject; - if (co == null) + if (GetManagedObject(ob) is CLRObject co) + { + return co.inst.GetHashCode(); + } + else { Exceptions.RaiseTypeError("unhashable type"); return 0; } - return co.inst.GetHashCode(); } @@ -298,8 +297,7 @@ public static NewReference tp_str(BorrowedReference ob) public static NewReference tp_repr(BorrowedReference ob) { - var co = GetManagedObject(ob) as CLRObject; - if (co == null) + if (GetManagedObject(ob) is not CLRObject co) { return Exceptions.RaiseTypeError("invalid object"); } @@ -307,11 +305,17 @@ public static NewReference tp_repr(BorrowedReference ob) { //if __repr__ is defined, use it var instType = co.inst.GetType(); - System.Reflection.MethodInfo methodInfo = instType.GetMethod("__repr__"); + var methodInfo = instType.GetMethod("__repr__"); if (methodInfo != null && methodInfo.IsPublic) { - var reprString = methodInfo.Invoke(co.inst, null) as string; - return reprString is null ? new NewReference(Runtime.PyNone) : Runtime.PyString_FromString(reprString); + if (methodInfo.Invoke(co.inst, null) is string reprString) + { + return Runtime.PyString_FromString(reprString); + } + else + { + return new NewReference(Runtime.PyNone); + } } //otherwise use the standard object.__repr__(inst) diff --git a/src/runtime/Types/ClassDerived.cs b/src/runtime/Types/ClassDerived.cs index 6c2c81b13..dd1f4595f 100644 --- a/src/runtime/Types/ClassDerived.cs +++ b/src/runtime/Types/ClassDerived.cs @@ -107,7 +107,8 @@ internal static NewReference ToPython(IPythonDerivedType obj) try { self = GetPyObj(obj).CheckRun(); - } catch (RuntimeShutdownException e) + } + catch (RuntimeShutdownException e) { Exceptions.SetError(e); return default; @@ -197,22 +198,19 @@ internal static Type CreateDerivedType(string name, if (py_dict != null && Runtime.PyDict_Check(py_dict)) { using var dict = new PyDict(py_dict); - using (PyIterable keys = dict.Keys()) + using var keys = dict.Keys(); + foreach (PyObject pyKey in keys) { - foreach (PyObject pyKey in keys) + using var value = dict[pyKey]; + if (value.HasAttr("_clr_property_type_")) { - using (PyObject value = dict[pyKey]) - { - if (value.HasAttr("_clr_property_type_")) - { - string propertyName = pyKey.ToString()!; - pyProperties.Add(propertyName); + string propertyName = pyKey.ToString()!; + pyProperties.Add(propertyName); - // Add the property to the type - AddPythonProperty(propertyName, value, typeBuilder); - } - } + // Add the property to the type + AddPythonProperty(propertyName, value, typeBuilder); } + pyKey.Dispose(); } } @@ -245,27 +243,24 @@ internal static Type CreateDerivedType(string name, if (py_dict != null && Runtime.PyDict_Check(py_dict)) { using var dict = new PyDict(py_dict); - using (PyIterable keys = dict.Keys()) + using var keys = dict.Keys(); + foreach (PyObject pyKey in keys) { - foreach (PyObject pyKey in keys) + using var value = dict[pyKey]; + if (value.HasAttr("_clr_return_type_") && value.HasAttr("_clr_arg_types_")) { - using (PyObject value = dict[pyKey]) - { - if (value.HasAttr("_clr_return_type_") && value.HasAttr("_clr_arg_types_")) - { - string methodName = pyKey.ToString()!; + string methodName = pyKey.ToString()!; - // if this method has already been redirected to the python method skip it - if (virtualMethods.Contains(methodName)) - { - continue; - } - - // Add the method to the type - AddPythonMethod(methodName, value, typeBuilder); - } + // if this method has already been redirected to the python method skip it + if (virtualMethods.Contains(methodName)) + { + continue; } + + // Add the method to the type + AddPythonMethod(methodName, value, typeBuilder); } + pyKey.Dispose(); } } @@ -868,10 +863,8 @@ public static void InvokeMethodVoid(IPythonDerivedType obj, string methodName, s try { using var pyself = new PyObject(self.CheckRun()); - using (PyObject pyvalue = pyself.GetAttr(propertyName)) - { - return pyvalue.As(); - } + using var pyvalue = pyself.GetAttr(propertyName); + return pyvalue.As(); } finally { diff --git a/src/runtime/Types/ClassObject.cs b/src/runtime/Types/ClassObject.cs index 5ba83c25e..04613afa5 100644 --- a/src/runtime/Types/ClassObject.cs +++ b/src/runtime/Types/ClassObject.cs @@ -53,12 +53,10 @@ internal NewReference GetDocString() /// static NewReference tp_new_impl(BorrowedReference tp, BorrowedReference args, BorrowedReference kw) { - var self = GetManagedObject(tp) as ClassObject; - // Sanity check: this ensures a graceful error if someone does // something intentially wrong like use the managed metatype for // a class that is not really derived from a managed class. - if (self == null) + if (GetManagedObject(tp) is not ClassObject self) { return Exceptions.RaiseTypeError("invalid object"); } @@ -224,8 +222,15 @@ public override NewReference type_subscript(BorrowedReference idx) { return Exceptions.RaiseTypeError("type expected"); } - var c = GetManagedObject(idx) as ClassBase; - Type? t = c != null ? c.type.Value : Converter.GetTypeByAlias(idx); + Type? t; + if (GetManagedObject(idx) is ClassBase c) + { + t = c.type.Value; + } + else + { + t = Converter.GetTypeByAlias(idx); + } if (t == null) { return Exceptions.RaiseTypeError("type expected"); diff --git a/src/runtime/Types/DelegateObject.cs b/src/runtime/Types/DelegateObject.cs index 43a75aba7..cbc32696e 100644 --- a/src/runtime/Types/DelegateObject.cs +++ b/src/runtime/Types/DelegateObject.cs @@ -11,7 +11,7 @@ namespace Python.Runtime [Serializable] internal class DelegateObject : ClassBase { - private MethodBinder binder; + private readonly MethodBinder binder; internal DelegateObject(Type tp) : base(tp) { @@ -25,8 +25,7 @@ internal DelegateObject(Type tp) : base(tp) /// private static Delegate? GetTrueDelegate(BorrowedReference op) { - var o = GetManagedObject(op) as CLRObject; - if (o != null) + if (GetManagedObject(op) is CLRObject o) { var d = o.inst as Delegate; return d; @@ -83,20 +82,12 @@ public static NewReference tp_call(BorrowedReference ob, BorrowedReference args, // TODO: add fast type check! BorrowedReference pytype = Runtime.PyObject_TYPE(ob); var self = (DelegateObject)GetManagedObject(pytype)!; - var o = GetManagedObject(ob) as CLRObject; - if (o == null) + if (GetManagedObject(ob) is CLRObject o && o.inst is Delegate _) { - return Exceptions.RaiseTypeError("invalid argument"); + return self.binder.Invoke(ob, args, kw); } - - var d = o.inst as Delegate; - - if (d == null) - { - return Exceptions.RaiseTypeError("invalid argument"); - } - return self.binder.Invoke(ob, args, kw); + return Exceptions.RaiseTypeError("invalid argument"); } diff --git a/src/runtime/Types/EventBinding.cs b/src/runtime/Types/EventBinding.cs index 69ca8f88e..9eb2382ec 100644 --- a/src/runtime/Types/EventBinding.cs +++ b/src/runtime/Types/EventBinding.cs @@ -12,7 +12,7 @@ internal class EventBinding : ExtensionType { private readonly string name; private readonly EventHandlerCollection e; - private PyObject? target; + private readonly PyObject? target; public EventBinding(string name, EventHandlerCollection e, PyObject? target) { diff --git a/src/runtime/Types/EventObject.cs b/src/runtime/Types/EventObject.cs index 90346f2d2..a682bfebd 100644 --- a/src/runtime/Types/EventObject.cs +++ b/src/runtime/Types/EventObject.cs @@ -57,9 +57,7 @@ public static NewReference tp_descr_get(BorrowedReference ds, BorrowedReference /// public static int tp_descr_set(BorrowedReference ds, BorrowedReference ob, BorrowedReference val) { - var e = GetManagedObject(val) as EventBinding; - - if (e != null) + if (GetManagedObject(val) is EventBinding _) { return 0; } diff --git a/src/runtime/Types/ExtensionType.cs b/src/runtime/Types/ExtensionType.cs index 439bd3314..305fdc15d 100644 --- a/src/runtime/Types/ExtensionType.cs +++ b/src/runtime/Types/ExtensionType.cs @@ -40,7 +40,7 @@ public virtual NewReference Alloc() return py; } - public PyObject AllocObject() => new PyObject(Alloc().Steal()); + public PyObject AllocObject() => new(Alloc().Steal()); // "borrowed" references internal static readonly HashSet loadedExtensions = new(); diff --git a/src/runtime/Types/InterfaceObject.cs b/src/runtime/Types/InterfaceObject.cs index b7e865b62..2d82392bf 100644 --- a/src/runtime/Types/InterfaceObject.cs +++ b/src/runtime/Types/InterfaceObject.cs @@ -27,7 +27,7 @@ internal InterfaceObject(Type tp) : base(tp) return comClass?.CoClass.GetConstructor(Type.EmptyTypes); } - private static Type cc_attr; + private static readonly Type cc_attr; static InterfaceObject() { @@ -51,15 +51,16 @@ public static NewReference tp_new(BorrowedReference tp, BorrowedReference args, if (nargs == 1) { BorrowedReference inst = Runtime.PyTuple_GetItem(args, 0); - var co = GetManagedObject(inst) as CLRObject; - if (co == null || !type.IsInstanceOfType(co.inst)) + if (GetManagedObject(inst) is CLRObject co && type.IsInstanceOfType(co.inst)) + { + obj = co.inst; + } + else { Exceptions.SetError(Exceptions.TypeError, $"object does not implement {type.Name}"); return default; } - - obj = co.inst; } else if (nargs == 0 && self.ctor != null) diff --git a/src/runtime/Types/Iterator.cs b/src/runtime/Types/Iterator.cs index 829ff8a7a..49145d2c3 100644 --- a/src/runtime/Types/Iterator.cs +++ b/src/runtime/Types/Iterator.cs @@ -9,8 +9,8 @@ namespace Python.Runtime /// internal class Iterator : ExtensionType { - private IEnumerator iter; - private Type elemType; + private readonly IEnumerator iter; + private readonly Type elemType; public Iterator(IEnumerator e, Type elemType) { diff --git a/src/runtime/Types/MetaType.cs b/src/runtime/Types/MetaType.cs index 1543711f6..5b59f5139 100644 --- a/src/runtime/Types/MetaType.cs +++ b/src/runtime/Types/MetaType.cs @@ -100,8 +100,7 @@ public static NewReference tp_new(BorrowedReference tp, BorrowedReference args, // Ensure that the reflected type is appropriate for subclassing, // disallowing subclassing of delegates, enums and array types. - var cb = GetManagedObject(base_type) as ClassBase; - if (cb != null) + if (GetManagedObject(base_type) is ClassBase cb) { try { @@ -128,12 +127,10 @@ public static NewReference tp_new(BorrowedReference tp, BorrowedReference args, // into python. if (null != dict) { - using (var clsDict = new PyDict(dict)) + using var clsDict = new PyDict(dict); + if (clsDict.HasKey("__assembly__") || clsDict.HasKey("__namespace__")) { - if (clsDict.HasKey("__assembly__") || clsDict.HasKey("__namespace__")) - { - return TypeManager.CreateSubType(name, base_type, clsDict); - } + return TypeManager.CreateSubType(name, base_type, clsDict); } } @@ -259,8 +256,7 @@ public static int tp_setattro(BorrowedReference tp, BorrowedReference name, Borr /// public static NewReference mp_subscript(BorrowedReference tp, BorrowedReference idx) { - var cb = GetManagedObject(tp) as ClassBase; - if (cb != null) + if (GetManagedObject(tp) is ClassBase cb) { return cb.type_subscript(idx); } @@ -310,44 +306,30 @@ public static void tp_dealloc(NewReference lastRef) private static NewReference DoInstanceCheck(BorrowedReference tp, BorrowedReference args, bool checkType) { - var cb = GetManagedObject(tp) as ClassBase; - - if (cb == null || !cb.type.Valid) + if (GetManagedObject(tp) is not ClassBase cb || !cb.type.Valid) { return new NewReference(Runtime.PyFalse); } - using (var argsObj = new PyList(args)) + using var argsObj = new PyList(args); + if (argsObj.Length() != 1) { - if (argsObj.Length() != 1) - { - return Exceptions.RaiseTypeError("Invalid parameter count"); - } - - PyObject arg = argsObj[0]; - PyObject otherType; - if (checkType) - { - otherType = arg; - } - else - { - otherType = arg.GetPythonType(); - } + return Exceptions.RaiseTypeError("Invalid parameter count"); + } - if (Runtime.PyObject_TYPE(otherType) != PyCLRMetaType) - { - return new NewReference(Runtime.PyFalse); - } + PyObject arg = argsObj[0]; + var otherType = checkType ? arg : arg.GetPythonType(); - var otherCb = GetManagedObject(otherType) as ClassBase; - if (otherCb == null || !otherCb.type.Valid) - { - return new NewReference(Runtime.PyFalse); - } + if (Runtime.PyObject_TYPE(otherType) != PyCLRMetaType) + { + return new NewReference(Runtime.PyFalse); + } + if (GetManagedObject(otherType) is ClassBase otherCb && otherCb.type.Valid) + { return Converter.ToPython(cb.type.Value.IsAssignableFrom(otherCb.type.Value)); } + return new NewReference(Runtime.PyFalse); } public static NewReference __instancecheck__(BorrowedReference tp, BorrowedReference args) diff --git a/src/runtime/Types/MethodBinding.cs b/src/runtime/Types/MethodBinding.cs index 6d21af01e..334d705a6 100644 --- a/src/runtime/Types/MethodBinding.cs +++ b/src/runtime/Types/MethodBinding.cs @@ -172,7 +172,6 @@ public static NewReference tp_call(BorrowedReference ob, BorrowedReference args, var info = self.info.Value; if (info.IsGenericMethod) { - var len = Runtime.PyTuple_Size(args); //FIXME: Never used Type[]? sigTp = Runtime.PythonArgsToTypeArray(args, true); if (sigTp != null) { @@ -220,15 +219,13 @@ public static NewReference tp_call(BorrowedReference ob, BorrowedReference args, var inst = GetManagedObject(target) as CLRObject; if (inst?.inst is IPythonDerivedType) { - var baseType = GetManagedObject(self.targetType!) as ClassBase; - if (baseType != null && baseType.type.Valid) + if (GetManagedObject(self.targetType!) is ClassBase baseType && baseType.type.Valid) { - string baseMethodName = "_" + baseType.type.Value.Name + "__" + self.m.name; + var baseMethodName = $"_{baseType.type.Value.Name}__{self.m.name}"; using var baseMethod = Runtime.PyObject_GetAttrString(target, baseMethodName); if (!baseMethod.IsNull()) { - var baseSelf = GetManagedObject(baseMethod.Borrow()) as MethodBinding; - if (baseSelf != null) + if (GetManagedObject(baseMethod.Borrow()) is MethodBinding baseSelf) { self = baseSelf; } diff --git a/src/runtime/Types/MethodObject.cs b/src/runtime/Types/MethodObject.cs index b0fda49d3..0bdd11ac2 100644 --- a/src/runtime/Types/MethodObject.cs +++ b/src/runtime/Types/MethodObject.cs @@ -186,8 +186,7 @@ public static NewReference tp_descr_get(BorrowedReference ds, BorrowedReference // this descriptor was defined on then it will be because the base class method // is being called via super(Derived, self).method(...). // In which case create a MethodBinding bound to the base class. - var obj = GetManagedObject(ob) as CLRObject; - if (obj != null + if (GetManagedObject(ob) is CLRObject obj && obj.inst.GetType() != self.type.Value && obj.inst is IPythonDerivedType && self.type.Value.IsInstanceOfType(obj.inst)) diff --git a/src/runtime/Types/ModuleObject.cs b/src/runtime/Types/ModuleObject.cs index 1cc9f04b2..a9e8c9937 100644 --- a/src/runtime/Types/ModuleObject.cs +++ b/src/runtime/Types/ModuleObject.cs @@ -499,8 +499,7 @@ public static Assembly AddReference(string name) { AssemblyManager.UpdatePath(); var origNs = AssemblyManager.GetNamespaces(); - Assembly? assembly = null; - assembly = AssemblyManager.FindLoadedAssembly(name); + Assembly? assembly = AssemblyManager.FindLoadedAssembly(name); if (assembly == null) { assembly = AssemblyManager.LoadAssemblyPath(name); diff --git a/src/runtime/Types/MpLengthSlot.cs b/src/runtime/Types/MpLengthSlot.cs index 9e4865fe0..4cf15f221 100644 --- a/src/runtime/Types/MpLengthSlot.cs +++ b/src/runtime/Types/MpLengthSlot.cs @@ -32,8 +32,7 @@ public static bool CanAssign(Type clrType) /// internal static nint impl(BorrowedReference ob) { - var co = ManagedType.GetManagedObject(ob) as CLRObject; - if (co == null) + if (ManagedType.GetManagedObject(ob) is not CLRObject co) { Exceptions.RaiseTypeError("invalid object"); return -1; diff --git a/src/runtime/Types/OperatorMethod.cs b/src/runtime/Types/OperatorMethod.cs index abe6ded1a..0c6127f65 100644 --- a/src/runtime/Types/OperatorMethod.cs +++ b/src/runtime/Types/OperatorMethod.cs @@ -148,7 +148,7 @@ public static string GetPyMethodName(string clrName) private static string GenerateDummyCode() { - StringBuilder sb = new StringBuilder(); + var sb = new StringBuilder(); sb.AppendLine("class OperatorMethod(object):"); foreach (var item in OpMethodMap.Values) { @@ -160,15 +160,13 @@ private static string GenerateDummyCode() private static PyObject GetOperatorType() { - using (PyDict locals = new PyDict()) - { - // A hack way for getting typeobject.c::slotdefs - string code = GenerateDummyCode(); - // The resulting OperatorMethod class is stored in a PyDict. - PythonEngine.Exec(code, null, locals); - // Return the class itself, which is a type. - return locals.GetItem("OperatorMethod"); - } + using var locals = new PyDict(); + // A hack way for getting typeobject.c::slotdefs + string code = GenerateDummyCode(); + // The resulting OperatorMethod class is stored in a PyDict. + PythonEngine.Exec(code, null, locals); + // Return the class itself, which is a type. + return locals.GetItem("OperatorMethod"); } public static string ReversePyMethodName(string pyName) diff --git a/src/runtime/Types/OverloadMapper.cs b/src/runtime/Types/OverloadMapper.cs index 20939f4c6..8f6e30478 100644 --- a/src/runtime/Types/OverloadMapper.cs +++ b/src/runtime/Types/OverloadMapper.cs @@ -9,8 +9,8 @@ namespace Python.Runtime /// internal class OverloadMapper : ExtensionType { - private MethodObject m; - private PyObject? target; + private readonly MethodObject m; + private readonly PyObject? target; public OverloadMapper(MethodObject m, PyObject? target) { diff --git a/src/runtime/Util/CodeGenerator.cs b/src/runtime/Util/CodeGenerator.cs index d0079fabb..35a637113 100644 --- a/src/runtime/Util/CodeGenerator.cs +++ b/src/runtime/Util/CodeGenerator.cs @@ -14,8 +14,8 @@ namespace Python.Runtime /// internal class CodeGenerator { - private AssemblyBuilder aBuilder; - private ModuleBuilder mBuilder; + private readonly AssemblyBuilder aBuilder; + private readonly ModuleBuilder mBuilder; internal CodeGenerator() { diff --git a/src/runtime/Util/DebugUtil.cs b/src/runtime/Util/DebugUtil.cs index eb9facb3c..0eecc87b0 100644 --- a/src/runtime/Util/DebugUtil.cs +++ b/src/runtime/Util/DebugUtil.cs @@ -57,7 +57,6 @@ internal static void DumpType(BorrowedReference type) var slots = TypeOffset.GetOffsets(); - int size = IntPtr.Size; foreach (var entry in slots) { @@ -99,7 +98,7 @@ internal static void DumpInst(BorrowedReference ob) } [Conditional("DEBUG")] - internal static void debug(string msg) + internal static void Debug(string msg) { var st = new StackTrace(1, true); StackFrame sf = st.GetFrame(0); @@ -138,13 +137,13 @@ public static void PrintHexBytes(byte[] bytes) public static void AssertHasReferences(BorrowedReference obj) { nint refcount = Runtime.Refcount(obj); - Debug.Assert(refcount > 0, "Object refcount is 0 or less"); + System.Diagnostics.Debug.Assert(refcount > 0, "Object refcount is 0 or less"); } [Conditional("DEBUG")] public static void EnsureGIL() { - Debug.Assert(HaveInterpreterLock(), "GIL must be acquired"); + System.Diagnostics.Debug.Assert(HaveInterpreterLock(), "GIL must be acquired"); } public static bool HaveInterpreterLock() => Runtime.PyGILState_Check() == 1; diff --git a/src/runtime/Util/GenericUtil.cs b/src/runtime/Util/GenericUtil.cs index 74db54af1..907a3a616 100644 --- a/src/runtime/Util/GenericUtil.cs +++ b/src/runtime/Util/GenericUtil.cs @@ -32,15 +32,13 @@ internal static void Register(Type t) return; } - Dictionary> nsmap; - if (!mapping.TryGetValue(t.Namespace, out nsmap)) + if (!mapping.TryGetValue(t.Namespace, out var nsmap)) { nsmap = new Dictionary>(); mapping[t.Namespace] = nsmap; } string basename = GetBasename(t.Name); - List gnames; - if (!nsmap.TryGetValue(basename, out gnames)) + if (!nsmap.TryGetValue(basename, out var gnames)) { gnames = new List(); nsmap[basename] = gnames; @@ -53,17 +51,11 @@ internal static void Register(Type t) /// public static List? GetGenericBaseNames(string ns) { - Dictionary> nsmap; - if (!mapping.TryGetValue(ns, out nsmap)) + if (mapping.TryGetValue(ns, out var nsmap)) { - return null; + return nsmap.Keys.ToList(); } - var names = new List(); - foreach (string key in nsmap.Keys) - { - names.Add(key); - } - return names; + return null; } /// @@ -79,28 +71,21 @@ internal static void Register(Type t) /// public static Type? GenericByName(string ns, string basename, int paramCount) { - Dictionary> nsmap; - if (!mapping.TryGetValue(ns, out nsmap)) - { - return null; - } - - List names; - if (!nsmap.TryGetValue(GetBasename(basename), out names)) - { - return null; - } - - foreach (string name in names) + if (mapping.TryGetValue(ns, out var nsmap)) { - string qname = ns + "." + name; - Type o = AssemblyManager.LookupTypes(qname).FirstOrDefault(); - if (o != null && o.GetGenericArguments().Length == paramCount) + if (nsmap.TryGetValue(GetBasename(basename), out var names)) { - return o; + foreach (string name in names) + { + string qname = $"{ns}.{name}"; + Type o = AssemblyManager.LookupTypes(qname).FirstOrDefault(); + if (o != null && o.GetGenericArguments().Length == paramCount) + { + return o; + } + } } } - return null; } @@ -109,16 +94,13 @@ internal static void Register(Type t) /// public static string? GenericNameForBaseName(string ns, string name) { - Dictionary> nsmap; - if (!mapping.TryGetValue(ns, out nsmap)) + if (mapping.TryGetValue(ns, out var nsmap)) { - return null; - } - List gnames; - nsmap.TryGetValue(name, out gnames); - if (gnames?.Count > 0) - { - return gnames[0]; + nsmap.TryGetValue(name, out var gnames); + if (gnames?.Count > 0) + { + return gnames[0]; + } } return null; } diff --git a/src/runtime/Util/OpsHelper.cs b/src/runtime/Util/OpsHelper.cs index ab623f3de..12d0c7aa6 100644 --- a/src/runtime/Util/OpsHelper.cs +++ b/src/runtime/Util/OpsHelper.cs @@ -46,10 +46,12 @@ internal static class FlagEnumOps where T : Enum static readonly Func invert = UnaryOp(Expression.OnesComplement); +#pragma warning disable IDE1006 public static T op_BitwiseAnd(T a, T b) => and(a, b); public static T op_BitwiseOr(T a, T b) => or(a, b); public static T op_ExclusiveOr(T a, T b) => xor(a, b); public static T op_OnesComplement(T value) => invert(value); +#pragma warning restore IDE1006 static Expression FromNumber(Expression number) => Expression.Convert(number, typeof(T)); diff --git a/src/runtime/Util/ReflectionPolyfills.cs b/src/runtime/Util/ReflectionPolyfills.cs index 36bd39cef..b33698509 100644 --- a/src/runtime/Util/ReflectionPolyfills.cs +++ b/src/runtime/Util/ReflectionPolyfills.cs @@ -7,7 +7,7 @@ namespace Python.Runtime { internal static class ReflectionPolyfills { - public static AssemblyBuilder DefineDynamicAssembly(this AppDomain appDomain, AssemblyName assemblyName, AssemblyBuilderAccess assemblyBuilderAccess) + public static AssemblyBuilder DefineDynamicAssembly(this AppDomain _, AssemblyName assemblyName, AssemblyBuilderAccess assemblyBuilderAccess) { return AssemblyBuilder.DefineDynamicAssembly(assemblyName, assemblyBuilderAccess); } From d8990839a8eaf87ed885cba75427736cffc38353 Mon Sep 17 00:00:00 2001 From: Benedikt Reinartz Date: Wed, 30 Mar 2022 15:38:30 +0200 Subject: [PATCH 50/75] Fix Py.SetArgv for systems that don't support GetCommandLineArgs --- src/runtime/Py.cs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/runtime/Py.cs b/src/runtime/Py.cs index ec991f971..1fcce5110 100644 --- a/src/runtime/Py.cs +++ b/src/runtime/Py.cs @@ -122,11 +122,7 @@ public static void SetArgv() args = Enumerable.Empty(); } - SetArgv( - new[] { "" }.Concat( - Environment.GetCommandLineArgs().Skip(1) - ) - ); + SetArgv(new[] { "" }.Concat(args.Skip(1))); } public static void SetArgv(params string[] argv) From aa5b54bf7f27970275b89764140dccf0b44d38e2 Mon Sep 17 00:00:00 2001 From: Benedikt Reinartz Date: Wed, 30 Mar 2022 15:39:15 +0200 Subject: [PATCH 51/75] Modernise syntax in ClassDerived --- src/runtime/Types/ClassDerived.cs | 236 +++++++++++++++--------------- 1 file changed, 115 insertions(+), 121 deletions(-) diff --git a/src/runtime/Types/ClassDerived.cs b/src/runtime/Types/ClassDerived.cs index dd1f4595f..02288faee 100644 --- a/src/runtime/Types/ClassDerived.cs +++ b/src/runtime/Types/ClassDerived.cs @@ -469,100 +469,100 @@ private static void AddPythonMethod(string methodName, PyObject func, TypeBuilde methodName = pyMethodName.As() ?? throw new ArgumentNullException(methodNameAttribute); } - using (PyObject pyReturnType = func.GetAttr("_clr_return_type_")) - using (var pyArgTypes = PyIter.GetIter(func.GetAttr("_clr_arg_types_"))) + using var pyReturnType = func.GetAttr("_clr_return_type_"); + using var pyArgTypes = func.GetAttr("_clr_arg_types_"); + using var pyArgTypesIter = PyIter.GetIter(pyArgTypes); + var returnType = pyReturnType.AsManagedObject(typeof(Type)) as Type; + if (returnType == null) { - var returnType = pyReturnType.AsManagedObject(typeof(Type)) as Type; - if (returnType == null) - { - returnType = typeof(void); - } + returnType = typeof(void); + } - var argTypes = new List(); - foreach (PyObject pyArgType in pyArgTypes) + var argTypes = new List(); + foreach (PyObject pyArgType in pyArgTypesIter) + { + var argType = pyArgType.AsManagedObject(typeof(Type)) as Type; + if (argType == null) { - var argType = pyArgType.AsManagedObject(typeof(Type)) as Type; - if (argType == null) - { - throw new ArgumentException("_clr_arg_types_ must be a list or tuple of CLR types"); - } - argTypes.Add(argType); + throw new ArgumentException("_clr_arg_types_ must be a list or tuple of CLR types"); } + argTypes.Add(argType); + pyArgType.Dispose(); + } - // add the method to call back into python - MethodAttributes methodAttribs = MethodAttributes.Public | - MethodAttributes.Virtual | - MethodAttributes.ReuseSlot | - MethodAttributes.HideBySig; + // add the method to call back into python + MethodAttributes methodAttribs = MethodAttributes.Public | + MethodAttributes.Virtual | + MethodAttributes.ReuseSlot | + MethodAttributes.HideBySig; - MethodBuilder methodBuilder = typeBuilder.DefineMethod(methodName, - methodAttribs, - returnType, - argTypes.ToArray()); + MethodBuilder methodBuilder = typeBuilder.DefineMethod(methodName, + methodAttribs, + returnType, + argTypes.ToArray()); - ILGenerator il = methodBuilder.GetILGenerator(); + ILGenerator il = methodBuilder.GetILGenerator(); - il.DeclareLocal(typeof(object[])); - il.DeclareLocal(typeof(RuntimeMethodHandle)); + il.DeclareLocal(typeof(object[])); + il.DeclareLocal(typeof(RuntimeMethodHandle)); - // this - il.Emit(OpCodes.Ldarg_0); + // this + il.Emit(OpCodes.Ldarg_0); - // Python method to call - il.Emit(OpCodes.Ldstr, methodName); + // Python method to call + il.Emit(OpCodes.Ldstr, methodName); - // original method name - il.Emit(OpCodes.Ldnull); // don't fall back to the base type's method + // original method name + il.Emit(OpCodes.Ldnull); // don't fall back to the base type's method - // create args array - il.Emit(OpCodes.Ldc_I4, argTypes.Count); - il.Emit(OpCodes.Newarr, typeof(object)); - il.Emit(OpCodes.Stloc_0); + // create args array + il.Emit(OpCodes.Ldc_I4, argTypes.Count); + il.Emit(OpCodes.Newarr, typeof(object)); + il.Emit(OpCodes.Stloc_0); - // fill args array - for (var i = 0; i < argTypes.Count; ++i) + // fill args array + for (var i = 0; i < argTypes.Count; ++i) + { + il.Emit(OpCodes.Ldloc_0); + il.Emit(OpCodes.Ldc_I4, i); + il.Emit(OpCodes.Ldarg, i + 1); + var type = argTypes[i]; + if (type.IsByRef) { - il.Emit(OpCodes.Ldloc_0); - il.Emit(OpCodes.Ldc_I4, i); - il.Emit(OpCodes.Ldarg, i + 1); - var type = argTypes[i]; - if (type.IsByRef) - { - type = type.GetElementType(); - il.Emit(OpCodes.Ldobj, type); - } - if (type.IsValueType) - { - il.Emit(OpCodes.Box, type); - } - il.Emit(OpCodes.Stelem, typeof(object)); + type = type.GetElementType(); + il.Emit(OpCodes.Ldobj, type); + } + if (type.IsValueType) + { + il.Emit(OpCodes.Box, type); } + il.Emit(OpCodes.Stelem, typeof(object)); + } - // args array - il.Emit(OpCodes.Ldloc_0); + // args array + il.Emit(OpCodes.Ldloc_0); - // method handle for the base method is null - il.Emit(OpCodes.Ldloca_S, 1); - il.Emit(OpCodes.Initobj, typeof(RuntimeMethodHandle)); - il.Emit(OpCodes.Ldloc_1); + // method handle for the base method is null + il.Emit(OpCodes.Ldloca_S, 1); + il.Emit(OpCodes.Initobj, typeof(RuntimeMethodHandle)); + il.Emit(OpCodes.Ldloc_1); #pragma warning disable CS0618 // PythonDerivedType is for internal use only - // invoke the method - if (returnType == typeof(void)) - { - il.Emit(OpCodes.Call, typeof(PythonDerivedType).GetMethod(nameof(InvokeMethodVoid))); - } - else - { - il.Emit(OpCodes.Call, - typeof(PythonDerivedType).GetMethod(nameof(InvokeMethod)).MakeGenericMethod(returnType)); - } + // invoke the method + if (returnType == typeof(void)) + { + il.Emit(OpCodes.Call, typeof(PythonDerivedType).GetMethod(nameof(InvokeMethodVoid))); + } + else + { + il.Emit(OpCodes.Call, + typeof(PythonDerivedType).GetMethod(nameof(InvokeMethod)).MakeGenericMethod(returnType)); + } - CodeGenerator.GenerateMarshalByRefsBack(il, argTypes); + CodeGenerator.GenerateMarshalByRefsBack(il, argTypes); #pragma warning restore CS0618 // PythonDerivedType is for internal use only - il.Emit(OpCodes.Ret); - } + il.Emit(OpCodes.Ret); } /// @@ -581,68 +581,62 @@ private static void AddPythonProperty(string propertyName, PyObject func, TypeBu MethodAttributes.HideBySig | MethodAttributes.SpecialName; - using (PyObject pyPropertyType = func.GetAttr("_clr_property_type_")) + using var pyPropertyType = func.GetAttr("_clr_property_type_"); + var propertyType = pyPropertyType.AsManagedObject(typeof(Type)) as Type; + if (propertyType == null) { - var propertyType = pyPropertyType.AsManagedObject(typeof(Type)) as Type; - if (propertyType == null) - { - throw new ArgumentException("_clr_property_type must be a CLR type"); - } + throw new ArgumentException("_clr_property_type must be a CLR type"); + } - PropertyBuilder propertyBuilder = typeBuilder.DefineProperty(propertyName, - PropertyAttributes.None, - propertyType, - null); + PropertyBuilder propertyBuilder = typeBuilder.DefineProperty(propertyName, + PropertyAttributes.None, + propertyType, + null); - if (func.HasAttr("fget")) + if (func.HasAttr("fget")) + { + using var pyfget = func.GetAttr("fget"); + if (pyfget.IsTrue()) { - using (PyObject pyfget = func.GetAttr("fget")) - { - if (pyfget.IsTrue()) - { - MethodBuilder methodBuilder = typeBuilder.DefineMethod("get_" + propertyName, - methodAttribs, - propertyType, - null); - - ILGenerator il = methodBuilder.GetILGenerator(); - il.Emit(OpCodes.Ldarg_0); - il.Emit(OpCodes.Ldstr, propertyName); + MethodBuilder methodBuilder = typeBuilder.DefineMethod("get_" + propertyName, + methodAttribs, + propertyType, + null); + + ILGenerator il = methodBuilder.GetILGenerator(); + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Ldstr, propertyName); #pragma warning disable CS0618 // PythonDerivedType is for internal use only - il.Emit(OpCodes.Call, - typeof(PythonDerivedType).GetMethod("InvokeGetProperty").MakeGenericMethod(propertyType)); + il.Emit(OpCodes.Call, + typeof(PythonDerivedType).GetMethod("InvokeGetProperty").MakeGenericMethod(propertyType)); #pragma warning restore CS0618 // PythonDerivedType is for internal use only - il.Emit(OpCodes.Ret); + il.Emit(OpCodes.Ret); - propertyBuilder.SetGetMethod(methodBuilder); - } - } + propertyBuilder.SetGetMethod(methodBuilder); } + } - if (func.HasAttr("fset")) + if (func.HasAttr("fset")) + { + using var pyset = func.GetAttr("fset"); + if (pyset.IsTrue()) { - using (PyObject pyset = func.GetAttr("fset")) - { - if (pyset.IsTrue()) - { - MethodBuilder methodBuilder = typeBuilder.DefineMethod("set_" + propertyName, - methodAttribs, - null, - new[] { propertyType }); - - ILGenerator il = methodBuilder.GetILGenerator(); - il.Emit(OpCodes.Ldarg_0); - il.Emit(OpCodes.Ldstr, propertyName); - il.Emit(OpCodes.Ldarg_1); + MethodBuilder methodBuilder = typeBuilder.DefineMethod("set_" + propertyName, + methodAttribs, + null, + new[] { propertyType }); + + ILGenerator il = methodBuilder.GetILGenerator(); + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Ldstr, propertyName); + il.Emit(OpCodes.Ldarg_1); #pragma warning disable CS0618 // PythonDerivedType is for internal use only - il.Emit(OpCodes.Call, - typeof(PythonDerivedType).GetMethod("InvokeSetProperty").MakeGenericMethod(propertyType)); + il.Emit(OpCodes.Call, + typeof(PythonDerivedType).GetMethod("InvokeSetProperty").MakeGenericMethod(propertyType)); #pragma warning restore CS0618 // PythonDerivedType is for internal use only - il.Emit(OpCodes.Ret); + il.Emit(OpCodes.Ret); - propertyBuilder.SetSetMethod(methodBuilder); - } - } + propertyBuilder.SetSetMethod(methodBuilder); } } } From df3b569eb1f8da0524d1322139aab520918f2a1b Mon Sep 17 00:00:00 2001 From: Benedikt Reinartz Date: Tue, 23 Nov 2021 19:47:01 +0100 Subject: [PATCH 52/75] Fix enum codec A boxed enum value can't be casted directly to an integer, but using `System.Convert` functions instead works fine. --- src/runtime/Codecs/EnumPyIntCodec.cs | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/runtime/Codecs/EnumPyIntCodec.cs b/src/runtime/Codecs/EnumPyIntCodec.cs index 7d33b34ce..34030ffb0 100644 --- a/src/runtime/Codecs/EnumPyIntCodec.cs +++ b/src/runtime/Codecs/EnumPyIntCodec.cs @@ -53,14 +53,7 @@ public bool TryDecode(PyObject pyObj, out T? value) var enumType = value.GetType(); if (!enumType.IsEnum) return null; - try - { - return new PyInt((long)value); - } - catch (InvalidCastException) - { - return new PyInt((ulong)value); - } + return new PyInt(Convert.ToInt64(value)); } private EnumPyIntCodec() { } From 4f070f24c3e7500b73d88d62a827f75be8f676ac Mon Sep 17 00:00:00 2001 From: Benedikt Reinartz Date: Wed, 4 May 2022 08:33:32 +0200 Subject: [PATCH 53/75] Add unit test for enum encoder --- tests/test_codec.py | 68 +++++++++++++++++++++++++++++++++++++-------- 1 file changed, 56 insertions(+), 12 deletions(-) diff --git a/tests/test_codec.py b/tests/test_codec.py index 9744d3856..6c74bd8eb 100644 --- a/tests/test_codec.py +++ b/tests/test_codec.py @@ -1,39 +1,53 @@ # -*- coding: utf-8 -*- """Test conversions using codecs from client python code""" -import clr -import System + import pytest import Python.Runtime +import Python.Test as Test from Python.Test import ListConversionTester, ListMember, CodecResetter -class int_iterable(): + +@pytest.fixture(autouse=True) +def reset(): + yield + CodecResetter.Reset() + + +class int_iterable: def __init__(self): self.counter = 0 + def __iter__(self): return self + def __next__(self): if self.counter == 3: raise StopIteration self.counter = self.counter + 1 return self.counter -class obj_iterable(): + +class obj_iterable: def __init__(self): self.counter = 0 + def __iter__(self): return self + def __next__(self): if self.counter == 3: raise StopIteration self.counter = self.counter + 1 return ListMember(self.counter, "Number " + str(self.counter)) + def test_iterable(): - """Test that a python iterable can be passed into a function that takes an IEnumerable""" + """Test that a python iterable can be passed into a function that takes an + IEnumerable""" - #Python.Runtime.Codecs.ListDecoder.Register() - #Python.Runtime.Codecs.SequenceDecoder.Register() + # Python.Runtime.Codecs.ListDecoder.Register() + # Python.Runtime.Codecs.SequenceDecoder.Register() Python.Runtime.Codecs.IterableDecoder.Register() ob = ListConversionTester() @@ -43,28 +57,58 @@ def test_iterable(): iterable2 = obj_iterable() assert 3 == ob.GetLength2(iterable2) - CodecResetter.Reset() def test_sequence(): Python.Runtime.Codecs.SequenceDecoder.Register() ob = ListConversionTester() - tup = (1,2,3) + tup = (1, 2, 3) assert 3 == ob.GetLength(tup) tup2 = (ListMember(1, "one"), ListMember(2, "two"), ListMember(3, "three")) assert 3 == ob.GetLength(tup2) - CodecResetter.Reset() def test_list(): Python.Runtime.Codecs.SequenceDecoder.Register() ob = ListConversionTester() - l = [1,2,3] + l = [1, 2, 3] assert 3 == ob.GetLength(l) l2 = [ListMember(1, "one"), ListMember(2, "two"), ListMember(3, "three")] assert 3 == ob.GetLength(l2) - CodecResetter.Reset() + +def test_enum(): + Python.Runtime.PyObjectConversions.RegisterEncoder( + Python.Runtime.Codecs.EnumPyIntCodec.Instance + ) + + assert Test.ByteEnum.Zero == 0 + assert Test.ByteEnum.One == 1 + assert Test.ByteEnum.Two == 2 + assert Test.SByteEnum.Zero == 0 + assert Test.SByteEnum.One == 1 + assert Test.SByteEnum.Two == 2 + assert Test.ShortEnum.Zero == 0 + assert Test.ShortEnum.One == 1 + assert Test.ShortEnum.Two == 2 + assert Test.UShortEnum.Zero == 0 + assert Test.UShortEnum.One == 1 + assert Test.UShortEnum.Two == 2 + assert Test.IntEnum.Zero == 0 + assert Test.IntEnum.One == 1 + assert Test.IntEnum.Two == 2 + assert Test.UIntEnum.Zero == 0 + assert Test.UIntEnum.One == 1 + assert Test.UIntEnum.Two == 2 + assert Test.LongEnum.Zero == 0 + assert Test.LongEnum.One == 1 + assert Test.LongEnum.Two == 2 + assert Test.ULongEnum.Zero == 0 + assert Test.ULongEnum.One == 1 + assert Test.ULongEnum.Two == 2 + assert Test.LongEnum.Max == 9223372036854775807 + assert Test.LongEnum.Min == -9223372036854775808 + assert int(Test.ULongEnum.Max) == 18446744073709551615 From 7d6e27a23ec88138ada829f50e473cea394df189 Mon Sep 17 00:00:00 2001 From: Benedikt Reinartz Date: Wed, 4 May 2022 08:40:50 +0200 Subject: [PATCH 54/75] Allow conversion of UInt64 based enums --- src/runtime/Codecs/EnumPyIntCodec.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/runtime/Codecs/EnumPyIntCodec.cs b/src/runtime/Codecs/EnumPyIntCodec.cs index 34030ffb0..42f5eb1b2 100644 --- a/src/runtime/Codecs/EnumPyIntCodec.cs +++ b/src/runtime/Codecs/EnumPyIntCodec.cs @@ -53,7 +53,14 @@ public bool TryDecode(PyObject pyObj, out T? value) var enumType = value.GetType(); if (!enumType.IsEnum) return null; - return new PyInt(Convert.ToInt64(value)); + try + { + return new PyInt(Convert.ToInt64(value)); + } + catch (OverflowException) + { + return new PyInt(Convert.ToUInt64(value)); + } } private EnumPyIntCodec() { } From a80c685d197d47e959d65f82941068286c481276 Mon Sep 17 00:00:00 2001 From: Victor Date: Wed, 4 May 2022 11:11:13 -0700 Subject: [PATCH 55/75] disallow runtime shutdown when the Python error indicator is set, as it may lead to unpredictable behavior (#1780) --- CHANGELOG.md | 1 + src/runtime/PythonEngine.cs | 6 ++++++ src/runtime/PythonException.cs | 17 +++++++++++++++++ 3 files changed, 24 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 766258c5d..ea0f1f7bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -36,6 +36,7 @@ and other `PyObject` derived types when called from Python. details about the cause of the failure - `clr.AddReference` no longer adds ".dll" implicitly - `PyIter(PyObject)` constructor replaced with static `PyIter.GetIter(PyObject)` method +- Python runtime can no longer be shut down if the Python error indicator is set, as it would have unpredictable behavior - BREAKING: Return values from .NET methods that return an interface are now automatically wrapped in that interface. This is a breaking change for users that rely on being able to access members that are part of the implementation class, but not the diff --git a/src/runtime/PythonEngine.cs b/src/runtime/PythonEngine.cs index 24e1bedeb..c4c9fad61 100644 --- a/src/runtime/PythonEngine.cs +++ b/src/runtime/PythonEngine.cs @@ -351,6 +351,12 @@ public static void Shutdown() { return; } + if (Exceptions.ErrorOccurred()) + { + throw new InvalidOperationException( + "Python error indicator is set", + innerException: PythonException.PeekCurrentOrNull(out _)); + } // If the shutdown handlers trigger a domain unload, // don't call shutdown again. AppDomain.CurrentDomain.DomainUnload -= OnDomainUnload; diff --git a/src/runtime/PythonException.cs b/src/runtime/PythonException.cs index 8d3330c7b..e4d38c362 100644 --- a/src/runtime/PythonException.cs +++ b/src/runtime/PythonException.cs @@ -75,6 +75,23 @@ internal static PythonException FetchCurrentRaw() => FetchCurrentOrNullRaw() ?? throw new InvalidOperationException("No exception is set"); + internal static Exception? PeekCurrentOrNull(out ExceptionDispatchInfo? dispatchInfo) + { + using var _ = new Py.GILState(); + + Runtime.PyErr_Fetch(out var type, out var value, out var traceback); + Runtime.PyErr_Restore( + new NewReference(type, canBeNull: true).StealNullable(), + new NewReference(value, canBeNull: true).StealNullable(), + new NewReference(traceback, canBeNull: true).StealNullable()); + + var err = FetchCurrentOrNull(out dispatchInfo); + + Runtime.PyErr_Restore(type.StealNullable(), value.StealNullable(), traceback.StealNullable()); + + return err; + } + internal static Exception? FetchCurrentOrNull(out ExceptionDispatchInfo? dispatchInfo) { dispatchInfo = null; From 2910800eb6f04fbe1fc70645b8c1820e6e192369 Mon Sep 17 00:00:00 2001 From: Victor Nova Date: Fri, 6 May 2022 23:59:33 -0700 Subject: [PATCH 56/75] type name generator ignored names of nested classes --- src/runtime/TypeManager.cs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/runtime/TypeManager.cs b/src/runtime/TypeManager.cs index d9ca184f6..217b4820e 100644 --- a/src/runtime/TypeManager.cs +++ b/src/runtime/TypeManager.cs @@ -217,6 +217,23 @@ static void GetPythonTypeName(Type clrType, System.Text.StringBuilder target) } target.Append(']'); + + int nestedStart = fullName.IndexOf('+'); + while (nestedStart >= 0) + { + target.Append('.'); + int nextNested = fullName.IndexOf('+', nestedStart + 1); + if (nextNested < 0) + { + target.Append(fullName.Substring(nestedStart + 1)); + } + else + { + target.Append(fullName.Substring(nestedStart + 1, length: nextNested - nestedStart - 1)); + } + nestedStart = nextNested; + } + return; } } From cf8823fa941842cfd19ad808f04bc6aa6d0b7970 Mon Sep 17 00:00:00 2001 From: Victor Nova Date: Sat, 7 May 2022 10:03:50 -0700 Subject: [PATCH 57/75] added regression test for "in Dictionary.Keys" check --- tests/test_collection_mixins.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/test_collection_mixins.py b/tests/test_collection_mixins.py index 2f74e93ab..0ac040038 100644 --- a/tests/test_collection_mixins.py +++ b/tests/test_collection_mixins.py @@ -14,3 +14,10 @@ def test_dict_items(): k,v = items[0] assert k == 42 assert v == "a" + +# regression test for https://github.com/pythonnet/pythonnet/issues/1785 +def test_dict_in_keys(): + d = C.Dictionary[str, int]() + d["a"] = 42 + assert "a" in d.Keys + assert "b" not in d.Keys From f48d7a96f76cf1e44411c3d19ad036e96b9adaf0 Mon Sep 17 00:00:00 2001 From: Victor Nova Date: Sat, 7 May 2022 10:05:36 -0700 Subject: [PATCH 58/75] ClassGeneric.GetClass now returns BorrowedReference to indicate that the value should not be disposed --- src/runtime/ClassManager.cs | 2 +- src/runtime/Types/ClassObject.cs | 2 +- src/runtime/Types/ClrObject.cs | 4 ++-- src/runtime/Types/MethodObject.cs | 2 +- src/runtime/Types/ReflectedClrType.cs | 1 + 5 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/runtime/ClassManager.cs b/src/runtime/ClassManager.cs index 6379f51de..6c5558f3a 100644 --- a/src/runtime/ClassManager.cs +++ b/src/runtime/ClassManager.cs @@ -133,7 +133,7 @@ internal static void RestoreRuntimeData(ClassManagerState storage) /// Return the ClassBase-derived instance that implements a particular /// reflected managed type, creating it if it doesn't yet exist. /// - internal static ReflectedClrType GetClass(Type type) => ReflectedClrType.GetOrCreate(type); + internal static BorrowedReference GetClass(Type type) => ReflectedClrType.GetOrCreate(type); internal static ClassBase GetClassImpl(Type type) { diff --git a/src/runtime/Types/ClassObject.cs b/src/runtime/Types/ClassObject.cs index 04613afa5..474e9dd7b 100644 --- a/src/runtime/Types/ClassObject.cs +++ b/src/runtime/Types/ClassObject.cs @@ -236,7 +236,7 @@ public override NewReference type_subscript(BorrowedReference idx) return Exceptions.RaiseTypeError("type expected"); } Type a = t.MakeArrayType(); - PyType o = ClassManager.GetClass(a); + BorrowedReference o = ClassManager.GetClass(a); return new NewReference(o); } diff --git a/src/runtime/Types/ClrObject.cs b/src/runtime/Types/ClrObject.cs index db6e99121..4cf9062cb 100644 --- a/src/runtime/Types/ClrObject.cs +++ b/src/runtime/Types/ClrObject.cs @@ -43,13 +43,13 @@ internal static NewReference GetReference(object ob, BorrowedReference pyType) internal static NewReference GetReference(object ob, Type type) { - PyType cc = ClassManager.GetClass(type); + BorrowedReference cc = ClassManager.GetClass(type); return Create(ob, cc); } internal static NewReference GetReference(object ob) { - PyType cc = ClassManager.GetClass(ob.GetType()); + BorrowedReference cc = ClassManager.GetClass(ob.GetType()); return Create(ob, cc); } diff --git a/src/runtime/Types/MethodObject.cs b/src/runtime/Types/MethodObject.cs index 0bdd11ac2..55ad06e2d 100644 --- a/src/runtime/Types/MethodObject.cs +++ b/src/runtime/Types/MethodObject.cs @@ -191,7 +191,7 @@ public static NewReference tp_descr_get(BorrowedReference ds, BorrowedReference && obj.inst is IPythonDerivedType && self.type.Value.IsInstanceOfType(obj.inst)) { - var basecls = ClassManager.GetClass(self.type.Value); + var basecls = ReflectedClrType.GetOrCreate(self.type.Value); return new MethodBinding(self, new PyObject(ob), basecls).Alloc(); } diff --git a/src/runtime/Types/ReflectedClrType.cs b/src/runtime/Types/ReflectedClrType.cs index 15ea5c2b2..2e8f95924 100644 --- a/src/runtime/Types/ReflectedClrType.cs +++ b/src/runtime/Types/ReflectedClrType.cs @@ -12,6 +12,7 @@ internal sealed class ReflectedClrType : PyType { private ReflectedClrType(StolenReference reference) : base(reference, prevalidated: true) { } internal ReflectedClrType(ReflectedClrType original) : base(original, prevalidated: true) { } + internal ReflectedClrType(BorrowedReference original) : base(original) { } ReflectedClrType(SerializationInfo info, StreamingContext context) : base(info, context) { } internal ClassBase Impl => (ClassBase)ManagedType.GetManagedObject(this)!; From 537ddf4b7fc938bca461b1ffb532a136af7b7d67 Mon Sep 17 00:00:00 2001 From: Victor Nova Date: Sat, 7 May 2022 10:15:16 -0700 Subject: [PATCH 59/75] allow casting objects to generic .NET interfaces without specifying generic arguments as long as there is no ambiguity --- CHANGELOG.md | 1 + src/runtime/Types/GenericType.cs | 49 ++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ea0f1f7bb..afd2f58c4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ This document follows the conventions laid out in [Keep a CHANGELOG][]. - `__name__` and `__signature__` to reflected .NET methods - .NET collection types now implement standard Python collection interfaces from `collections.abc`. See [Mixins/collections.py](src/runtime/Mixins/collections.py). +- you can cast objects to generic .NET interfaces without specifying generic arguments as long as there is no ambiguity. - .NET arrays implement Python buffer protocol - Python integer interoperability with `System.Numerics.BigInteger` - Python.NET will correctly resolve .NET methods, that accept `PyList`, `PyInt`, diff --git a/src/runtime/Types/GenericType.cs b/src/runtime/Types/GenericType.cs index 6b537931e..380ca8875 100644 --- a/src/runtime/Types/GenericType.cs +++ b/src/runtime/Types/GenericType.cs @@ -1,4 +1,5 @@ using System; +using System.Linq; namespace Python.Runtime { @@ -20,10 +21,58 @@ internal GenericType(Type tp) : base(tp) /// public static NewReference tp_new(BorrowedReference tp, BorrowedReference args, BorrowedReference kw) { + var self = (GenericType)GetManagedObject(tp)!; + if (!self.type.Valid) + { + return Exceptions.RaiseTypeError(self.type.DeletedMessage); + } + var type = self.type.Value; + + if (type.IsInterface && !type.IsConstructedGenericType) + { + var nargs = Runtime.PyTuple_Size(args); + if (nargs == 1) + { + var instance = Runtime.PyTuple_GetItem(args, 0); + return AsGenericInterface(instance, type); + } + } + Exceptions.SetError(Exceptions.TypeError, "cannot instantiate an open generic type"); + return default; } + static NewReference AsGenericInterface(BorrowedReference instance, Type targetType) + { + if (GetManagedObject(instance) is not CLRObject obj) + { + return Exceptions.RaiseTypeError("only .NET objects can be cast to .NET interfaces"); + } + + Type[] supportedInterfaces = obj.inst.GetType().GetInterfaces(); + Type[] constructedInterfaces = supportedInterfaces + .Where(i => i.IsConstructedGenericType && i.GetGenericTypeDefinition() == targetType) + .ToArray(); + + if (constructedInterfaces.Length == 1) + { + BorrowedReference pythonic = ClassManager.GetClass(constructedInterfaces[0]); + using var args = Runtime.PyTuple_New(1); + Runtime.PyTuple_SetItem(args.Borrow(), 0, instance); + return Runtime.PyObject_CallObject(pythonic, args.Borrow()); + } + + if (constructedInterfaces.Length > 1) + { + string interfaces = string.Join(", ", constructedInterfaces.Select(TypeManager.GetPythonTypeName)); + return Exceptions.RaiseTypeError("Ambiguous cast to .NET interface. " + + $"Object implements: {interfaces}"); + } + + return Exceptions.RaiseTypeError("object does not implement " + + TypeManager.GetPythonTypeName(targetType)); + } /// /// Implements __call__ for reflected generic types. From d8543321a32535b90789770219f0d7c1ff0265de Mon Sep 17 00:00:00 2001 From: Victor Nova Date: Sat, 7 May 2022 10:16:43 -0700 Subject: [PATCH 60/75] a few collection mixins now properly handle private interface implementations fixes https://github.com/pythonnet/pythonnet/issues/1785 --- src/runtime/Mixins/collections.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/runtime/Mixins/collections.py b/src/runtime/Mixins/collections.py index a82032472..3203ad96f 100644 --- a/src/runtime/Mixins/collections.py +++ b/src/runtime/Mixins/collections.py @@ -7,7 +7,11 @@ class IteratorMixin(col.Iterator): def close(self): - self.Dispose() + if hasattr(self, 'Dispose'): + self.Dispose() + else: + from System import IDisposable + IDisposable(self).Dispose() class IterableMixin(col.Iterable): pass @@ -16,7 +20,12 @@ class SizedMixin(col.Sized): def __len__(self): return self.Count class ContainerMixin(col.Container): - def __contains__(self, item): return self.Contains(item) + def __contains__(self, item): + if hasattr('self', 'Contains'): + return self.Contains(item) + else: + from System.Collections.Generic import ICollection + return ICollection(self).Contains(item) try: abc_Collection = col.Collection From e258cee22bbe7e97bb0a333af5c45541fe2dea1b Mon Sep 17 00:00:00 2001 From: Benedikt Reinartz Date: Mon, 23 May 2022 08:29:24 +0200 Subject: [PATCH 61/75] Drop Python 3.6 support (#1795) Fixes https://github.com/pythonnet/pythonnet/issues/1640 Co-authored-by: Victor Nova --- .github/workflows/main.yml | 2 +- CHANGELOG.md | 2 +- setup.py | 1 - src/runtime/Mixins/collections.py | 2 +- src/runtime/Native/TypeOffset36.cs | 136 ----------------------------- src/runtime/Util/Util.cs | 2 +- 6 files changed, 4 insertions(+), 141 deletions(-) delete mode 100644 src/runtime/Native/TypeOffset36.cs diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 284658620..2a77682f7 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -16,7 +16,7 @@ jobs: fail-fast: false matrix: os: [windows, ubuntu, macos] - python: ["3.6", "3.7", "3.8", "3.9", "3.10"] + python: ["3.7", "3.8", "3.9", "3.10"] platform: [x64, x86] exclude: - os: ubuntu diff --git a/CHANGELOG.md b/CHANGELOG.md index afd2f58c4..31deb70ac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,7 +31,7 @@ and other `PyObject` derived types when called from Python. ### Changed -- Drop support for Python 2, 3.4, and 3.5 +- Drop support for Python 2, 3.4, 3.5, and 3.6 - `wchar_t` size aka `Runtime.UCS` is now determined at runtime - `clr.AddReference` may now throw errors besides `FileNotFoundException`, that provide more details about the cause of the failure diff --git a/setup.py b/setup.py index 527bcc893..84f6f3ad2 100644 --- a/setup.py +++ b/setup.py @@ -165,7 +165,6 @@ def finalize_options(self): "License :: OSI Approved :: MIT License", "Programming Language :: C#", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", diff --git a/src/runtime/Mixins/collections.py b/src/runtime/Mixins/collections.py index 3203ad96f..95a6d8162 100644 --- a/src/runtime/Mixins/collections.py +++ b/src/runtime/Mixins/collections.py @@ -1,6 +1,6 @@ """ Implements collections.abc for common .NET types -https://docs.python.org/3.6/library/collections.abc.html +https://docs.python.org/3/library/collections.abc.html """ import collections.abc as col diff --git a/src/runtime/Native/TypeOffset36.cs b/src/runtime/Native/TypeOffset36.cs deleted file mode 100644 index 4b3b8bfb9..000000000 --- a/src/runtime/Native/TypeOffset36.cs +++ /dev/null @@ -1,136 +0,0 @@ - -// Auto-generated by geninterop.py. -// DO NOT MODIFY BY HAND. - -// Python 3.6: ABI flags: '' - -// ReSharper disable InconsistentNaming -// ReSharper disable IdentifierTypo - -using System; -using System.Diagnostics.CodeAnalysis; -using System.Runtime.InteropServices; - -using Python.Runtime.Native; - -namespace Python.Runtime -{ - [SuppressMessage("Style", "IDE1006:Naming Styles", - Justification = "Following CPython", - Scope = "type")] - - [StructLayout(LayoutKind.Sequential)] - internal class TypeOffset36 : GeneratedTypeOffsets, ITypeOffsets - { - public TypeOffset36() { } - // Auto-generated from PyHeapTypeObject in Python.h - public int ob_refcnt { get; private set; } - public int ob_type { get; private set; } - public int ob_size { get; private set; } - public int tp_name { get; private set; } - public int tp_basicsize { get; private set; } - public int tp_itemsize { get; private set; } - public int tp_dealloc { get; private set; } - public int tp_print { get; private set; } - public int tp_getattr { get; private set; } - public int tp_setattr { get; private set; } - public int tp_as_async { get; private set; } - public int tp_repr { get; private set; } - public int tp_as_number { get; private set; } - public int tp_as_sequence { get; private set; } - public int tp_as_mapping { get; private set; } - public int tp_hash { get; private set; } - public int tp_call { get; private set; } - public int tp_str { get; private set; } - public int tp_getattro { get; private set; } - public int tp_setattro { get; private set; } - public int tp_as_buffer { get; private set; } - public int tp_flags { get; private set; } - public int tp_doc { get; private set; } - public int tp_traverse { get; private set; } - public int tp_clear { get; private set; } - public int tp_richcompare { get; private set; } - public int tp_weaklistoffset { get; private set; } - public int tp_iter { get; private set; } - public int tp_iternext { get; private set; } - public int tp_methods { get; private set; } - public int tp_members { get; private set; } - public int tp_getset { get; private set; } - public int tp_base { get; private set; } - public int tp_dict { get; private set; } - public int tp_descr_get { get; private set; } - public int tp_descr_set { get; private set; } - public int tp_dictoffset { get; private set; } - public int tp_init { get; private set; } - public int tp_alloc { get; private set; } - public int tp_new { get; private set; } - public int tp_free { get; private set; } - public int tp_is_gc { get; private set; } - public int tp_bases { get; private set; } - public int tp_mro { get; private set; } - public int tp_cache { get; private set; } - public int tp_subclasses { get; private set; } - public int tp_weaklist { get; private set; } - public int tp_del { get; private set; } - public int tp_version_tag { get; private set; } - public int tp_finalize { get; private set; } - public int am_await { get; private set; } - public int am_aiter { get; private set; } - public int am_anext { get; private set; } - public int nb_add { get; private set; } - public int nb_subtract { get; private set; } - public int nb_multiply { get; private set; } - public int nb_remainder { get; private set; } - public int nb_divmod { get; private set; } - public int nb_power { get; private set; } - public int nb_negative { get; private set; } - public int nb_positive { get; private set; } - public int nb_absolute { get; private set; } - public int nb_bool { get; private set; } - public int nb_invert { get; private set; } - public int nb_lshift { get; private set; } - public int nb_rshift { get; private set; } - public int nb_and { get; private set; } - public int nb_xor { get; private set; } - public int nb_or { get; private set; } - public int nb_int { get; private set; } - public int nb_reserved { get; private set; } - public int nb_float { get; private set; } - public int nb_inplace_add { get; private set; } - public int nb_inplace_subtract { get; private set; } - public int nb_inplace_multiply { get; private set; } - public int nb_inplace_remainder { get; private set; } - public int nb_inplace_power { get; private set; } - public int nb_inplace_lshift { get; private set; } - public int nb_inplace_rshift { get; private set; } - public int nb_inplace_and { get; private set; } - public int nb_inplace_xor { get; private set; } - public int nb_inplace_or { get; private set; } - public int nb_floor_divide { get; private set; } - public int nb_true_divide { get; private set; } - public int nb_inplace_floor_divide { get; private set; } - public int nb_inplace_true_divide { get; private set; } - public int nb_index { get; private set; } - public int nb_matrix_multiply { get; private set; } - public int nb_inplace_matrix_multiply { get; private set; } - public int mp_length { get; private set; } - public int mp_subscript { get; private set; } - public int mp_ass_subscript { get; private set; } - public int sq_length { get; private set; } - public int sq_concat { get; private set; } - public int sq_repeat { get; private set; } - public int sq_item { get; private set; } - public int was_sq_slice { get; private set; } - public int sq_ass_item { get; private set; } - public int was_sq_ass_slice { get; private set; } - public int sq_contains { get; private set; } - public int sq_inplace_concat { get; private set; } - public int sq_inplace_repeat { get; private set; } - public int bf_getbuffer { get; private set; } - public int bf_releasebuffer { get; private set; } - public int name { get; private set; } - public int ht_slots { get; private set; } - public int qualname { get; private set; } - public int ht_cached_keys { get; private set; } - } -} diff --git a/src/runtime/Util/Util.cs b/src/runtime/Util/Util.cs index 89f5bdf4c..2429c460b 100644 --- a/src/runtime/Util/Util.cs +++ b/src/runtime/Util/Util.cs @@ -13,7 +13,7 @@ internal static class Util internal const string UnstableApiMessage = "This API is unstable, and might be changed or removed in the next minor release"; internal const string MinimalPythonVersionRequired = - "Only Python 3.6 or newer is supported"; + "Only Python 3.7 or newer is supported"; internal const string InternalUseOnly = "This API is for internal use only"; From 987b2eec097b9c83920ea8285d5a97d6a19064b5 Mon Sep 17 00:00:00 2001 From: Benedikt Reinartz Date: Mon, 23 May 2022 21:54:35 +0200 Subject: [PATCH 62/75] Move to modern setuptools with pyproject.toml (#1793) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Move to modern setuptools with pyproject.toml Also moves the version to a common ̀`version.txt` that is read by both the .NET and Python builds. * Allow explicitly overriding the version suffix for .NET builds --- Directory.Build.props | 7 ++++-- MANIFEST.in | 1 + pyproject.toml | 48 ++++++++++++++++++++++++++++++++++++++- setup.py | 53 ++++++------------------------------------- version.txt | 1 + 5 files changed, 61 insertions(+), 49 deletions(-) create mode 100644 version.txt diff --git a/Directory.Build.props b/Directory.Build.props index 496060877..8c5b53685 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,11 +1,14 @@ + - 3.0.0 - Copyright (c) 2006-2021 The Contributors of the Python.NET Project + Copyright (c) 2006-2022 The Contributors of the Python.NET Project pythonnet Python.NET 10.0 false + $([System.IO.File]::ReadAllText("version.txt")) + $(FullVersion.Split('-', 2)[0]) + $(FullVersion.Split('-', 2)[1]) diff --git a/MANIFEST.in b/MANIFEST.in index 01f4156ca..4763ae70f 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,4 +1,5 @@ recursive-include src/ * include Directory.Build.* include pythonnet.sln +include version.txt global-exclude **/obj/* **/bin/* diff --git a/pyproject.toml b/pyproject.toml index b6df82f71..5ee89d3b7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,53 @@ [build-system] -requires = ["setuptools>=42", "wheel", "pycparser"] +requires = ["setuptools>=61", "wheel"] build-backend = "setuptools.build_meta" +[project] +name = "pythonnet" +description = ".NET and Mono integration for Python" +license = {text = "MIT"} + +readme = "README.rst" + +dependencies = [ + "clr_loader>=0.1.7" +] + +classifiers = [ + "Development Status :: 5 - Production/Stable", + "Intended Audience :: Developers", + "License :: OSI Approved :: MIT License", + "Programming Language :: C#", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Operating System :: Microsoft :: Windows", + "Operating System :: POSIX :: Linux", + "Operating System :: MacOS :: MacOS X", +] + +dynamic = ["version"] + +[[project.authors]] +name = "The Contributors of the Python.NET Project" +email = "pythonnet@python.org" + +[project.urls] +Homepage = "https://pythonnet.github.io/" +Sources = "https://github.com/pythonnet/pythonnet" + +[tool.setuptools] +zip-safe = false +py-modules = ["clr"] + +[tool.setuptools.dynamic.version] +file = "version.txt" + +[tool.setuptools.packages.find] +include = ["pythonnet*"] + [tool.pytest.ini_options] xfail_strict = true testpaths = [ diff --git a/setup.py b/setup.py index 84f6f3ad2..7c02b7710 100644 --- a/setup.py +++ b/setup.py @@ -1,16 +1,18 @@ #!/usr/bin/env python -from setuptools import setup, Command, Extension -from setuptools.command.build_ext import build_ext import distutils -from distutils.command import build -from subprocess import check_output, check_call +from distutils.command.build import build as _build +from setuptools.command.develop import develop as _develop +from wheel.bdist_wheel import bdist_wheel as _bdist_wheel +from setuptools import Distribution +from setuptools import setup, Command -import sys, os +import os # Disable SourceLink during the build until it can read repo-format v1, #1613 os.environ["EnableSourceControlManagerQueries"] = "false" + class DotnetLib: def __init__(self, name, path, **kwargs): self.name = name @@ -91,13 +93,6 @@ def run(self): # Add build_dotnet to the build tasks: -from distutils.command.build import build as _build -from setuptools.command.develop import develop as _develop -from wheel.bdist_wheel import bdist_wheel as _bdist_wheel -from setuptools import Distribution -import setuptools - - class build(_build): sub_commands = _build.sub_commands + [("build_dotnet", None)] @@ -129,10 +124,6 @@ def finalize_options(self): "bdist_wheel": bdist_wheel, } - -with open("README.rst", "r") as f: - long_description = f.read() - dotnet_libs = [ DotnetLib( "python-runtime", @@ -143,35 +134,5 @@ def finalize_options(self): setup( cmdclass=cmdclass, - name="pythonnet", - version="3.0.0.dev1", - description=".Net and Mono integration for Python", - url="https://pythonnet.github.io/", - project_urls={ - "Source": "https://github.com/pythonnet/pythonnet", - }, - license="MIT", - author="The Contributors of the Python.NET Project", - author_email="pythonnet@python.org", - packages=["pythonnet", "pythonnet.find_libpython"], - install_requires=["clr_loader >= 0.1.7"], - long_description=long_description, - long_description_content_type="text/x-rst", - py_modules=["clr"], dotnet_libs=dotnet_libs, - classifiers=[ - "Development Status :: 5 - Production/Stable", - "Intended Audience :: Developers", - "License :: OSI Approved :: MIT License", - "Programming Language :: C#", - "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.7", - "Programming Language :: Python :: 3.8", - "Programming Language :: Python :: 3.9", - "Programming Language :: Python :: 3.10", - "Operating System :: Microsoft :: Windows", - "Operating System :: POSIX :: Linux", - "Operating System :: MacOS :: MacOS X", - ], - zip_safe=False, ) diff --git a/version.txt b/version.txt new file mode 100644 index 000000000..e16bcc8be --- /dev/null +++ b/version.txt @@ -0,0 +1 @@ +3.0.0-dev1 From 1492d7d7c682257833b9f05c217186277a1567d1 Mon Sep 17 00:00:00 2001 From: Victor Nova Date: Wed, 25 May 2022 11:37:48 -0700 Subject: [PATCH 63/75] expose Min/MaxSupportedVersion and IsSupportedVersion on PythonEngine fixes https://github.com/pythonnet/pythonnet/discussions/1724 --- CHANGELOG.md | 1 + src/runtime/PythonEngine.cs | 4 ++++ tests/test_engine.py | 8 ++++++++ 3 files changed, 13 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 31deb70ac..069629021 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,7 @@ See [Mixins/collections.py](src/runtime/Mixins/collections.py). and other `PyObject` derived types when called from Python. - .NET classes, that have `__call__` method are callable from Python - `PyIterable` type, that wraps any iterable object in Python +- `PythonEngine` properties for supported Python versions: `MinSupportedVersion`, `MaxSupportedVersion`, and `IsSupportedVersion` ### Changed diff --git a/src/runtime/PythonEngine.cs b/src/runtime/PythonEngine.cs index c4c9fad61..f184ebe88 100644 --- a/src/runtime/PythonEngine.cs +++ b/src/runtime/PythonEngine.cs @@ -127,6 +127,10 @@ public static string PythonPath } } + public static Version MinSupportedVersion => new(3, 7); + public static Version MaxSupportedVersion => new(3, 10, int.MaxValue, int.MaxValue); + public static bool IsSupportedVersion(Version version) => version >= MinSupportedVersion && version <= MaxSupportedVersion; + public static string Version { get { return Marshal.PtrToStringAnsi(Runtime.Py_GetVersion()); } diff --git a/tests/test_engine.py b/tests/test_engine.py index 06a44d561..65aaa3ce8 100644 --- a/tests/test_engine.py +++ b/tests/test_engine.py @@ -19,6 +19,14 @@ def test_multiple_calls_to_initialize(): assert False # Initialize() raise an exception. +def test_supported_version(): + major, minor, build, *_ = sys.version_info + ver = System.Version(major, minor, build) + assert PythonEngine.IsSupportedVersion(ver) + assert ver >= PythonEngine.MinSupportedVersion + assert ver <= PythonEngine.MaxSupportedVersion + + @pytest.mark.skip(reason="FIXME: test crashes") def test_import_module(): """Test module import.""" From 26d1039c9702746c8deb50db1b2aee34a0c27dab Mon Sep 17 00:00:00 2001 From: Benedikt Reinartz Date: Thu, 26 May 2022 20:58:28 +0200 Subject: [PATCH 64/75] Ensure that codecs are definitely clean in tests --- tests/test_codec.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_codec.py b/tests/test_codec.py index 6c74bd8eb..0c1fb44f4 100644 --- a/tests/test_codec.py +++ b/tests/test_codec.py @@ -10,6 +10,7 @@ @pytest.fixture(autouse=True) def reset(): + CodecResetter.Reset() yield CodecResetter.Reset() From 2e1652b8f8878d9db3f1676e3f3a97674fcc3ecb Mon Sep 17 00:00:00 2001 From: Benedikt Reinartz Date: Thu, 26 May 2022 21:05:34 +0200 Subject: [PATCH 65/75] Ensure that the RawProxyEncoder is removed again --- tests/test_conversion.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/test_conversion.py b/tests/test_conversion.py index 4de286b14..6693d8000 100644 --- a/tests/test_conversion.py +++ b/tests/test_conversion.py @@ -4,7 +4,7 @@ import pytest import System -from Python.Test import ConversionTest, MethodResolutionInt, UnicodeString +from Python.Test import ConversionTest, MethodResolutionInt, UnicodeString, CodecResetter from Python.Runtime import PyObjectConversions from Python.Runtime.Codecs import RawProxyEncoder @@ -659,6 +659,8 @@ def CanEncode(self, clr_type): l.Add(42) assert ob.ListField.Count == 1 + CodecResetter.Reset() + def test_int_param_resolution_required(): """Test resolution of `int` parameters when resolution is needed""" From c1dab2712006badcbf69ac1d1390ef1ca75175d1 Mon Sep 17 00:00:00 2001 From: Benedikt Reinartz Date: Tue, 31 May 2022 06:36:09 +0200 Subject: [PATCH 66/75] Add explicit tests for vararg call (#1805) --- src/embed_tests/NumPyTests.cs | 65 ++++++++++++++++++----------------- 1 file changed, 34 insertions(+), 31 deletions(-) diff --git a/src/embed_tests/NumPyTests.cs b/src/embed_tests/NumPyTests.cs index 8b76f4ca1..e102ddb99 100644 --- a/src/embed_tests/NumPyTests.cs +++ b/src/embed_tests/NumPyTests.cs @@ -1,6 +1,8 @@ using System; using System.Collections.Generic; + using NUnit.Framework; + using Python.Runtime; using Python.Runtime.Codecs; @@ -24,17 +26,6 @@ public void Dispose() [Test] public void TestReadme() { - dynamic np; - try - { - np = Py.Import("numpy"); - } - catch (PythonException) - { - Assert.Inconclusive("Numpy or dependency not installed"); - return; - } - Assert.AreEqual("1.0", np.cos(np.pi * 2).ToString()); dynamic sin = np.sin; @@ -55,17 +46,9 @@ public void TestReadme() [Test] public void MultidimensionalNumPyArray() { - PyObject np; - try { - np = Py.Import("numpy"); - } catch (PythonException) { - Assert.Inconclusive("Numpy or dependency not installed"); - return; - } - var array = new[,] { { 1, 2 }, { 3, 4 } }; var ndarray = np.InvokeMethod("asarray", array.ToPython()); - Assert.AreEqual((2,2), ndarray.GetAttr("shape").As<(int,int)>()); + Assert.AreEqual((2, 2), ndarray.GetAttr("shape").As<(int, int)>()); Assert.AreEqual(1, ndarray[(0, 0).ToPython()].InvokeMethod("__int__").As()); Assert.AreEqual(array[1, 0], ndarray[(1, 0).ToPython()].InvokeMethod("__int__").As()); } @@ -73,22 +56,42 @@ public void MultidimensionalNumPyArray() [Test] public void Int64Array() { - PyObject np; - try - { - np = Py.Import("numpy"); - } - catch (PythonException) - { - Assert.Inconclusive("Numpy or dependency not installed"); - return; - } - var array = new long[,] { { 1, 2 }, { 3, 4 } }; var ndarray = np.InvokeMethod("asarray", array.ToPython()); Assert.AreEqual((2, 2), ndarray.GetAttr("shape").As<(int, int)>()); Assert.AreEqual(1, ndarray[(0, 0).ToPython()].InvokeMethod("__int__").As()); Assert.AreEqual(array[1, 0], ndarray[(1, 0).ToPython()].InvokeMethod("__int__").As()); } + + [Test] + public void VarArg() + { + dynamic zX = np.array(new[,] { { 1, 2, 3 }, { 4, 5, 6 }, { 8, 9, 0 } }); + dynamic grad = np.gradient(zX, 4.0, 5.0); + dynamic grad2 = np.InvokeMethod("gradient", new PyObject[] {zX, new PyFloat(4.0), new PyFloat(5.0)}); + + Assert.AreEqual(4.125, grad[0].sum().__float__().As(), 0.001); + Assert.AreEqual(-1.2, grad[1].sum().__float__().As(), 0.001); + Assert.AreEqual(4.125, grad2[0].sum().__float__().As(), 0.001); + Assert.AreEqual(-1.2, grad2[1].sum().__float__().As(), 0.001); + } + +#pragma warning disable IDE1006 + dynamic np + { + get + { + try + { + return Py.Import("numpy"); + } + catch (PythonException) + { + Assert.Inconclusive("Numpy or dependency not installed"); + return null; + } + } + } + } } From 31b9c4e56da54b9cde81630ea7cf032024624347 Mon Sep 17 00:00:00 2001 From: Benedikt Reinartz Date: Tue, 31 May 2022 14:47:29 +0200 Subject: [PATCH 67/75] Add necessary `Initialize()` call to the README --- README.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/README.rst b/README.rst index d5b280bfa..c2c2d53e1 100644 --- a/README.rst +++ b/README.rst @@ -69,6 +69,7 @@ Example static void Main(string[] args) { + PythonEngine.Initialize(); using (Py.GIL()) { dynamic np = Py.Import("numpy"); From 5ee587dbba7b2aec1fa1fc27c57a21a1093d731e Mon Sep 17 00:00:00 2001 From: Benedikt Reinartz Date: Mon, 30 May 2022 00:23:00 +0200 Subject: [PATCH 68/75] Fix demo scripts --- demo/helloform.py | 4 ++-- demo/splitter.py | 10 ++++++---- demo/wordpad.py | 9 ++++++--- 3 files changed, 14 insertions(+), 9 deletions(-) diff --git a/demo/helloform.py b/demo/helloform.py index 6e404771a..503d1c8b5 100644 --- a/demo/helloform.py +++ b/demo/helloform.py @@ -3,8 +3,7 @@ import clr -SWF = clr.AddReference("System.Windows.Forms") -print (SWF.Location) +clr.AddReference("System.Windows.Forms") import System.Windows.Forms as WinForms from System.Drawing import Size, Point @@ -14,6 +13,7 @@ class HelloApp(WinForms.Form): winforms programming and event-based programming in Python.""" def __init__(self): + super().__init__() self.Text = "Hello World From Python" self.AutoScaleBaseSize = Size(5, 13) self.ClientSize = Size(392, 117) diff --git a/demo/splitter.py b/demo/splitter.py index 126499db6..c209de6ab 100644 --- a/demo/splitter.py +++ b/demo/splitter.py @@ -4,6 +4,7 @@ import clr import System +clr.AddReference("System.Windows.Forms") import System.Windows.Forms as WinForms from System.Drawing import Color, Size, Point @@ -14,6 +15,7 @@ class Splitter(WinForms.Form): 'Creating a Multipane User Interface with Windows Forms'.""" def __init__(self): + super().__init__() # Create an instance of each control being used. self.components = System.ComponentModel.Container() @@ -26,13 +28,13 @@ def __init__(self): # Set properties of TreeView control. self.treeView1.Dock = WinForms.DockStyle.Left - self.treeView1.Width = self.ClientSize.Width / 3 + self.treeView1.Width = self.ClientSize.Width // 3 self.treeView1.TabIndex = 0 self.treeView1.Nodes.Add("TreeView") # Set properties of ListView control. self.listView1.Dock = WinForms.DockStyle.Top - self.listView1.Height = self.ClientSize.Height * 2 / 3 + self.listView1.Height = self.ClientSize.Height * 2 // 3 self.listView1.TabIndex = 0 self.listView1.Items.Add("ListView") @@ -52,7 +54,7 @@ def __init__(self): self.splitter2.TabIndex = 1 # Set TabStop to false for ease of use when negotiating UI. - self.splitter2.TabStop = 0 + self.splitter2.TabStop = False # Set properties of Form's Splitter control. self.splitter1.Location = System.Drawing.Point(121, 0) @@ -61,7 +63,7 @@ def __init__(self): self.splitter1.TabIndex = 1 # Set TabStop to false for ease of use when negotiating UI. - self.splitter1.TabStop = 0 + self.splitter1.TabStop = False # Add the appropriate controls to the Panel. for item in (self.richTextBox1, self.splitter2, self.listView1): diff --git a/demo/wordpad.py b/demo/wordpad.py index 409c8ad4d..b0666024a 100644 --- a/demo/wordpad.py +++ b/demo/wordpad.py @@ -3,6 +3,7 @@ import clr import System +clr.AddReference("System.Windows.Forms") import System.Windows.Forms as WinForms from System.IO import File @@ -15,8 +16,9 @@ class Wordpad(WinForms.Form): """A simple example winforms application similar to wordpad.""" def __init__(self): + super().__init__() self.filename = '' - self.word_wrap = 1 + self.word_wrap = True self.doctype = 1 self.InitializeComponent() self.NewDocument() @@ -194,10 +196,10 @@ def InitializeComponent(self): self.richTextBox.Dock = WinForms.DockStyle.Fill self.richTextBox.Size = System.Drawing.Size(795, 485) self.richTextBox.TabIndex = 0 - self.richTextBox.AutoSize = 1 + self.richTextBox.AutoSize = True self.richTextBox.ScrollBars = WinForms.RichTextBoxScrollBars.ForcedBoth self.richTextBox.Font = System.Drawing.Font("Tahoma", 10.0) - self.richTextBox.AcceptsTab = 1 + self.richTextBox.AcceptsTab = True self.richTextBox.Location = System.Drawing.Point(0, 0) self.statusBar.BackColor = System.Drawing.SystemColors.Control @@ -360,6 +362,7 @@ def SaveChangesDialog(self): class AboutForm(WinForms.Form): def __init__(self): + super.__init__() self.InitializeComponent() def InitializeComponent(self): From a6e435339b18e387d9b80f93b1393201f08ae639 Mon Sep 17 00:00:00 2001 From: Benedikt Reinartz Date: Thu, 9 Jun 2022 07:14:16 +0200 Subject: [PATCH 69/75] Fix remaining issues with wordpad demo --- demo/wordpad.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/demo/wordpad.py b/demo/wordpad.py index b0666024a..c7e998944 100644 --- a/demo/wordpad.py +++ b/demo/wordpad.py @@ -362,7 +362,7 @@ def SaveChangesDialog(self): class AboutForm(WinForms.Form): def __init__(self): - super.__init__() + super().__init__() self.InitializeComponent() def InitializeComponent(self): @@ -393,8 +393,8 @@ def InitializeComponent(self): self.Controls.AddRange((self.label1, self.btnClose)) self.FormBorderStyle = WinForms.FormBorderStyle.FixedDialog - self.MaximizeBox = 0 - self.MinimizeBox = 0 + self.MaximizeBox = False + self.MinimizeBox = False self.Name = "AboutForm" self.ShowInTaskbar = False self.StartPosition = WinForms.FormStartPosition.CenterScreen From 4d1e09a0765abcb633af859bfa96357373e500a9 Mon Sep 17 00:00:00 2001 From: Benedikt Reinartz Date: Mon, 13 Jun 2022 14:15:35 +0200 Subject: [PATCH 70/75] fixed ForbidPythonThreadsAttribute being ignored in certain scenarios (#1815) Co-authored-by: Victor Nova --- src/runtime/Types/MethodObject.cs | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/src/runtime/Types/MethodObject.cs b/src/runtime/Types/MethodObject.cs index 55ad06e2d..05198da76 100644 --- a/src/runtime/Types/MethodObject.cs +++ b/src/runtime/Types/MethodObject.cs @@ -27,7 +27,7 @@ internal class MethodObject : ExtensionType internal PyString? doc; internal MaybeType type; - public MethodObject(MaybeType type, string name, MethodBase[] info, bool allow_threads = MethodBinder.DefaultAllowThreads) + public MethodObject(MaybeType type, string name, MethodBase[] info, bool allow_threads) { this.type = type; this.name = name; @@ -45,6 +45,11 @@ public MethodObject(MaybeType type, string name, MethodBase[] info, bool allow_t binder.allow_threads = allow_threads; } + public MethodObject(MaybeType type, string name, MethodBase[] info) + : this(type, name, info, allow_threads: AllowThreads(info)) + { + } + public bool IsInstanceConstructor => name == "__init__"; public MethodObject WithOverloads(MethodBase[] overloads) @@ -206,5 +211,27 @@ public static NewReference tp_repr(BorrowedReference ob) var self = (MethodObject)GetManagedObject(ob)!; return Runtime.PyString_FromString($""); } + + static bool AllowThreads(MethodBase[] methods) + { + bool hasAllowOverload = false, hasForbidOverload = false; + foreach (var method in methods) + { + bool forbidsThreads = method.GetCustomAttribute(inherit: false) != null; + if (forbidsThreads) + { + hasForbidOverload = true; + } + else + { + hasAllowOverload = true; + } + } + + if (hasAllowOverload && hasForbidOverload) + throw new NotImplementedException("All method overloads currently must either allow or forbid Python threads together"); + + return !hasForbidOverload; + } } } From 43d16404b2466b2ee5c495abcb91d2950624fbe4 Mon Sep 17 00:00:00 2001 From: Benedikt Reinartz Date: Mon, 13 Jun 2022 15:33:59 +0200 Subject: [PATCH 71/75] Add test for simple enum int conversion (#1811) --- tests/test_enum.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/test_enum.py b/tests/test_enum.py index b2eb0569f..981fb735c 100644 --- a/tests/test_enum.py +++ b/tests/test_enum.py @@ -87,6 +87,11 @@ def test_ulong_enum(): assert Test.ULongEnum.Two == Test.ULongEnum(2) +def test_simple_enum_to_int(): + from System import DayOfWeek + assert int(DayOfWeek.Sunday) == 0 + + def test_long_enum_to_int(): assert int(Test.LongEnum.Max) == 9223372036854775807 assert int(Test.LongEnum.Min) == -9223372036854775808 @@ -138,6 +143,7 @@ def test_enum_undefined_value(): # explicitly permit undefined values Test.FieldTest().EnumField = Test.ShortEnum(20, True) + def test_enum_conversion(): """Test enumeration conversion.""" ob = Test.FieldTest() From f5de0bf9eb73708b80a75416a5e33c778e4228dc Mon Sep 17 00:00:00 2001 From: Benedikt Reinartz Date: Tue, 14 Jun 2022 08:22:33 +0200 Subject: [PATCH 72/75] Drop vendored find_libpython --- .github/workflows/ARM.yml | 2 +- .github/workflows/main.yml | 4 +- .github/workflows/nuget-preview.yml | 2 +- pythonnet/find_libpython/__init__.py | 400 ----------------------- pythonnet/find_libpython/__main__.py | 2 - pythonnet/util/__init__.py | 1 - requirements.txt | 3 + tests/domain_tests/test_domain_reload.py | 2 +- 8 files changed, 8 insertions(+), 408 deletions(-) delete mode 100644 pythonnet/find_libpython/__init__.py delete mode 100644 pythonnet/find_libpython/__main__.py delete mode 100644 pythonnet/util/__init__.py diff --git a/.github/workflows/ARM.yml b/.github/workflows/ARM.yml index 66f68366d..db580f741 100644 --- a/.github/workflows/ARM.yml +++ b/.github/workflows/ARM.yml @@ -36,7 +36,7 @@ jobs: - name: Set Python DLL path (non Windows) run: | - python -m pythonnet.find_libpython --export >> $GITHUB_ENV + echo PYTHONNET_PYDLL=$(python -m find_libpython) >> $GITHUB_ENV - name: Embedding tests run: dotnet test --logger "console;verbosity=detailed" src/embed_tests/ diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 2a77682f7..4a0fda9e9 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -57,12 +57,12 @@ jobs: - name: Set Python DLL path (non Windows) if: ${{ matrix.os != 'windows' }} run: | - python -m pythonnet.find_libpython --export >> $GITHUB_ENV + echo PYTHONNET_PYDLL=$(python -m find_libpython) >> $GITHUB_ENV - name: Set Python DLL path (Windows) if: ${{ matrix.os == 'windows' }} run: | - python -m pythonnet.find_libpython --export | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append + Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append -InputObject "PYTHONNET_PYDLL=$(python -m find_libpython)" - name: Embedding tests run: dotnet test --runtime any-${{ matrix.platform }} --logger "console;verbosity=detailed" src/embed_tests/ diff --git a/.github/workflows/nuget-preview.yml b/.github/workflows/nuget-preview.yml index 1dfa17d5a..d652f4b1e 100644 --- a/.github/workflows/nuget-preview.yml +++ b/.github/workflows/nuget-preview.yml @@ -46,7 +46,7 @@ jobs: - name: Set Python DLL path (non Windows) if: ${{ matrix.os != 'windows' }} run: | - python -m pythonnet.find_libpython --export >> $GITHUB_ENV + echo PYTHONNET_PYDLL=$(python -m find_libpython) >> $GITHUB_ENV - name: Python Tests run: pytest diff --git a/pythonnet/find_libpython/__init__.py b/pythonnet/find_libpython/__init__.py deleted file mode 100644 index 3ae28970e..000000000 --- a/pythonnet/find_libpython/__init__.py +++ /dev/null @@ -1,400 +0,0 @@ -#!/usr/bin/env python - -""" -Locate libpython associated with this Python executable. -""" - -# License -# -# Copyright 2018, Takafumi Arakaki -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be -# included in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -from __future__ import print_function, absolute_import - -from logging import getLogger -import ctypes.util -import functools -import os -import sys -import sysconfig - -logger = getLogger("find_libpython") - -is_windows = os.name == "nt" -is_apple = sys.platform == "darwin" - -SHLIB_SUFFIX = sysconfig.get_config_var("SHLIB_SUFFIX") -if SHLIB_SUFFIX is None: - if is_windows: - SHLIB_SUFFIX = ".dll" - else: - SHLIB_SUFFIX = ".so" -if is_apple: - # sysconfig.get_config_var("SHLIB_SUFFIX") can be ".so" in macOS. - # Let's not use the value from sysconfig. - SHLIB_SUFFIX = ".dylib" - - -def linked_libpython(): - """ - Find the linked libpython using dladdr (in *nix). - - Returns - ------- - path : str or None - A path to linked libpython. Return `None` if statically linked. - """ - if is_windows: - return _linked_libpython_windows() - return _linked_libpython_unix() - - -class Dl_info(ctypes.Structure): - _fields_ = [ - ("dli_fname", ctypes.c_char_p), - ("dli_fbase", ctypes.c_void_p), - ("dli_sname", ctypes.c_char_p), - ("dli_saddr", ctypes.c_void_p), - ] - - -def _linked_libpython_unix(): - if not sysconfig.get_config_var("Py_ENABLE_SHARED"): - return None - - libdl = ctypes.CDLL(ctypes.util.find_library("dl")) - libdl.dladdr.argtypes = [ctypes.c_void_p, ctypes.POINTER(Dl_info)] - libdl.dladdr.restype = ctypes.c_int - - dlinfo = Dl_info() - retcode = libdl.dladdr( - ctypes.cast(ctypes.pythonapi.Py_GetVersion, ctypes.c_void_p), - ctypes.pointer(dlinfo)) - if retcode == 0: # means error - return None - path = os.path.realpath(dlinfo.dli_fname.decode()) - return path - - -def _linked_libpython_windows(): - """ - Based on: https://stackoverflow.com/a/16659821 - """ - from ctypes.wintypes import HANDLE, LPWSTR, DWORD - - GetModuleFileName = ctypes.windll.kernel32.GetModuleFileNameW - GetModuleFileName.argtypes = [HANDLE, LPWSTR, DWORD] - GetModuleFileName.restype = DWORD - - MAX_PATH = 260 - try: - buf = ctypes.create_unicode_buffer(MAX_PATH) - GetModuleFileName(ctypes.pythonapi._handle, buf, MAX_PATH) - return buf.value - except (ValueError, OSError): - return None - - - -def library_name(name, suffix=SHLIB_SUFFIX, is_windows=is_windows): - """ - Convert a file basename `name` to a library name (no "lib" and ".so" etc.) - - >>> library_name("libpython3.7m.so") # doctest: +SKIP - 'python3.7m' - >>> library_name("libpython3.7m.so", suffix=".so", is_windows=False) - 'python3.7m' - >>> library_name("libpython3.7m.dylib", suffix=".dylib", is_windows=False) - 'python3.7m' - >>> library_name("python37.dll", suffix=".dll", is_windows=True) - 'python37' - """ - if not is_windows and name.startswith("lib"): - name = name[len("lib"):] - if suffix and name.endswith(suffix): - name = name[:-len(suffix)] - return name - - -def append_truthy(list, item): - if item: - list.append(item) - - -def uniquifying(items): - """ - Yield items while excluding the duplicates and preserving the order. - - >>> list(uniquifying([1, 2, 1, 2, 3])) - [1, 2, 3] - """ - seen = set() - for x in items: - if x not in seen: - yield x - seen.add(x) - - -def uniquified(func): - """ Wrap iterator returned from `func` by `uniquifying`. """ - @functools.wraps(func) - def wrapper(*args, **kwds): - return uniquifying(func(*args, **kwds)) - return wrapper - - -@uniquified -def candidate_names(suffix=SHLIB_SUFFIX): - """ - Iterate over candidate file names of libpython. - - Yields - ------ - name : str - Candidate name libpython. - """ - LDLIBRARY = sysconfig.get_config_var("LDLIBRARY") - if LDLIBRARY and not LDLIBRARY.endswith(".a"): - yield LDLIBRARY - - LIBRARY = sysconfig.get_config_var("LIBRARY") - if LIBRARY and not LIBRARY.endswith(".a"): - yield os.path.splitext(LIBRARY)[0] + suffix - - dlprefix = "" if is_windows else "lib" - sysdata = dict( - v=sys.version_info, - # VERSION is X.Y in Linux/macOS and XY in Windows: - VERSION=(sysconfig.get_python_version() or - "{v.major}.{v.minor}".format(v=sys.version_info) or - sysconfig.get_config_var("VERSION")), - ABIFLAGS=(sysconfig.get_config_var("ABIFLAGS") or - sysconfig.get_config_var("abiflags") or ""), - ) - - for stem in [ - "python{VERSION}{ABIFLAGS}".format(**sysdata), - "python{VERSION}".format(**sysdata), - "python{v.major}".format(**sysdata), - "python", - ]: - yield dlprefix + stem + suffix - - - -@uniquified -def candidate_paths(suffix=SHLIB_SUFFIX): - """ - Iterate over candidate paths of libpython. - - Yields - ------ - path : str or None - Candidate path to libpython. The path may not be a fullpath - and may not exist. - """ - - yield linked_libpython() - - # List candidates for directories in which libpython may exist - lib_dirs = [] - append_truthy(lib_dirs, sysconfig.get_config_var('LIBPL')) - append_truthy(lib_dirs, sysconfig.get_config_var('srcdir')) - append_truthy(lib_dirs, sysconfig.get_config_var("LIBDIR")) - - # LIBPL seems to be the right config_var to use. It is the one - # used in python-config when shared library is not enabled: - # https://github.com/python/cpython/blob/v3.7.0/Misc/python-config.in#L55-L57 - # - # But we try other places just in case. - - if is_windows: - lib_dirs.append(os.path.join(os.path.dirname(sys.executable))) - else: - lib_dirs.append(os.path.join( - os.path.dirname(os.path.dirname(sys.executable)), - "lib")) - - # For macOS: - append_truthy(lib_dirs, sysconfig.get_config_var("PYTHONFRAMEWORKPREFIX")) - - lib_dirs.append(sys.exec_prefix) - lib_dirs.append(os.path.join(sys.exec_prefix, "lib")) - - lib_basenames = list(candidate_names(suffix=suffix)) - - for directory in lib_dirs: - for basename in lib_basenames: - yield os.path.join(directory, basename) - - # In macOS and Windows, ctypes.util.find_library returns a full path: - for basename in lib_basenames: - yield ctypes.util.find_library(library_name(basename)) - -# Possibly useful links: -# * https://packages.ubuntu.com/bionic/amd64/libpython3.6/filelist -# * https://github.com/Valloric/ycmd/issues/518 -# * https://github.com/Valloric/ycmd/pull/519 - - -def normalize_path(path, suffix=SHLIB_SUFFIX, is_apple=is_apple): - """ - Normalize shared library `path` to a real path. - - If `path` is not a full path, `None` is returned. If `path` does - not exists, append `SHLIB_SUFFIX` and check if it exists. - Finally, the path is canonicalized by following the symlinks. - - Parameters - ---------- - path : str ot None - A candidate path to a shared library. - """ - if not path: - return None - if not os.path.isabs(path): - return None - if os.path.exists(path): - return os.path.realpath(path) - if os.path.exists(path + suffix): - return os.path.realpath(path + suffix) - if is_apple: - return normalize_path(_remove_suffix_apple(path), - suffix=".so", is_apple=False) - return None - - -def _remove_suffix_apple(path): - """ - Strip off .so or .dylib. - - >>> _remove_suffix_apple("libpython.so") - 'libpython' - >>> _remove_suffix_apple("libpython.dylib") - 'libpython' - >>> _remove_suffix_apple("libpython3.7") - 'libpython3.7' - """ - if path.endswith(".dylib"): - return path[:-len(".dylib")] - if path.endswith(".so"): - return path[:-len(".so")] - return path - - -@uniquified -def finding_libpython(): - """ - Iterate over existing libpython paths. - - The first item is likely to be the best one. - - Yields - ------ - path : str - Existing path to a libpython. - """ - logger.debug("is_windows = %s", is_windows) - logger.debug("is_apple = %s", is_apple) - for path in candidate_paths(): - logger.debug("Candidate: %s", path) - normalized = normalize_path(path) - if normalized: - logger.debug("Found: %s", normalized) - yield normalized - else: - logger.debug("Not found.") - - -def find_libpython(): - """ - Return a path (`str`) to libpython or `None` if not found. - - Parameters - ---------- - path : str or None - Existing path to the (supposedly) correct libpython. - """ - for path in finding_libpython(): - return os.path.realpath(path) - - -def print_all(items): - for x in items: - print(x) - - -def cli_find_libpython(cli_op, verbose, export): - import logging - # Importing `logging` module here so that using `logging.debug` - # instead of `logger.debug` outside of this function becomes an - # error. - - if verbose: - logging.basicConfig( - format="%(levelname)s %(message)s", - level=logging.DEBUG) - - if cli_op == "list-all": - print_all(finding_libpython()) - elif cli_op == "candidate-names": - print_all(candidate_names()) - elif cli_op == "candidate-paths": - print_all(p for p in candidate_paths() if p and os.path.isabs(p)) - else: - path = find_libpython() - if path is None: - return 1 - if export: - print(f"PYTHONNET_PYDLL={path}") - else: - print(path, end="") - - -def main(args=None): - import argparse - parser = argparse.ArgumentParser( - description=__doc__) - parser.add_argument( - "--verbose", "-v", action="store_true", - help="Print debugging information.") - - group = parser.add_mutually_exclusive_group() - group.add_argument( - "--list-all", - action="store_const", dest="cli_op", const="list-all", - help="Print list of all paths found.") - group.add_argument( - "--candidate-names", - action="store_const", dest="cli_op", const="candidate-names", - help="Print list of candidate names of libpython.") - group.add_argument( - "--candidate-paths", - action="store_const", dest="cli_op", const="candidate-paths", - help="Print list of candidate paths of libpython.") - group.add_argument( - "--export", - action="store_true", - help="Print as an environment export expression" - ) - - ns = parser.parse_args(args) - parser.exit(cli_find_libpython(**vars(ns))) diff --git a/pythonnet/find_libpython/__main__.py b/pythonnet/find_libpython/__main__.py deleted file mode 100644 index 031df43e1..000000000 --- a/pythonnet/find_libpython/__main__.py +++ /dev/null @@ -1,2 +0,0 @@ -from . import main -main() diff --git a/pythonnet/util/__init__.py b/pythonnet/util/__init__.py deleted file mode 100644 index 75d4bad8c..000000000 --- a/pythonnet/util/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .find_libpython import find_libpython diff --git a/requirements.txt b/requirements.txt index f5aedfc3f..8e911ef5a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -10,3 +10,6 @@ wheel pycparser setuptools clr-loader + +# Discover libpython +find_libpython \ No newline at end of file diff --git a/tests/domain_tests/test_domain_reload.py b/tests/domain_tests/test_domain_reload.py index d04d5a1f6..8999e481b 100644 --- a/tests/domain_tests/test_domain_reload.py +++ b/tests/domain_tests/test_domain_reload.py @@ -4,7 +4,7 @@ import pytest -from pythonnet.find_libpython import find_libpython +from find_libpython import find_libpython libpython = find_libpython() pytestmark = pytest.mark.xfail(libpython is None, reason="Can't find suitable libpython") From 7da78892d7c555284591f17d0c30c0ab2ae28ef2 Mon Sep 17 00:00:00 2001 From: Benedikt Reinartz Date: Fri, 17 Jun 2022 13:04:20 +0200 Subject: [PATCH 73/75] Allow configuring .NET runtimes via environment variables --- .github/workflows/ARM.yml | 2 +- .github/workflows/main.yml | 5 +- CHANGELOG.md | 1 + pythonnet/__init__.py | 116 ++++++++++++++++++++++++++++++------- tests/conftest.py | 92 +++++++++++++---------------- 5 files changed, 142 insertions(+), 74 deletions(-) diff --git a/.github/workflows/ARM.yml b/.github/workflows/ARM.yml index db580f741..af257bcb8 100644 --- a/.github/workflows/ARM.yml +++ b/.github/workflows/ARM.yml @@ -45,7 +45,7 @@ jobs: run: python -m pytest --runtime mono - name: Python Tests (.NET Core) - run: python -m pytest --runtime netcore + run: python -m pytest --runtime coreclr - name: Python tests run from .NET run: dotnet test src/python_tests_runner/ diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 4a0fda9e9..2cc793621 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -1,4 +1,4 @@ -name: Main (x64) +name: Main on: push: @@ -73,9 +73,10 @@ jobs: if: ${{ matrix.os != 'windows' }} run: pytest --runtime mono + # TODO: Run these tests on Windows x86 - name: Python Tests (.NET Core) if: ${{ matrix.platform == 'x64' }} - run: pytest --runtime netcore + run: pytest --runtime coreclr - name: Python Tests (.NET Framework) if: ${{ matrix.os == 'windows' }} diff --git a/CHANGELOG.md b/CHANGELOG.md index 069629021..52ee08484 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,7 @@ and other `PyObject` derived types when called from Python. - .NET classes, that have `__call__` method are callable from Python - `PyIterable` type, that wraps any iterable object in Python - `PythonEngine` properties for supported Python versions: `MinSupportedVersion`, `MaxSupportedVersion`, and `IsSupportedVersion` +- The runtime that is loaded on `import clr` can now be configured via environment variables ### Changed diff --git a/pythonnet/__init__.py b/pythonnet/__init__.py index 10dc403e4..fa6ed45cf 100644 --- a/pythonnet/__init__.py +++ b/pythonnet/__init__.py @@ -1,43 +1,115 @@ import sys +from pathlib import Path +from typing import Dict, Optional, Union import clr_loader -_RUNTIME = None -_LOADER_ASSEMBLY = None -_FFI = None -_LOADED = False +__all__ = ["set_runtime", "set_default_runtime", "load"] +_RUNTIME: Optional[clr_loader.Runtime] = None +_LOADER_ASSEMBLY: Optional[clr_loader.wrappers.Assembly] = None +_LOADED: bool = False + + +def set_runtime(runtime: Union[clr_loader.Runtime, str], **params: str) -> None: + """Set up a clr_loader runtime without loading it + + :param runtime: Either an already initialised `clr_loader` runtime, or one + of netfx, coreclr, mono, or default. If a string parameter is given, the + runtime will be created.""" -def set_runtime(runtime): global _RUNTIME if _LOADED: - raise RuntimeError("The runtime {} has already been loaded".format(_RUNTIME)) + raise RuntimeError(f"The runtime {_RUNTIME} has already been loaded") + + if isinstance(runtime, str): + runtime = _create_runtime_from_spec(runtime, params) _RUNTIME = runtime -def set_default_runtime() -> None: - if sys.platform == "win32": - set_runtime(clr_loader.get_netfx()) +def _get_params_from_env(prefix: str) -> Dict[str, str]: + from os import environ + + full_prefix = f"PYTHONNET_{prefix.upper()}" + len_ = len(full_prefix) + + env_vars = { + (k[len_:].lower()): v + for k, v in environ.items() + if k.upper().startswith(full_prefix) + } + + return env_vars + + +def _create_runtime_from_spec( + spec: str, params: Optional[Dict[str, str]] = None +) -> clr_loader.Runtime: + if spec == "default": + if sys.platform == "win32": + spec = "netfx" + else: + spec = "mono" + + params = params or _get_params_from_env(spec) + + if spec == "netfx": + return clr_loader.get_netfx(**params) + elif spec == "mono": + return clr_loader.get_mono(**params) + elif spec == "coreclr": + return clr_loader.get_coreclr(**params) else: - set_runtime(clr_loader.get_mono()) + raise RuntimeError(f"Invalid runtime name: '{spec}'") -def load(): - global _FFI, _LOADED, _LOADER_ASSEMBLY +def set_default_runtime() -> None: + """Set up the default runtime + + This will use the environment variable PYTHONNET_RUNTIME to decide the + runtime to use, which may be one of netfx, coreclr or mono. The parameters + of the respective clr_loader.get_ functions can also be given as + environment variables, named `PYTHONNET__`. In + particular, to use `PYTHONNET_RUNTIME=coreclr`, the variable + `PYTHONNET_CORECLR_RUNTIME_CONFIG` has to be set to a valid + `.runtimeconfig.json`. + + If no environment variable is specified, a globally installed Mono is used + for all environments but Windows, on Windows the legacy .NET Framework is + used. + """ + from os import environ + + print("Set default RUNTIME") + raise RuntimeError("Shouldn't be called here") + + spec = environ.get("PYTHONNET_RUNTIME", "default") + runtime = _create_runtime_from_spec(spec) + set_runtime(runtime) + + +def load( + runtime: Union[clr_loader.Runtime, str] = "default", **params: Dict[str, str] +) -> None: + """Load Python.NET in the specified runtime + + The same parameters as for `set_runtime` can be used. By default, + `set_default_runtime` is called if no environment has been set yet and no + parameters are passed.""" + global _LOADED, _LOADER_ASSEMBLY if _LOADED: return - from os.path import join, dirname + if _RUNTIME is None: + set_runtime(runtime, **params) if _RUNTIME is None: - # TODO: Warn, in the future the runtime must be set explicitly, either - # as a config/env variable or via set_runtime - set_default_runtime() + raise RuntimeError("No valid runtime selected") - dll_path = join(dirname(__file__), "runtime", "Python.Runtime.dll") + dll_path = Path(__file__).parent / "runtime" / "Python.Runtime.dll" - _LOADER_ASSEMBLY = _RUNTIME.get_assembly(dll_path) + _LOADER_ASSEMBLY = _RUNTIME.get_assembly(str(dll_path)) func = _LOADER_ASSEMBLY["Python.Runtime.Loader.Initialize"] if func(b"") != 0: @@ -48,13 +120,17 @@ def load(): atexit.register(unload) -def unload(): - global _RUNTIME +def unload() -> None: + """Explicitly unload a laoded runtime and shut down Python.NET""" + + global _RUNTIME, _LOADER_ASSEMBLY if _LOADER_ASSEMBLY is not None: func = _LOADER_ASSEMBLY["Python.Runtime.Loader.Shutdown"] if func(b"full_shutdown") != 0: raise RuntimeError("Failed to call Python.NET shutdown") + _LOADER_ASSEMBLY = None + if _RUNTIME is not None: # TODO: Add explicit `close` to clr_loader _RUNTIME = None diff --git a/tests/conftest.py b/tests/conftest.py index 89db46eca..fcd1d224a 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -8,91 +8,83 @@ import os import sys import sysconfig +from pathlib import Path from subprocess import check_call from tempfile import mkdtemp import shutil import pytest -from pythonnet import set_runtime - # Add path for `Python.Test` -cwd = os.path.dirname(__file__) -fixtures_path = os.path.join(cwd, "fixtures") -sys.path.append(fixtures_path) +cwd = Path(__file__).parent +fixtures_path = cwd / "fixtures" +sys.path.append(str(fixtures_path)) + def pytest_addoption(parser): parser.addoption( "--runtime", action="store", default="default", - help="Must be one of default, netcore, netfx and mono" + help="Must be one of default, coreclr, netfx and mono", ) + collect_ignore = [] + def pytest_configure(config): global bin_path if "clr" in sys.modules: # Already loaded (e.g. by the C# test runner), skip build import clr + clr.AddReference("Python.Test") return runtime_opt = config.getoption("runtime") - - test_proj_path = os.path.join(cwd, "..", "src", "testing") - - if runtime_opt not in ["netcore", "netfx", "mono", "default"]: + if runtime_opt not in ["coreclr", "netfx", "mono", "default"]: raise RuntimeError(f"Invalid runtime: {runtime_opt}") - bin_path = mkdtemp() - - # tmpdir_factory.mktemp(f"pythonnet-{runtime_opt}") - - fw = "net6.0" if runtime_opt == "netcore" else "netstandard2.0" - - check_call(["dotnet", "publish", "-f", fw, "-o", bin_path, test_proj_path]) - - sys.path.append(bin_path) - - if runtime_opt == "default": - pass - elif runtime_opt == "netfx": - from clr_loader import get_netfx - runtime = get_netfx() - set_runtime(runtime) - elif runtime_opt == "mono": - from clr_loader import get_mono - runtime = get_mono() - set_runtime(runtime) - elif runtime_opt == "netcore": - from clr_loader import get_coreclr - rt_config_path = os.path.join(bin_path, "Python.Test.runtimeconfig.json") - runtime = get_coreclr(rt_config_path) - set_runtime(runtime) - - import clr - clr.AddReference("Python.Test") + test_proj_path = cwd.parent / "src" / "testing" + bin_path = Path(mkdtemp()) - soft_mode = False - try: - os.environ['PYTHONNET_SHUTDOWN_MODE'] == 'Soft' - except: pass + fw = "netstandard2.0" + runtime_params = {} - if config.getoption("--runtime") == "netcore" or soft_mode\ - : + if runtime_opt == "coreclr": + fw = "net6.0" + runtime_params["runtime_config"] = str( + bin_path / "Python.Test.runtimeconfig.json" + ) collect_ignore.append("domain_tests/test_domain_reload.py") else: - domain_tests_dir = os.path.join(os.path.dirname(__file__), "domain_tests") - bin_path = os.path.join(domain_tests_dir, "bin") - build_cmd = ["dotnet", "build", domain_tests_dir, "-o", bin_path] + domain_tests_dir = cwd / "domain_tests" + domain_bin_path = domain_tests_dir / "bin" + build_cmd = [ + "dotnet", + "build", + str(domain_tests_dir), + "-o", + str(domain_bin_path), + ] is_64bits = sys.maxsize > 2**32 if not is_64bits: build_cmd += ["/p:Prefer32Bit=True"] check_call(build_cmd) + check_call( + ["dotnet", "publish", "-f", fw, "-o", str(bin_path), str(test_proj_path)] + ) + + from pythonnet import load + + load(runtime_opt, **runtime_params) + + import clr + sys.path.append(str(bin_path)) + clr.AddReference("Python.Test") def pytest_unconfigure(config): @@ -102,6 +94,7 @@ def pytest_unconfigure(config): except Exception: pass + def pytest_report_header(config): """Generate extra report headers""" # FIXME: https://github.com/pytest-dev/pytest/issues/2257 @@ -109,11 +102,8 @@ def pytest_report_header(config): arch = "x64" if is_64bits else "x86" ucs = ctypes.sizeof(ctypes.c_wchar) libdir = sysconfig.get_config_var("LIBDIR") - shared = bool(sysconfig.get_config_var("Py_ENABLE_SHARED")) - header = ("Arch: {arch}, UCS: {ucs}, LIBDIR: {libdir}, " - "Py_ENABLE_SHARED: {shared}".format(**locals())) - return header + return f"Arch: {arch}, UCS: {ucs}, LIBDIR: {libdir}" @pytest.fixture() From a49f3a885f8eb40bc0488b8d483ef0d5842f0834 Mon Sep 17 00:00:00 2001 From: Benedikt Reinartz Date: Mon, 27 Jun 2022 11:45:31 +0200 Subject: [PATCH 74/75] Take GIL when checking if error occurred on shutdown (#1836) --- src/runtime/PythonEngine.cs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/runtime/PythonEngine.cs b/src/runtime/PythonEngine.cs index f184ebe88..6e9c4d1f1 100644 --- a/src/runtime/PythonEngine.cs +++ b/src/runtime/PythonEngine.cs @@ -355,12 +355,17 @@ public static void Shutdown() { return; } - if (Exceptions.ErrorOccurred()) + + using (Py.GIL()) { - throw new InvalidOperationException( - "Python error indicator is set", - innerException: PythonException.PeekCurrentOrNull(out _)); + if (Exceptions.ErrorOccurred()) + { + throw new InvalidOperationException( + "Python error indicator is set", + innerException: PythonException.PeekCurrentOrNull(out _)); + } } + // If the shutdown handlers trigger a domain unload, // don't call shutdown again. AppDomain.CurrentDomain.DomainUnload -= OnDomainUnload; From 28a78ed6e9a77c6854be8269ad82cb2792ad88a8 Mon Sep 17 00:00:00 2001 From: Benedikt Reinartz Date: Mon, 27 Jun 2022 11:47:57 +0200 Subject: [PATCH 75/75] Bump version to 3.0.0-rc1 --- version.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.txt b/version.txt index e16bcc8be..cf8213193 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -3.0.0-dev1 +3.0.0-rc1