From 0d6194dce15ae5444697d0be80243a9d5f699504 Mon Sep 17 00:00:00 2001
From: inna-w <40801200+inna-w@users.noreply.github.com>
Date: Mon, 3 Jun 2019 22:16:51 +0300
Subject: [PATCH 001/998] Generate NuGet package during build (#875)
* Generate NuGet package during build
* Comment out PackageReleaseNotes field
* Update CHANGELOG and AUTHORS
---
AUTHORS.md | 5 +++--
CHANGELOG.md | 2 ++
appveyor.yml | 1 +
setup.py | 1 +
src/runtime/Python.Runtime.15.csproj | 21 ++++++++++++++-------
5 files changed, 21 insertions(+), 9 deletions(-)
diff --git a/AUTHORS.md b/AUTHORS.md
index 27aae63f4..ba954b47d 100644
--- a/AUTHORS.md
+++ b/AUTHORS.md
@@ -27,9 +27,10 @@
- David Lechner ([@dlech](https://github.com/dlech))
- Dmitriy Se ([@dmitriyse](https://github.com/dmitriyse))
- He-chien Tsai ([@t3476](https://github.com/t3476))
-- Ivan Cronyn ([@cronan](https://github.com/cronan))
+- Inna Wiesel ([@inna-w](https://github.com/inna-w))
+- Ivan Cronyn ([@cronan](https://github.com/cronan))
- Jan Krivanek ([@jakrivan](https://github.com/jakrivan))
-- Jeff Reback ([@jreback](https://github.com/jreback))
+- Jeff Reback ([@jreback](https://github.com/jreback))
- Joe Frayne ([@jfrayne](https://github.com/jfrayne))
- John Burnett ([@johnburnett](https://github.com/johnburnett))
- John Wilkes ([@jbw3](https://github.com/jbw3))
diff --git a/CHANGELOG.md b/CHANGELOG.md
index f2d276e51..b5531bf47 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,6 +9,8 @@ This document follows the conventions laid out in [Keep a CHANGELOG][].
### Added
+- Added automatic NuGet package generation in appveyor and local builds
+
### Changed
### Fixed
diff --git a/appveyor.yml b/appveyor.yml
index 74b9a9c8e..445f9bb5a 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -72,6 +72,7 @@ on_finish:
artifacts:
- path: dist\*
+ - path: '.\src\runtime\bin\*.nupkg'
notifications:
- provider: Slack
diff --git a/setup.py b/setup.py
index 8528753b0..53c7f3f67 100644
--- a/setup.py
+++ b/setup.py
@@ -334,6 +334,7 @@ def build_extension(self, ext):
),
'/p:PythonBuildDir="{}"'.format(os.path.abspath(dest_dir)),
'/p:PythonInteropFile="{}"'.format(os.path.basename(interop_file)),
+ "/p:PackageId=pythonnet_py{0}{1}_{2}".format(PY_MAJOR, PY_MINOR, ARCH),
"/verbosity:{}".format(VERBOSITY),
]
diff --git a/src/runtime/Python.Runtime.15.csproj b/src/runtime/Python.Runtime.15.csproj
index fb0020356..a4d1773f7 100644
--- a/src/runtime/Python.Runtime.15.csproj
+++ b/src/runtime/Python.Runtime.15.csproj
@@ -7,14 +7,21 @@
net45
Python.Runtime
Python.Runtime
- Python.Runtime
+ pythonnet
2.4.1
- false
- false
- false
- false
- false
- false
+ true
+ false
+
Python for .NET
+ Copyright (c) 2006-2019 the contributors of the 'Python for .NET' project
+ Python and CLR (.NET and Mono) cross-platform language interop
+ pythonnet
+ https://github.com/pythonnet/pythonnet/blob/master/LICENSE
+ https://github.com/pythonnet/pythonnet
+ git
+
+ python interop dynamic dlr Mono pinvoke
+ https://raw.githubusercontent.com/pythonnet/pythonnet/master/src/console/python-clear.ico
+ https://pythonnet.github.io/
bin\
false
$(OutputPath)\$(AssemblyName).xml
From d1928dcfde6cca317a33abadde2da27fd09adc60 Mon Sep 17 00:00:00 2001
From: amos402
Date: Fri, 14 Jun 2019 02:35:50 +0800
Subject: [PATCH 002/998] Drop LoadLibrary dependency
---
src/runtime/runtime.cs | 107 ++---------------------------------------
1 file changed, 5 insertions(+), 102 deletions(-)
diff --git a/src/runtime/runtime.cs b/src/runtime/runtime.cs
index 7623200e0..0359dac1a 100644
--- a/src/runtime/runtime.cs
+++ b/src/runtime/runtime.cs
@@ -7,97 +7,6 @@
namespace Python.Runtime
{
- [SuppressUnmanagedCodeSecurity]
- internal static class NativeMethods
- {
-#if MONO_LINUX || MONO_OSX
-#if NETSTANDARD
- private static int RTLD_NOW = 0x2;
-#if MONO_LINUX
- private static int RTLD_GLOBAL = 0x100;
- private static IntPtr RTLD_DEFAULT = IntPtr.Zero;
- private const string NativeDll = "libdl.so";
- public static IntPtr LoadLibrary(string fileName)
- {
- return dlopen($"lib{fileName}.so", RTLD_NOW | RTLD_GLOBAL);
- }
-#elif MONO_OSX
- private static int RTLD_GLOBAL = 0x8;
- private const string NativeDll = "/usr/lib/libSystem.dylib";
- private static IntPtr RTLD_DEFAULT = new IntPtr(-2);
-
- public static IntPtr LoadLibrary(string fileName)
- {
- return dlopen($"lib{fileName}.dylib", RTLD_NOW | RTLD_GLOBAL);
- }
-#endif
-#else
- private static int RTLD_NOW = 0x2;
- private static int RTLD_SHARED = 0x20;
-#if MONO_OSX
- private static IntPtr RTLD_DEFAULT = new IntPtr(-2);
- private const string NativeDll = "__Internal";
-#elif MONO_LINUX
- private static IntPtr RTLD_DEFAULT = IntPtr.Zero;
- private const string NativeDll = "libdl.so";
-#endif
-
- public static IntPtr LoadLibrary(string fileName)
- {
- return dlopen(fileName, RTLD_NOW | RTLD_SHARED);
- }
-#endif
-
-
- public static void FreeLibrary(IntPtr handle)
- {
- dlclose(handle);
- }
-
- public static IntPtr GetProcAddress(IntPtr dllHandle, string name)
- {
- // look in the exe if dllHandle is NULL
- if (dllHandle == IntPtr.Zero)
- {
- dllHandle = RTLD_DEFAULT;
- }
-
- // clear previous errors if any
- dlerror();
- IntPtr res = dlsym(dllHandle, name);
- IntPtr errPtr = dlerror();
- if (errPtr != IntPtr.Zero)
- {
- throw new Exception("dlsym: " + Marshal.PtrToStringAnsi(errPtr));
- }
- return res;
- }
-
- [DllImport(NativeDll, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
- public static extern IntPtr dlopen(String fileName, int flags);
-
- [DllImport(NativeDll, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
- private static extern IntPtr dlsym(IntPtr handle, String symbol);
-
- [DllImport(NativeDll, CallingConvention = CallingConvention.Cdecl)]
- private static extern int dlclose(IntPtr handle);
-
- [DllImport(NativeDll, CallingConvention = CallingConvention.Cdecl)]
- private static extern IntPtr dlerror();
-#else // Windows
- private const string NativeDll = "kernel32.dll";
-
- [DllImport(NativeDll)]
- public static extern IntPtr LoadLibrary(string dllToLoad);
-
- [DllImport(NativeDll)]
- public static extern IntPtr GetProcAddress(IntPtr hModule, string procedureName);
-
- [DllImport(NativeDll)]
- public static extern bool FreeLibrary(IntPtr hModule);
-#endif
- }
-
///
/// Encapsulates the low-level Python C API. Note that it is
/// the responsibility of the caller to have acquired the GIL
@@ -395,18 +304,12 @@ internal static void Initialize(bool initSigs = false)
IntPtr dllLocal = IntPtr.Zero;
- if (_PythonDll != "__Internal")
- {
- dllLocal = NativeMethods.LoadLibrary(_PythonDll);
- }
- _PyObject_NextNotImplemented = NativeMethods.GetProcAddress(dllLocal, "_PyObject_NextNotImplemented");
+ var zipimport = PyImport_ImportModule("zipimport");
+ var ZipImportError = PyObject_GetAttrString(zipimport, "ZipImportError");
+ _PyObject_NextNotImplemented = Marshal.ReadIntPtr(ZipImportError, TypeOffset.tp_iternext);
+ XDecref(ZipImportError);
+ XDecref(zipimport);
-#if !(MONO_LINUX || MONO_OSX)
- if (dllLocal != IntPtr.Zero)
- {
- NativeMethods.FreeLibrary(dllLocal);
- }
-#endif
// Initialize data about the platform we're running on. We need
// this for the type manager and potentially other details. Must
// happen after caching the python types, above.
From f000e0846407a5f0a8c30b612b3c874981330feb Mon Sep 17 00:00:00 2001
From: amos402
Date: Fri, 14 Jun 2019 02:51:24 +0800
Subject: [PATCH 003/998] Explain for getting _PyObject_NextNotImplemented
---
src/runtime/runtime.cs | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/src/runtime/runtime.cs b/src/runtime/runtime.cs
index 0359dac1a..7f0349da4 100644
--- a/src/runtime/runtime.cs
+++ b/src/runtime/runtime.cs
@@ -304,6 +304,10 @@ internal static void Initialize(bool initSigs = false)
IntPtr dllLocal = IntPtr.Zero;
+ // Since `_PyObject_NextNotImplemented` would set to a heap class
+ // for tp_iternext which doesn't implement __next__.
+ // Thus we need a heap class to get it, the ZipImportError is a
+ // heap class and it's in builtins, so we can use it as a trick.
var zipimport = PyImport_ImportModule("zipimport");
var ZipImportError = PyObject_GetAttrString(zipimport, "ZipImportError");
_PyObject_NextNotImplemented = Marshal.ReadIntPtr(ZipImportError, TypeOffset.tp_iternext);
From f8824005d489f7d5f3d7bfb95f59af3a2dd03cfe Mon Sep 17 00:00:00 2001
From: amos402
Date: Fri, 14 Jun 2019 03:18:58 +0800
Subject: [PATCH 004/998] Update changelog and authors
---
AUTHORS.md | 1 +
CHANGELOG.md | 3 ++-
2 files changed, 3 insertions(+), 1 deletion(-)
diff --git a/AUTHORS.md b/AUTHORS.md
index ba954b47d..4f4c9862e 100644
--- a/AUTHORS.md
+++ b/AUTHORS.md
@@ -52,6 +52,7 @@
- William Sardar ([@williamsardar])(https://github.com/williamsardar)
- Xavier Dupré ([@sdpython](https://github.com/sdpython))
- Zane Purvis ([@zanedp](https://github.com/zanedp))
+- ([@amos402]https://github.com/amos402)
- ([@bltribble](https://github.com/bltribble))
- ([@civilx64](https://github.com/civilx64))
- ([@GSPP](https://github.com/GSPP))
diff --git a/CHANGELOG.md b/CHANGELOG.md
index b5531bf47..d8430da0a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -42,6 +42,7 @@ This document follows the conventions laid out in [Keep a CHANGELOG][].
- PythonEngine.Intialize will now call `Py_InitializeEx` with a default value of 0, so signals will not be configured by default on embedding. This is different from the previous behaviour, where `Py_Initialize` was called instead, which sets initSigs to 1. ([#449][i449])
- Refactored MethodBinder.Bind in preparation to make it extensible (#829)
- Look for installed Windows 10 sdk's during installation instead of relying on specific versions.
+- Remove `LoadLibrary` call. ([#880][p880])
### Fixed
@@ -57,7 +58,7 @@ This document follows the conventions laid out in [Keep a CHANGELOG][].
- Fixed conversion of 'float' and 'double' values ([#486][i486])
- Fixed 'clrmethod' for python 2 ([#492][i492])
- Fixed double calling of constructor when deriving from .NET class ([#495][i495])
-- Fixed `clr.GetClrType` when iterating over `System` members ([#607][p607])
+- Fixed `clr.GetClrType` when iterating over `System` members ([#607][p607])
- Fixed `LockRecursionException` when loading assemblies ([#627][i627])
- Fixed errors breaking .NET Remoting on method invoke ([#276][i276])
- Fixed PyObject.GetHashCode ([#676][i676])
From 43c972d50fa2526f5f9ad73c591f6170c0538854 Mon Sep 17 00:00:00 2001
From: Victor
Date: Mon, 17 Jun 2019 12:31:11 -0700
Subject: [PATCH 005/998] Enable C# parameters of type `object` accept any
argument, passed from Python (#853)
* added a regression test for #881
https://github.com/pythonnet/pythonnet/issues/811
* when converting to object, wrap values of unknown type into PyObject instead of failing
This enables overload resolution with object parameters to behave the same way PyObject parameters behave - e.g. allow any Python object to be passed as PyObject as fallback.
Resolves #811
---
CHANGELOG.md | 1 +
src/embed_tests/Python.EmbeddingTest.csproj | 1 +
src/embed_tests/TestInstanceWrapping.cs | 58 +++++++++++++++++++++
src/runtime/converter.cs | 9 ++--
4 files changed, 63 insertions(+), 6 deletions(-)
create mode 100644 src/embed_tests/TestInstanceWrapping.cs
diff --git a/CHANGELOG.md b/CHANGELOG.md
index b5531bf47..d8683622f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -41,6 +41,7 @@ This document follows the conventions laid out in [Keep a CHANGELOG][].
- Reattach python exception traceback information (#545)
- PythonEngine.Intialize will now call `Py_InitializeEx` with a default value of 0, so signals will not be configured by default on embedding. This is different from the previous behaviour, where `Py_Initialize` was called instead, which sets initSigs to 1. ([#449][i449])
- Refactored MethodBinder.Bind in preparation to make it extensible (#829)
+- When calling C# from Python, enable passing argument of any type to a parameter of C# type `object` by wrapping it into `PyObject` instance. ([#881][i881])
- Look for installed Windows 10 sdk's during installation instead of relying on specific versions.
### Fixed
diff --git a/src/embed_tests/Python.EmbeddingTest.csproj b/src/embed_tests/Python.EmbeddingTest.csproj
index faa55fa27..d351709a4 100644
--- a/src/embed_tests/Python.EmbeddingTest.csproj
+++ b/src/embed_tests/Python.EmbeddingTest.csproj
@@ -89,6 +89,7 @@
+
diff --git a/src/embed_tests/TestInstanceWrapping.cs b/src/embed_tests/TestInstanceWrapping.cs
new file mode 100644
index 000000000..ec275d67a
--- /dev/null
+++ b/src/embed_tests/TestInstanceWrapping.cs
@@ -0,0 +1,58 @@
+using System;
+using System.Globalization;
+using NUnit.Framework;
+using Python.Runtime;
+
+namespace Python.EmbeddingTest
+{
+ public class TestInstanceWrapping
+ {
+ [OneTimeSetUp]
+ public void SetUp()
+ {
+ PythonEngine.Initialize();
+ }
+
+ [OneTimeTearDown]
+ public void Dispose()
+ {
+ PythonEngine.Shutdown();
+ }
+
+ // regression test for https://github.com/pythonnet/pythonnet/issues/811
+ [Test]
+ public void OverloadResolution_UnknownToObject()
+ {
+ var overloaded = new Overloaded();
+ using (Py.GIL())
+ {
+ var o = overloaded.ToPython();
+
+ dynamic callWithSelf = PythonEngine.Eval("lambda o: o.ObjOrClass(KeyError())");
+ callWithSelf(o);
+ Assert.AreEqual(Overloaded.Object, overloaded.Value);
+ }
+ }
+
+ class Base {}
+ class Derived: Base { }
+
+ class Overloaded: Derived
+ {
+ public int Value { get; set; }
+ public void IntOrStr(int arg) => this.Value = arg;
+ public void IntOrStr(string arg) =>
+ this.Value = int.Parse(arg, NumberStyles.Integer, CultureInfo.InvariantCulture);
+
+ public const int Object = 1;
+ public const int ConcreteClass = 2;
+ public void ObjOrClass(object _) => this.Value = Object;
+ public void ObjOrClass(Overloaded _) => this.Value = ConcreteClass;
+
+ public const int Base = ConcreteClass + 1;
+ public const int Derived = Base + 1;
+ public void BaseOrDerived(Base _) => this.Value = Base;
+ public void BaseOrDerived(Derived _) => this.Value = Derived;
+ }
+ }
+}
diff --git a/src/runtime/converter.cs b/src/runtime/converter.cs
index 11c67bf82..1883dc32b 100644
--- a/src/runtime/converter.cs
+++ b/src/runtime/converter.cs
@@ -382,12 +382,9 @@ internal static bool ToManagedValue(IntPtr value, Type obType,
return ToArray(value, typeof(object[]), out result, setError);
}
- if (setError)
- {
- Exceptions.SetError(Exceptions.TypeError, "value cannot be converted to Object");
- }
-
- return false;
+ Runtime.XIncref(value); // PyObject() assumes ownership
+ result = new PyObject(value);
+ return true;
}
// Conversion to 'Type' is done using the same mappings as above for objects.
From e4e16a422a466b8ed5524f568859c3aba1f9d2ff Mon Sep 17 00:00:00 2001
From: Benedikt Reinartz
Date: Thu, 20 Jun 2019 17:59:28 +0200
Subject: [PATCH 006/998] Revert "Enable C# parameters of type `object` accept
any argument, passed from Python (#853)" (#882)
This reverts commit 43c972d50fa2526f5f9ad73c591f6170c0538854.
---
CHANGELOG.md | 1 -
src/embed_tests/Python.EmbeddingTest.csproj | 1 -
src/embed_tests/TestInstanceWrapping.cs | 58 ---------------------
src/runtime/converter.cs | 9 ++--
4 files changed, 6 insertions(+), 63 deletions(-)
delete mode 100644 src/embed_tests/TestInstanceWrapping.cs
diff --git a/CHANGELOG.md b/CHANGELOG.md
index d8683622f..b5531bf47 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -41,7 +41,6 @@ This document follows the conventions laid out in [Keep a CHANGELOG][].
- Reattach python exception traceback information (#545)
- PythonEngine.Intialize will now call `Py_InitializeEx` with a default value of 0, so signals will not be configured by default on embedding. This is different from the previous behaviour, where `Py_Initialize` was called instead, which sets initSigs to 1. ([#449][i449])
- Refactored MethodBinder.Bind in preparation to make it extensible (#829)
-- When calling C# from Python, enable passing argument of any type to a parameter of C# type `object` by wrapping it into `PyObject` instance. ([#881][i881])
- Look for installed Windows 10 sdk's during installation instead of relying on specific versions.
### Fixed
diff --git a/src/embed_tests/Python.EmbeddingTest.csproj b/src/embed_tests/Python.EmbeddingTest.csproj
index d351709a4..faa55fa27 100644
--- a/src/embed_tests/Python.EmbeddingTest.csproj
+++ b/src/embed_tests/Python.EmbeddingTest.csproj
@@ -89,7 +89,6 @@
-
diff --git a/src/embed_tests/TestInstanceWrapping.cs b/src/embed_tests/TestInstanceWrapping.cs
deleted file mode 100644
index ec275d67a..000000000
--- a/src/embed_tests/TestInstanceWrapping.cs
+++ /dev/null
@@ -1,58 +0,0 @@
-using System;
-using System.Globalization;
-using NUnit.Framework;
-using Python.Runtime;
-
-namespace Python.EmbeddingTest
-{
- public class TestInstanceWrapping
- {
- [OneTimeSetUp]
- public void SetUp()
- {
- PythonEngine.Initialize();
- }
-
- [OneTimeTearDown]
- public void Dispose()
- {
- PythonEngine.Shutdown();
- }
-
- // regression test for https://github.com/pythonnet/pythonnet/issues/811
- [Test]
- public void OverloadResolution_UnknownToObject()
- {
- var overloaded = new Overloaded();
- using (Py.GIL())
- {
- var o = overloaded.ToPython();
-
- dynamic callWithSelf = PythonEngine.Eval("lambda o: o.ObjOrClass(KeyError())");
- callWithSelf(o);
- Assert.AreEqual(Overloaded.Object, overloaded.Value);
- }
- }
-
- class Base {}
- class Derived: Base { }
-
- class Overloaded: Derived
- {
- public int Value { get; set; }
- public void IntOrStr(int arg) => this.Value = arg;
- public void IntOrStr(string arg) =>
- this.Value = int.Parse(arg, NumberStyles.Integer, CultureInfo.InvariantCulture);
-
- public const int Object = 1;
- public const int ConcreteClass = 2;
- public void ObjOrClass(object _) => this.Value = Object;
- public void ObjOrClass(Overloaded _) => this.Value = ConcreteClass;
-
- public const int Base = ConcreteClass + 1;
- public const int Derived = Base + 1;
- public void BaseOrDerived(Base _) => this.Value = Base;
- public void BaseOrDerived(Derived _) => this.Value = Derived;
- }
- }
-}
diff --git a/src/runtime/converter.cs b/src/runtime/converter.cs
index 1883dc32b..11c67bf82 100644
--- a/src/runtime/converter.cs
+++ b/src/runtime/converter.cs
@@ -382,9 +382,12 @@ internal static bool ToManagedValue(IntPtr value, Type obType,
return ToArray(value, typeof(object[]), out result, setError);
}
- Runtime.XIncref(value); // PyObject() assumes ownership
- result = new PyObject(value);
- return true;
+ if (setError)
+ {
+ Exceptions.SetError(Exceptions.TypeError, "value cannot be converted to Object");
+ }
+
+ return false;
}
// Conversion to 'Type' is done using the same mappings as above for objects.
From 2bc514f6c5454bc9ff2b94709f5cda400c97442e Mon Sep 17 00:00:00 2001
From: Benedikt Reinartz
Date: Thu, 20 Jun 2019 19:29:20 +0200
Subject: [PATCH 007/998] Fix the failing test (#888)
Warn instead of fail on issues with the garbage collector itself.
---
src/embed_tests/TestFinalizer.cs | 24 ++++++++++++++++++------
1 file changed, 18 insertions(+), 6 deletions(-)
diff --git a/src/embed_tests/TestFinalizer.cs b/src/embed_tests/TestFinalizer.cs
index 53838f315..650ee5686 100644
--- a/src/embed_tests/TestFinalizer.cs
+++ b/src/embed_tests/TestFinalizer.cs
@@ -45,7 +45,7 @@ public void CollectBasicObject()
called = true;
};
- Assert.IsFalse(called);
+ Assert.IsFalse(called, "The event handler was called before it was installed");
Finalizer.Instance.CollectOnce += handler;
WeakReference shortWeak;
@@ -55,13 +55,25 @@ public void CollectBasicObject()
}
FullGCCollect();
// The object has been resurrected
- Assert.IsFalse(shortWeak.IsAlive);
- Assert.IsTrue(longWeak.IsAlive);
+ Warn.If(
+ shortWeak.IsAlive,
+ "The referenced object is alive although it should have been collected",
+ shortWeak
+ );
+ Assert.IsTrue(
+ longWeak.IsAlive,
+ "The reference object is not alive although it should still be",
+ longWeak
+ );
{
var garbage = Finalizer.Instance.GetCollectedObjects();
- Assert.NotZero(garbage.Count);
- Assert.IsTrue(garbage.Any(T => ReferenceEquals(T.Target, longWeak.Target)));
+ Assert.NotZero(garbage.Count, "There should still be garbage around");
+ Warn.Unless(
+ garbage.Any(T => ReferenceEquals(T.Target, longWeak.Target)),
+ $"The {nameof(longWeak)} reference doesn't show up in the garbage list",
+ garbage
+ );
}
try
{
@@ -71,7 +83,7 @@ public void CollectBasicObject()
{
Finalizer.Instance.CollectOnce -= handler;
}
- Assert.IsTrue(called);
+ Assert.IsTrue(called, "The event handler was not called during finalization");
Assert.GreaterOrEqual(objectCount, 1);
}
From fc7d8a466885ced4690327081695f708190e998a Mon Sep 17 00:00:00 2001
From: Benedikt Reinartz
Date: Mon, 24 Jun 2019 18:07:55 +0200
Subject: [PATCH 008/998] Support ARM architectures again (#887)
---
src/runtime/runtime.cs | 4 ++++
src/runtime/typemanager.cs | 32 ++++++++++++++++++++++++++++++++
2 files changed, 36 insertions(+)
diff --git a/src/runtime/runtime.cs b/src/runtime/runtime.cs
index 7623200e0..294ecaf48 100644
--- a/src/runtime/runtime.cs
+++ b/src/runtime/runtime.cs
@@ -229,6 +229,8 @@ public enum MachineType
{
i386,
x86_64,
+ armv7l,
+ armv8,
Other
};
@@ -247,6 +249,8 @@ public enum MachineType
["amd64"] = MachineType.x86_64,
["x64"] = MachineType.x86_64,
["em64t"] = MachineType.x86_64,
+ ["armv7l"] = MachineType.armv7l,
+ ["armv8"] = MachineType.armv8,
};
///
diff --git a/src/runtime/typemanager.cs b/src/runtime/typemanager.cs
index d19c8737f..a260e8dfa 100644
--- a/src/runtime/typemanager.cs
+++ b/src/runtime/typemanager.cs
@@ -510,6 +510,10 @@ public static NativeCode Active
return I386;
case Runtime.MachineType.x86_64:
return X86_64;
+ case Runtime.MachineType.armv7l:
+ return Armv7l;
+ case Runtime.MachineType.armv8:
+ return Armv8;
default:
throw new NotImplementedException($"No support for {Runtime.MachineName}");
}
@@ -546,6 +550,34 @@ public static NativeCode Active
///
///
public static readonly NativeCode I386 = X86_64;
+
+ public static readonly NativeCode Armv7l = new NativeCode()
+ {
+ Return0 = 0,
+ Return1 = 0x08,
+ Code = new byte[]
+ {
+ 0xe3, 0xa0, 0x00, 0x00, // mov r0, #0
+ 0xe1, 0x2f, 0xff, 0x1e, // bx lr
+
+ 0xe3, 0xa0, 0x00, 0x01, // mov r0, #1
+ 0xe1, 0x2f, 0xff, 0x1e, // bx lr
+ }
+ };
+
+ public static readonly NativeCode Armv8 = new NativeCode()
+ {
+ Return0 = 0,
+ Return1 = 0x08,
+ Code = new byte[]
+ {
+ 0x52, 0x80, 0x00, 0x00, // mov w0, #0x0
+ 0xd6, 0x5f, 0x03, 0xc0, // ret
+
+ 0x52, 0x80, 0x00, 0x20, // mov w0, #0x1
+ 0xd6, 0x5f, 0x03, 0xc0, // ret
+ }
+ };
}
///
From 1dd2ee1b02449b85eeee6120c88a4092dc78851a Mon Sep 17 00:00:00 2001
From: Benedikt Reinartz
Date: Tue, 25 Jun 2019 23:00:34 +0200
Subject: [PATCH 009/998] Get the correct library loading functions at runtime
---
src/embed_tests/TestRuntime.cs | 7 +-
src/runtime/Python.Runtime.csproj | 2 +
src/runtime/platform/LibraryLoader.cs | 153 ++++++++++++++++++++++++++
src/runtime/platform/Types.cs | 22 ++++
src/runtime/runtime.cs | 128 ++-------------------
src/runtime/typemanager.cs | 22 ++--
6 files changed, 203 insertions(+), 131 deletions(-)
create mode 100644 src/runtime/platform/LibraryLoader.cs
create mode 100644 src/runtime/platform/Types.cs
diff --git a/src/embed_tests/TestRuntime.cs b/src/embed_tests/TestRuntime.cs
index ac1fa1ac0..25b70fac5 100644
--- a/src/embed_tests/TestRuntime.cs
+++ b/src/embed_tests/TestRuntime.cs
@@ -1,6 +1,7 @@
using System;
using NUnit.Framework;
using Python.Runtime;
+using Python.Runtime.Platform;
namespace Python.EmbeddingTest
{
@@ -26,10 +27,10 @@ public static void PlatformCache()
{
Runtime.Runtime.Initialize();
- Assert.That(Runtime.Runtime.Machine, Is.Not.EqualTo(Runtime.Runtime.MachineType.Other));
+ Assert.That(Runtime.Runtime.Machine, Is.Not.EqualTo(MachineType.Other));
Assert.That(!string.IsNullOrEmpty(Runtime.Runtime.MachineName));
- Assert.That(Runtime.Runtime.OperatingSystem, Is.Not.EqualTo(Runtime.Runtime.OperatingSystemType.Other));
+ Assert.That(Runtime.Runtime.OperatingSystem, Is.Not.EqualTo(OperatingSystemType.Other));
Assert.That(!string.IsNullOrEmpty(Runtime.Runtime.OperatingSystemName));
// Don't shut down the runtime: if the python engine was initialized
@@ -39,7 +40,7 @@ public static void PlatformCache()
[Test]
public static void Py_IsInitializedValue()
{
- Runtime.Runtime.Py_Finalize();
+ Runtime.Runtime.Py_Finalize();
Assert.AreEqual(0, Runtime.Runtime.Py_IsInitialized());
Runtime.Runtime.Py_Initialize();
Assert.AreEqual(1, Runtime.Runtime.Py_IsInitialized());
diff --git a/src/runtime/Python.Runtime.csproj b/src/runtime/Python.Runtime.csproj
index 19f776c77..c4d63695f 100644
--- a/src/runtime/Python.Runtime.csproj
+++ b/src/runtime/Python.Runtime.csproj
@@ -140,6 +140,8 @@
+
+
diff --git a/src/runtime/platform/LibraryLoader.cs b/src/runtime/platform/LibraryLoader.cs
new file mode 100644
index 000000000..c0157b04b
--- /dev/null
+++ b/src/runtime/platform/LibraryLoader.cs
@@ -0,0 +1,153 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace Python.Runtime.Platform
+{
+ interface ILibraryLoader
+ {
+ IntPtr Load(string dllToLoad);
+
+ IntPtr GetFunction(IntPtr hModule, string procedureName);
+
+ bool Free(IntPtr hModule);
+ }
+
+ static class LibraryLoader
+ {
+ public static ILibraryLoader Get(OperatingSystemType os)
+ {
+ switch (os)
+ {
+ case OperatingSystemType.Windows:
+ return new WindowsLoader();
+ case OperatingSystemType.Darwin:
+ return new DarwinLoader();
+ case OperatingSystemType.Linux:
+ return new LinuxLoader();
+ default:
+ throw new Exception($"This operating system ({os}) is not supported");
+ }
+ }
+ }
+
+ class LinuxLoader : ILibraryLoader
+ {
+ private static int RTLD_NOW = 0x2;
+ private static int RTLD_GLOBAL = 0x100;
+ private static IntPtr RTLD_DEFAULT = IntPtr.Zero;
+ private const string NativeDll = "libdl.so";
+
+ public IntPtr Load(string fileName)
+ {
+ return dlopen($"lib{fileName}.so", RTLD_NOW | RTLD_GLOBAL);
+ }
+
+ public bool Free(IntPtr handle)
+ {
+ dlclose(handle);
+ return true;
+ }
+
+ public IntPtr GetFunction(IntPtr dllHandle, string name)
+ {
+ // look in the exe if dllHandle is NULL
+ if (dllHandle == IntPtr.Zero)
+ {
+ dllHandle = RTLD_DEFAULT;
+ }
+
+ // clear previous errors if any
+ dlerror();
+ IntPtr res = dlsym(dllHandle, name);
+ IntPtr errPtr = dlerror();
+ if (errPtr != IntPtr.Zero)
+ {
+ throw new Exception("dlsym: " + Marshal.PtrToStringAnsi(errPtr));
+ }
+ return res;
+ }
+
+ [DllImport(NativeDll, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
+ public static extern IntPtr dlopen(String fileName, int flags);
+
+ [DllImport(NativeDll, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
+ private static extern IntPtr dlsym(IntPtr handle, String symbol);
+
+ [DllImport(NativeDll, CallingConvention = CallingConvention.Cdecl)]
+ private static extern int dlclose(IntPtr handle);
+
+ [DllImport(NativeDll, CallingConvention = CallingConvention.Cdecl)]
+ private static extern IntPtr dlerror();
+ }
+
+ class DarwinLoader : ILibraryLoader
+ {
+ private static int RTLD_NOW = 0x2;
+ private static int RTLD_GLOBAL = 0x8;
+ private const string NativeDll = "/usr/lib/libSystem.dylib";
+ private static IntPtr RTLD_DEFAULT = new IntPtr(-2);
+
+ public IntPtr Load(string fileName)
+ {
+ return dlopen($"lib{fileName}.dylib", RTLD_NOW | RTLD_GLOBAL);
+ }
+
+ public bool Free(IntPtr handle)
+ {
+ dlclose(handle);
+ return true;
+ }
+
+ public IntPtr GetFunction(IntPtr dllHandle, string name)
+ {
+ // look in the exe if dllHandle is NULL
+ if (dllHandle == IntPtr.Zero)
+ {
+ dllHandle = RTLD_DEFAULT;
+ }
+
+ // clear previous errors if any
+ dlerror();
+ IntPtr res = dlsym(dllHandle, name);
+ IntPtr errPtr = dlerror();
+ if (errPtr != IntPtr.Zero)
+ {
+ throw new Exception("dlsym: " + Marshal.PtrToStringAnsi(errPtr));
+ }
+ return res;
+ }
+
+ [DllImport(NativeDll, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
+ public static extern IntPtr dlopen(String fileName, int flags);
+
+ [DllImport(NativeDll, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
+ private static extern IntPtr dlsym(IntPtr handle, String symbol);
+
+ [DllImport(NativeDll, CallingConvention = CallingConvention.Cdecl)]
+ private static extern int dlclose(IntPtr handle);
+
+ [DllImport(NativeDll, CallingConvention = CallingConvention.Cdecl)]
+ private static extern IntPtr dlerror();
+ }
+
+ class WindowsLoader : ILibraryLoader
+ {
+ private const string NativeDll = "kernel32.dll";
+
+ [DllImport(NativeDll)]
+ static extern IntPtr LoadLibrary(string dllToLoad);
+
+ public IntPtr Load(string dllToLoad) => WindowsLoader.LoadLibrary(dllToLoad);
+
+ [DllImport(NativeDll)]
+ static extern IntPtr GetProcAddress(IntPtr hModule, string procedureName);
+
+ public IntPtr GetFunction(IntPtr hModule, string procedureName) => WindowsLoader.GetProcAddress(hModule, procedureName);
+
+
+ [DllImport(NativeDll)]
+ static extern bool FreeLibrary(IntPtr hModule);
+
+ public bool Free(IntPtr hModule) => WindowsLoader.FreeLibrary(hModule);
+ }
+}
diff --git a/src/runtime/platform/Types.cs b/src/runtime/platform/Types.cs
new file mode 100644
index 000000000..bdc51af39
--- /dev/null
+++ b/src/runtime/platform/Types.cs
@@ -0,0 +1,22 @@
+namespace Python.Runtime.Platform
+{
+ public enum MachineType
+ {
+ i386,
+ x86_64,
+ armv7l,
+ armv8,
+ Other
+ };
+
+ ///
+ /// Operating system type as reported by Python.
+ ///
+ public enum OperatingSystemType
+ {
+ Windows,
+ Darwin,
+ Linux,
+ Other
+ }
+}
diff --git a/src/runtime/runtime.cs b/src/runtime/runtime.cs
index 294ecaf48..ec5bddfd0 100644
--- a/src/runtime/runtime.cs
+++ b/src/runtime/runtime.cs
@@ -7,96 +7,7 @@
namespace Python.Runtime
{
- [SuppressUnmanagedCodeSecurity]
- internal static class NativeMethods
- {
-#if MONO_LINUX || MONO_OSX
-#if NETSTANDARD
- private static int RTLD_NOW = 0x2;
-#if MONO_LINUX
- private static int RTLD_GLOBAL = 0x100;
- private static IntPtr RTLD_DEFAULT = IntPtr.Zero;
- private const string NativeDll = "libdl.so";
- public static IntPtr LoadLibrary(string fileName)
- {
- return dlopen($"lib{fileName}.so", RTLD_NOW | RTLD_GLOBAL);
- }
-#elif MONO_OSX
- private static int RTLD_GLOBAL = 0x8;
- private const string NativeDll = "/usr/lib/libSystem.dylib";
- private static IntPtr RTLD_DEFAULT = new IntPtr(-2);
-
- public static IntPtr LoadLibrary(string fileName)
- {
- return dlopen($"lib{fileName}.dylib", RTLD_NOW | RTLD_GLOBAL);
- }
-#endif
-#else
- private static int RTLD_NOW = 0x2;
- private static int RTLD_SHARED = 0x20;
-#if MONO_OSX
- private static IntPtr RTLD_DEFAULT = new IntPtr(-2);
- private const string NativeDll = "__Internal";
-#elif MONO_LINUX
- private static IntPtr RTLD_DEFAULT = IntPtr.Zero;
- private const string NativeDll = "libdl.so";
-#endif
-
- public static IntPtr LoadLibrary(string fileName)
- {
- return dlopen(fileName, RTLD_NOW | RTLD_SHARED);
- }
-#endif
-
-
- public static void FreeLibrary(IntPtr handle)
- {
- dlclose(handle);
- }
-
- public static IntPtr GetProcAddress(IntPtr dllHandle, string name)
- {
- // look in the exe if dllHandle is NULL
- if (dllHandle == IntPtr.Zero)
- {
- dllHandle = RTLD_DEFAULT;
- }
-
- // clear previous errors if any
- dlerror();
- IntPtr res = dlsym(dllHandle, name);
- IntPtr errPtr = dlerror();
- if (errPtr != IntPtr.Zero)
- {
- throw new Exception("dlsym: " + Marshal.PtrToStringAnsi(errPtr));
- }
- return res;
- }
-
- [DllImport(NativeDll, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
- public static extern IntPtr dlopen(String fileName, int flags);
-
- [DllImport(NativeDll, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
- private static extern IntPtr dlsym(IntPtr handle, String symbol);
-
- [DllImport(NativeDll, CallingConvention = CallingConvention.Cdecl)]
- private static extern int dlclose(IntPtr handle);
-
- [DllImport(NativeDll, CallingConvention = CallingConvention.Cdecl)]
- private static extern IntPtr dlerror();
-#else // Windows
- private const string NativeDll = "kernel32.dll";
-
- [DllImport(NativeDll)]
- public static extern IntPtr LoadLibrary(string dllToLoad);
-
- [DllImport(NativeDll)]
- public static extern IntPtr GetProcAddress(IntPtr hModule, string procedureName);
-
- [DllImport(NativeDll)]
- public static extern bool FreeLibrary(IntPtr hModule);
-#endif
- }
+ using Python.Runtime.Platform;
///
/// Encapsulates the low-level Python C API. Note that it is
@@ -197,17 +108,6 @@ public class Runtime
// .NET core: System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
internal static bool IsWindows = Environment.OSVersion.Platform == PlatformID.Win32NT;
- ///
- /// Operating system type as reported by Python.
- ///
- public enum OperatingSystemType
- {
- Windows,
- Darwin,
- Linux,
- Other
- }
-
static readonly Dictionary OperatingSystemTypeMapping = new Dictionary()
{
{ "Windows", OperatingSystemType.Windows },
@@ -225,14 +125,6 @@ public enum OperatingSystemType
///
public static string OperatingSystemName { get; private set; }
- public enum MachineType
- {
- i386,
- x86_64,
- armv7l,
- armv8,
- Other
- };
///
/// Map lower-case version of the python machine name to the processor
@@ -397,24 +289,24 @@ internal static void Initialize(bool initSigs = false)
Error = new IntPtr(-1);
+ // Initialize data about the platform we're running on. We need
+ // this for the type manager and potentially other details. Must
+ // happen after caching the python types, above.
+ InitializePlatformData();
+
IntPtr dllLocal = IntPtr.Zero;
+ var loader = LibraryLoader.Get(OperatingSystem);
if (_PythonDll != "__Internal")
{
- dllLocal = NativeMethods.LoadLibrary(_PythonDll);
+ dllLocal = loader.Load(_PythonDll);
}
- _PyObject_NextNotImplemented = NativeMethods.GetProcAddress(dllLocal, "_PyObject_NextNotImplemented");
+ _PyObject_NextNotImplemented = loader.GetFunction(dllLocal, "_PyObject_NextNotImplemented");
-#if !(MONO_LINUX || MONO_OSX)
if (dllLocal != IntPtr.Zero)
{
- NativeMethods.FreeLibrary(dllLocal);
+ loader.Free(dllLocal);
}
-#endif
- // Initialize data about the platform we're running on. We need
- // this for the type manager and potentially other details. Must
- // happen after caching the python types, above.
- InitializePlatformData();
// Initialize modules that depend on the runtime class.
AssemblyManager.Initialize();
diff --git a/src/runtime/typemanager.cs b/src/runtime/typemanager.cs
index a260e8dfa..00a8f0a89 100644
--- a/src/runtime/typemanager.cs
+++ b/src/runtime/typemanager.cs
@@ -6,6 +6,8 @@
namespace Python.Runtime
{
+ using Python.Runtime.Platform;
+
///
/// The TypeManager class is responsible for building binary-compatible
/// Python type objects that are implemented in managed code.
@@ -504,15 +506,15 @@ public static NativeCode Active
{
get
{
- switch(Runtime.Machine)
+ switch (Runtime.Machine)
{
- case Runtime.MachineType.i386:
+ case MachineType.i386:
return I386;
- case Runtime.MachineType.x86_64:
+ case MachineType.x86_64:
return X86_64;
- case Runtime.MachineType.armv7l:
+ case MachineType.armv7l:
return Armv7l;
- case Runtime.MachineType.armv8:
+ case MachineType.armv8:
return Armv8;
default:
throw new NotImplementedException($"No support for {Runtime.MachineName}");
@@ -635,9 +637,9 @@ int MAP_ANONYMOUS
{
switch (Runtime.OperatingSystem)
{
- case Runtime.OperatingSystemType.Darwin:
+ case OperatingSystemType.Darwin:
return 0x1000;
- case Runtime.OperatingSystemType.Linux:
+ case OperatingSystemType.Linux:
return 0x20;
default:
throw new NotImplementedException($"mmap is not supported on {Runtime.OperatingSystemName}");
@@ -668,10 +670,10 @@ internal static IMemoryMapper CreateMemoryMapper()
{
switch (Runtime.OperatingSystem)
{
- case Runtime.OperatingSystemType.Darwin:
- case Runtime.OperatingSystemType.Linux:
+ case OperatingSystemType.Darwin:
+ case OperatingSystemType.Linux:
return new UnixMemoryMapper();
- case Runtime.OperatingSystemType.Windows:
+ case OperatingSystemType.Windows:
return new WindowsMemoryMapper();
default:
throw new NotImplementedException($"No support for {Runtime.OperatingSystemName}");
From 537ee5fae147c78d0221133f4db4de3371ebc319 Mon Sep 17 00:00:00 2001
From: Benedikt Reinartz
Date: Wed, 26 Jun 2019 08:04:00 +0200
Subject: [PATCH 010/998] Implement error handling, move using statements
---
src/runtime/platform/LibraryLoader.cs | 115 +++++++++++++++++++-------
src/runtime/runtime.cs | 2 +-
src/runtime/typemanager.cs | 2 +-
3 files changed, 87 insertions(+), 32 deletions(-)
diff --git a/src/runtime/platform/LibraryLoader.cs b/src/runtime/platform/LibraryLoader.cs
index c0157b04b..a6d88cd19 100644
--- a/src/runtime/platform/LibraryLoader.cs
+++ b/src/runtime/platform/LibraryLoader.cs
@@ -1,4 +1,5 @@
using System;
+using System.ComponentModel;
using System.Runtime.InteropServices;
namespace Python.Runtime.Platform
@@ -9,7 +10,7 @@ interface ILibraryLoader
IntPtr GetFunction(IntPtr hModule, string procedureName);
- bool Free(IntPtr hModule);
+ void Free(IntPtr hModule);
}
static class LibraryLoader
@@ -25,7 +26,7 @@ public static ILibraryLoader Get(OperatingSystemType os)
case OperatingSystemType.Linux:
return new LinuxLoader();
default:
- throw new Exception($"This operating system ({os}) is not supported");
+ throw new PlatformNotSupportedException($"This operating system ({os}) is not supported");
}
}
}
@@ -37,15 +38,23 @@ class LinuxLoader : ILibraryLoader
private static IntPtr RTLD_DEFAULT = IntPtr.Zero;
private const string NativeDll = "libdl.so";
- public IntPtr Load(string fileName)
+ public IntPtr Load(string dllToLoad)
{
- return dlopen($"lib{fileName}.so", RTLD_NOW | RTLD_GLOBAL);
+ var filename = $"lib{dllToLoad}.so";
+ ClearError();
+ var res = dlopen(filename, RTLD_NOW | RTLD_GLOBAL);
+ if (res == IntPtr.Zero)
+ {
+ var err = GetError();
+ throw new DllNotFoundException($"Could not load {filename} with flags RTLD_NOW | RTLD_GLOBAL: {err}");
+ }
+
+ return res;
}
- public bool Free(IntPtr handle)
+ public void Free(IntPtr handle)
{
dlclose(handle);
- return true;
}
public IntPtr GetFunction(IntPtr dllHandle, string name)
@@ -56,22 +65,35 @@ public IntPtr GetFunction(IntPtr dllHandle, string name)
dllHandle = RTLD_DEFAULT;
}
- // clear previous errors if any
- dlerror();
+ ClearError();
IntPtr res = dlsym(dllHandle, name);
- IntPtr errPtr = dlerror();
- if (errPtr != IntPtr.Zero)
+ if (res == IntPtr.Zero)
{
- throw new Exception("dlsym: " + Marshal.PtrToStringAnsi(errPtr));
+ var err = GetError();
+ throw new MissingMethodException($"Failed to load symbol {name}: {err}");
}
return res;
}
+ void ClearError()
+ {
+ dlerror();
+ }
+
+ string GetError()
+ {
+ var res = dlerror();
+ if (res != IntPtr.Zero)
+ return Marshal.PtrToStringAnsi(res);
+ else
+ return null;
+ }
+
[DllImport(NativeDll, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
- public static extern IntPtr dlopen(String fileName, int flags);
+ public static extern IntPtr dlopen(string fileName, int flags);
[DllImport(NativeDll, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
- private static extern IntPtr dlsym(IntPtr handle, String symbol);
+ private static extern IntPtr dlsym(IntPtr handle, string symbol);
[DllImport(NativeDll, CallingConvention = CallingConvention.Cdecl)]
private static extern int dlclose(IntPtr handle);
@@ -87,15 +109,23 @@ class DarwinLoader : ILibraryLoader
private const string NativeDll = "/usr/lib/libSystem.dylib";
private static IntPtr RTLD_DEFAULT = new IntPtr(-2);
- public IntPtr Load(string fileName)
+ public IntPtr Load(string dllToLoad)
{
- return dlopen($"lib{fileName}.dylib", RTLD_NOW | RTLD_GLOBAL);
+ var filename = $"lib{dllToLoad}.dylib";
+ ClearError();
+ var res = dlopen(filename, RTLD_NOW | RTLD_GLOBAL);
+ if (res == IntPtr.Zero)
+ {
+ var err = GetError();
+ throw new DllNotFoundException($"Could not load {filename} with flags RTLD_NOW | RTLD_GLOBAL: {err}");
+ }
+
+ return res;
}
- public bool Free(IntPtr handle)
+ public void Free(IntPtr handle)
{
dlclose(handle);
- return true;
}
public IntPtr GetFunction(IntPtr dllHandle, string name)
@@ -106,17 +136,30 @@ public IntPtr GetFunction(IntPtr dllHandle, string name)
dllHandle = RTLD_DEFAULT;
}
- // clear previous errors if any
- dlerror();
+ ClearError();
IntPtr res = dlsym(dllHandle, name);
- IntPtr errPtr = dlerror();
- if (errPtr != IntPtr.Zero)
+ if (res == IntPtr.Zero)
{
- throw new Exception("dlsym: " + Marshal.PtrToStringAnsi(errPtr));
+ var err = GetError();
+ throw new MissingMethodException($"Failed to load symbol {name}: {err}");
}
return res;
}
+ void ClearError()
+ {
+ dlerror();
+ }
+
+ string GetError()
+ {
+ var res = dlerror();
+ if (res != IntPtr.Zero)
+ return Marshal.PtrToStringAnsi(res);
+ else
+ return null;
+ }
+
[DllImport(NativeDll, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
public static extern IntPtr dlopen(String fileName, int flags);
@@ -134,20 +177,32 @@ class WindowsLoader : ILibraryLoader
{
private const string NativeDll = "kernel32.dll";
- [DllImport(NativeDll)]
- static extern IntPtr LoadLibrary(string dllToLoad);
- public IntPtr Load(string dllToLoad) => WindowsLoader.LoadLibrary(dllToLoad);
+ public IntPtr Load(string dllToLoad)
+ {
+ var res = WindowsLoader.LoadLibrary(dllToLoad);
+ if (res == IntPtr.Zero)
+ throw new DllNotFoundException($"Could not load {dllToLoad}", new Win32Exception());
+ return res;
+ }
+
+ public IntPtr GetFunction(IntPtr hModule, string procedureName)
+ {
+ var res = WindowsLoader.GetProcAddress(hModule, procedureName);
+ if (res == IntPtr.Zero)
+ throw new MissingMethodException($"Failed to load symbol {procedureName}", new Win32Exception());
+ return res;
+ }
- [DllImport(NativeDll)]
- static extern IntPtr GetProcAddress(IntPtr hModule, string procedureName);
+ public void Free(IntPtr hModule) => WindowsLoader.FreeLibrary(hModule);
- public IntPtr GetFunction(IntPtr hModule, string procedureName) => WindowsLoader.GetProcAddress(hModule, procedureName);
+ [DllImport(NativeDll, SetLastError = true)]
+ static extern IntPtr LoadLibrary(string dllToLoad);
+ [DllImport(NativeDll, SetLastError = true)]
+ static extern IntPtr GetProcAddress(IntPtr hModule, string procedureName);
[DllImport(NativeDll)]
static extern bool FreeLibrary(IntPtr hModule);
-
- public bool Free(IntPtr hModule) => WindowsLoader.FreeLibrary(hModule);
}
}
diff --git a/src/runtime/runtime.cs b/src/runtime/runtime.cs
index ec5bddfd0..a347651d0 100644
--- a/src/runtime/runtime.cs
+++ b/src/runtime/runtime.cs
@@ -4,10 +4,10 @@
using System.Text;
using System.Threading;
using System.Collections.Generic;
+using Python.Runtime.Platform;
namespace Python.Runtime
{
- using Python.Runtime.Platform;
///
/// Encapsulates the low-level Python C API. Note that it is
diff --git a/src/runtime/typemanager.cs b/src/runtime/typemanager.cs
index 00a8f0a89..127e82eaa 100644
--- a/src/runtime/typemanager.cs
+++ b/src/runtime/typemanager.cs
@@ -3,10 +3,10 @@
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.InteropServices;
+using Python.Runtime.Platform;
namespace Python.Runtime
{
- using Python.Runtime.Platform;
///
/// The TypeManager class is responsible for building binary-compatible
From a8a94264164406e7166038d7fa8b03ac40eb8b71 Mon Sep 17 00:00:00 2001
From: Victor
Date: Wed, 26 Jun 2019 10:55:55 -0700
Subject: [PATCH 011/998] Bump C# language version to 7.3 (#896)
* Bump C# language version to 7.3 #860
* Switch to .NET SDK 2.2
* Use xenial image for travis CI
---
.travis.yml | 20 ++++++++++----------
src/runtime/Python.Runtime.15.csproj | 2 +-
src/runtime/Python.Runtime.csproj | 2 +-
3 files changed, 12 insertions(+), 12 deletions(-)
diff --git a/.travis.yml b/.travis.yml
index 1dadbad1d..46f47489d 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,4 +1,4 @@
-dist: trusty
+dist: xenial
sudo: false
language: python
@@ -12,16 +12,16 @@ matrix:
addons: &xplat-addons
apt:
sources:
- - sourceline: deb [arch=amd64] https://packages.microsoft.com/repos/microsoft-ubuntu-trusty-prod trusty main
+ - sourceline: deb [arch=amd64] https://packages.microsoft.com/repos/microsoft-ubuntu-xenial-prod xenial main
key_url: https://packages.microsoft.com/keys/microsoft.asc
- - sourceline: deb http://download.mono-project.com/repo/ubuntu trusty main
+ - sourceline: deb http://download.mono-project.com/repo/ubuntu xenial main
key_url: http://keyserver.ubuntu.com/pks/lookup?op=get&search=0xA6A19B38D3D831EF
packages:
- mono-devel
- ca-certificates-mono
- - dotnet-hostfxr-2.0.0
- - dotnet-runtime-2.0.0
- - dotnet-sdk-2.0.0
+ - dotnet-hostfxr-2.2
+ - dotnet-runtime-2.2
+ - dotnet-sdk-2.2
- python: 3.5
env: *xplat-env
@@ -45,9 +45,9 @@ matrix:
packages:
- mono-devel
- ca-certificates-mono
- - dotnet-hostfxr-2.0.0
- - dotnet-runtime-2.0.0
- - dotnet-sdk-2.0.0
+ - dotnet-hostfxr-2.2
+ - dotnet-runtime-2.2
+ - dotnet-sdk-2.2
# --------------------- Classic builds ------------------------
- python: 2.7
@@ -84,7 +84,7 @@ env:
addons:
apt:
sources:
- - sourceline: deb http://download.mono-project.com/repo/ubuntu trusty main
+ - sourceline: deb http://download.mono-project.com/repo/ubuntu xenial main
key_url: http://keyserver.ubuntu.com/pks/lookup?op=get&search=0xA6A19B38D3D831EF
packages:
- mono-devel
diff --git a/src/runtime/Python.Runtime.15.csproj b/src/runtime/Python.Runtime.15.csproj
index a4d1773f7..122132513 100644
--- a/src/runtime/Python.Runtime.15.csproj
+++ b/src/runtime/Python.Runtime.15.csproj
@@ -30,7 +30,7 @@
..\..\
$(SolutionDir)\bin\
$(PythonBuildDir)\$(TargetFramework)\
- 6
+ 7.3
True
..\pythonnet.snk
$(PYTHONNET_DEFINE_CONSTANTS)
diff --git a/src/runtime/Python.Runtime.csproj b/src/runtime/Python.Runtime.csproj
index c4d63695f..ac6b59150 100644
--- a/src/runtime/Python.Runtime.csproj
+++ b/src/runtime/Python.Runtime.csproj
@@ -15,7 +15,7 @@
..\..\
$(SolutionDir)\bin\
Properties
- 6
+ 7.3
true
false
..\pythonnet.snk
From df0574db552020397728648ee3ec576aa3a1c2a8 Mon Sep 17 00:00:00 2001
From: Victor
Date: Thu, 27 Jun 2019 22:36:30 -0700
Subject: [PATCH 012/998] Improve "No method matches given arguments" message
with more details (#900)
* generate more useful message, when a .NET overload can't be found, that matches Python parameter types
* provide detailed error message, when an overload can't be found when calling C# from Python
Related: #811, #265, #782
---
CHANGELOG.md | 2 ++
src/embed_tests/TestCallbacks.cs | 35 ++++++++++++++++++++++++++++++++
src/runtime/methodbinder.cs | 31 +++++++++++++++++++++++++---
3 files changed, 65 insertions(+), 3 deletions(-)
create mode 100644 src/embed_tests/TestCallbacks.cs
diff --git a/CHANGELOG.md b/CHANGELOG.md
index b5531bf47..e5a990922 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -13,6 +13,8 @@ This document follows the conventions laid out in [Keep a CHANGELOG][].
### Changed
+- Added argument types information to "No method matches given arguments" message
+
### Fixed
## [2.4.0][]
diff --git a/src/embed_tests/TestCallbacks.cs b/src/embed_tests/TestCallbacks.cs
new file mode 100644
index 000000000..220b0a86a
--- /dev/null
+++ b/src/embed_tests/TestCallbacks.cs
@@ -0,0 +1,35 @@
+using System;
+
+using NUnit.Framework;
+using Python.Runtime;
+
+namespace Python.EmbeddingTest {
+ using Runtime = Python.Runtime.Runtime;
+
+ public class TestCallbacks {
+ [OneTimeSetUp]
+ public void SetUp() {
+ PythonEngine.Initialize();
+ }
+
+ [OneTimeTearDown]
+ public void Dispose() {
+ PythonEngine.Shutdown();
+ }
+
+ [Test]
+ 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()));
+ Assert.AreEqual("TypeError", error.PythonTypeName);
+ string expectedArgTypes = Runtime.IsPython2
+ ? "()"
+ : "()";
+ StringAssert.EndsWith(expectedArgTypes, error.Message);
+ }
+ }
+ }
+}
diff --git a/src/runtime/methodbinder.cs b/src/runtime/methodbinder.cs
index 7471d5d7c..95b953555 100644
--- a/src/runtime/methodbinder.cs
+++ b/src/runtime/methodbinder.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections;
using System.Reflection;
+using System.Text;
namespace Python.Runtime
{
@@ -555,12 +556,36 @@ internal virtual IntPtr Invoke(IntPtr inst, IntPtr args, IntPtr kw, MethodBase i
if (binding == null)
{
- var value = "No method matches given arguments";
+ var value = new StringBuilder("No method matches given arguments");
if (methodinfo != null && methodinfo.Length > 0)
{
- value += $" for {methodinfo[0].Name}";
+ value.Append($" for {methodinfo[0].Name}");
}
- Exceptions.SetError(Exceptions.TypeError, value);
+
+ long argCount = Runtime.PyTuple_Size(args);
+ value.Append(": (");
+ for(long argIndex = 0; argIndex < argCount; argIndex++) {
+ var arg = Runtime.PyTuple_GetItem(args, argIndex);
+ if (arg != IntPtr.Zero) {
+ var type = Runtime.PyObject_Type(arg);
+ if (type != IntPtr.Zero) {
+ try {
+ var description = Runtime.PyObject_Unicode(type);
+ if (description != IntPtr.Zero) {
+ value.Append(Runtime.GetManagedString(description));
+ Runtime.XDecref(description);
+ }
+ } finally {
+ Runtime.XDecref(type);
+ }
+ }
+ }
+
+ if (argIndex + 1 < argCount)
+ value.Append(", ");
+ }
+ value.Append(')');
+ Exceptions.SetError(Exceptions.TypeError, value.ToString());
return IntPtr.Zero;
}
From 93968d25728b7282937067ebb966202da8c21a69 Mon Sep 17 00:00:00 2001
From: chrisjbremner
Date: Wed, 3 Jul 2019 14:36:44 -0700
Subject: [PATCH 013/998] Safe wheel import (#905)
Only allow wheel commands if wheel is installed
---
AUTHORS.md | 1 +
CHANGELOG.md | 1 +
setup.py | 47 +++++++++++++++++++++++++++--------------------
3 files changed, 29 insertions(+), 20 deletions(-)
diff --git a/AUTHORS.md b/AUTHORS.md
index ba954b47d..a45cf6d78 100644
--- a/AUTHORS.md
+++ b/AUTHORS.md
@@ -19,6 +19,7 @@
- Callum Noble ([@callumnoble](https://github.com/callumnoble))
- Christian Heimes ([@tiran](https://github.com/tiran))
- Christoph Gohlke ([@cgohlke](https://github.com/cgohlke))
+- Christopher Bremner ([@chrisjbremner](https://github.com/chrisjbremner))
- Christopher Pow ([@christopherpow](https://github.com/christopherpow))
- Daniel Fernandez ([@fdanny](https://github.com/fdanny))
- Daniel Santana ([@dgsantana](https://github.com/dgsantana))
diff --git a/CHANGELOG.md b/CHANGELOG.md
index e5a990922..ada979147 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -14,6 +14,7 @@ This document follows the conventions laid out in [Keep a CHANGELOG][].
### Changed
- Added argument types information to "No method matches given arguments" message
+- Moved wheel import in setup.py inside of a try/except to prevent pip collection failures
### Fixed
diff --git a/setup.py b/setup.py
index 53c7f3f67..c6e4007e6 100644
--- a/setup.py
+++ b/setup.py
@@ -15,10 +15,14 @@
import sysconfig
from distutils import spawn
from distutils.command import install, build, build_ext, install_data, install_lib
-from wheel import bdist_wheel
from setuptools import Extension, setup
+try:
+ from wheel import bdist_wheel
+except ImportError:
+ bdist_wheel = None
+
# Allow config/verbosity to be set from cli
# http://stackoverflow.com/a/4792601/5208670
CONFIG = "Release" # Release or Debug
@@ -594,21 +598,21 @@ def run(self):
_update_xlat_devtools()
return install.install.run(self)
+if bdist_wheel:
+ class BDistWheelPythonnet(bdist_wheel.bdist_wheel):
+ user_options = bdist_wheel.bdist_wheel.user_options + [("xplat", None, None)]
-class BDistWheelPythonnet(bdist_wheel.bdist_wheel):
- user_options = bdist_wheel.bdist_wheel.user_options + [("xplat", None, None)]
+ def initialize_options(self):
+ bdist_wheel.bdist_wheel.initialize_options(self)
+ self.xplat = None
- def initialize_options(self):
- bdist_wheel.bdist_wheel.initialize_options(self)
- self.xplat = None
+ def finalize_options(self):
+ bdist_wheel.bdist_wheel.finalize_options(self)
- def finalize_options(self):
- bdist_wheel.bdist_wheel.finalize_options(self)
-
- def run(self):
- if self.xplat:
- _update_xlat_devtools()
- return bdist_wheel.bdist_wheel.run(self)
+ def run(self):
+ if self.xplat:
+ _update_xlat_devtools()
+ return bdist_wheel.bdist_wheel.run(self)
###############################################################################
@@ -621,6 +625,15 @@ def run(self):
if not os.path.exists(_get_interop_filename()):
setup_requires.append("pycparser")
+cmdclass={
+ "install": InstallPythonnet,
+ "build_ext": BuildExtPythonnet,
+ "install_lib": InstallLibPythonnet,
+ "install_data": InstallDataPythonnet,
+}
+if bdist_wheel:
+ cmdclass["bdist_wheel"] = BDistWheelPythonnet
+
setup(
name="pythonnet",
version="2.4.1-dev",
@@ -633,13 +646,7 @@ def run(self):
long_description=_get_long_description(),
ext_modules=[Extension("clr", sources=list(_get_source_files()))],
data_files=[("{install_platlib}", ["{build_lib}/Python.Runtime.dll"])],
- cmdclass={
- "install": InstallPythonnet,
- "build_ext": BuildExtPythonnet,
- "install_lib": InstallLibPythonnet,
- "install_data": InstallDataPythonnet,
- "bdist_wheel": BDistWheelPythonnet,
- },
+ cmdclass=cmdclass,
classifiers=[
"Development Status :: 5 - Production/Stable",
"Intended Audience :: Developers",
From 6f635a42a97400bf5e284c4d821a75708393ac70 Mon Sep 17 00:00:00 2001
From: Benedikt Reinartz
Date: Thu, 1 Aug 2019 17:04:07 +0200
Subject: [PATCH 014/998] Simplify travis config and pin mono to 5.20 (#927)
---
.travis.yml | 94 +++++++++--------------------------------------------
1 file changed, 16 insertions(+), 78 deletions(-)
diff --git a/.travis.yml b/.travis.yml
index 46f47489d..9689c0422 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,80 +1,17 @@
dist: xenial
sudo: false
language: python
-
-matrix:
- include:
-# --------------------- XPLAT builds ------------------------
- - python: 2.7
- env: &xplat-env
- - BUILD_OPTS=--xplat
- - NUNIT_PATH=~/.nuget/packages/nunit.consolerunner/3.*/tools/nunit3-console.exe
- addons: &xplat-addons
- apt:
- sources:
- - sourceline: deb [arch=amd64] https://packages.microsoft.com/repos/microsoft-ubuntu-xenial-prod xenial main
- key_url: https://packages.microsoft.com/keys/microsoft.asc
- - sourceline: deb http://download.mono-project.com/repo/ubuntu xenial main
- key_url: http://keyserver.ubuntu.com/pks/lookup?op=get&search=0xA6A19B38D3D831EF
- packages:
- - mono-devel
- - ca-certificates-mono
- - dotnet-hostfxr-2.2
- - dotnet-runtime-2.2
- - dotnet-sdk-2.2
-
- - python: 3.5
- env: *xplat-env
- addons: *xplat-addons
-
- - python: 3.6
- env: *xplat-env
- addons: *xplat-addons
-
- - python: 3.7
- env: *xplat-env
- dist: xenial
- sudo: true
- addons: &xplat-addons-xenial
- apt:
- sources:
- - sourceline: deb [arch=amd64] https://packages.microsoft.com/repos/microsoft-ubuntu-xenial-prod xenial main
- key_url: https://packages.microsoft.com/keys/microsoft.asc
- - sourceline: deb https://download.mono-project.com/repo/ubuntu stable-xenial main
- key_url: http://keyserver.ubuntu.com/pks/lookup?op=get&search=0xA6A19B38D3D831EF
- packages:
- - mono-devel
- - ca-certificates-mono
- - dotnet-hostfxr-2.2
- - dotnet-runtime-2.2
- - dotnet-sdk-2.2
-
-# --------------------- Classic builds ------------------------
- - python: 2.7
- env: &classic-env
- - BUILD_OPTS=
- - NUNIT_PATH=./packages/NUnit.*/tools/nunit3-console.exe
-
- - python: 3.5
- env: *classic-env
-
- - python: 3.6
- env: *classic-env
-
- - python: 3.7
- env: *classic-env
- dist: xenial
- sudo: true
- addons:
- apt:
- sources:
- - sourceline: deb http://download.mono-project.com/repo/ubuntu xenial main
- key_url: http://keyserver.ubuntu.com/pks/lookup?op=get&search=0xA6A19B38D3D831EF
- packages:
- - mono-devel
- - ca-certificates-mono
+python:
+ - 2.7
+ - 3.5
+ - 3.6
+ - 3.7
env:
+ matrix:
+ - BUILD_OPTS=--xplat NUNIT_PATH="~/.nuget/packages/nunit.consolerunner/3.*/tools/nunit3-console.exe" RUN_TESTS=dotnet EMBED_TESTS_PATH=netcoreapp2.0_publish/
+ - BUILD_OPTS="" NUNIT_PATH="./packages/NUnit.*/tools/nunit3-console.exe" RUN_TESTS="mono $NUNIT_PATH" EMBED_TESTS_PATH=""
+
global:
- LD_PRELOAD=/lib/x86_64-linux-gnu/libSegFault.so
- SEGFAULT_SIGNALS=all
@@ -84,11 +21,16 @@ env:
addons:
apt:
sources:
- - sourceline: deb http://download.mono-project.com/repo/ubuntu xenial main
+ - sourceline: deb [arch=amd64] https://packages.microsoft.com/repos/microsoft-ubuntu-xenial-prod xenial main
+ key_url: https://packages.microsoft.com/keys/microsoft.asc
+ - sourceline: deb http://download.mono-project.com/repo/ubuntu stable-xenial/snapshots/5.20 main
key_url: http://keyserver.ubuntu.com/pks/lookup?op=get&search=0xA6A19B38D3D831EF
packages:
- mono-devel
- ca-certificates-mono
+ - dotnet-hostfxr-2.2
+ - dotnet-runtime-2.2
+ - dotnet-sdk-2.2
before_install:
# Set-up dll path for embedded tests
@@ -102,13 +44,9 @@ install:
script:
- python -m pytest
- - mono $NUNIT_PATH src/embed_tests/bin/Python.EmbeddingTest.dll
- - if [[ $BUILD_OPTS == --xplat ]]; then dotnet src/embed_tests/bin/netcoreapp2.0_publish/Python.EmbeddingTest.dll; fi
+ - $RUN_TESTS src/embed_tests/bin/$EMBED_TESTS_PATH/Python.EmbeddingTest.dll
after_script:
- # Uncomment if need to geninterop, ie. py37 final
- # - python tools/geninterop/geninterop.py
-
# Waiting on mono-coverage, SharpCover or xr.Baboon
- coverage xml -i
- codecov --file coverage.xml --flags setup_linux
From 1ce630e5b0c2fce208267591d6502a5d4d0e2a0a Mon Sep 17 00:00:00 2001
From: Ivan Cronyn
Date: Fri, 2 Aug 2019 11:19:10 +0100
Subject: [PATCH 015/998] Removes imports deprecated in Python3 (#925)
---
CHANGELOG.md | 1 +
src/runtime/runtime.cs | 6 ++----
2 files changed, 3 insertions(+), 4 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index ada979147..b0d525b36 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -15,6 +15,7 @@ This document follows the conventions laid out in [Keep a CHANGELOG][].
- Added argument types information to "No method matches given arguments" message
- Moved wheel import in setup.py inside of a try/except to prevent pip collection failures
+- Removes PyLong_GetMax and PyClass_New when targetting Python3
### Fixed
diff --git a/src/runtime/runtime.cs b/src/runtime/runtime.cs
index a347651d0..a6bfca431 100644
--- a/src/runtime/runtime.cs
+++ b/src/runtime/runtime.cs
@@ -769,8 +769,10 @@ public static extern int Py_Main(
[DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)]
internal static extern IntPtr PyCFunction_Call(IntPtr func, IntPtr args, IntPtr kw);
+#if PYTHON2
[DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)]
internal static extern IntPtr PyClass_New(IntPtr bases, IntPtr dict, IntPtr name);
+#endif
[DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)]
internal static extern IntPtr PyInstance_New(IntPtr cls, IntPtr args, IntPtr kw);
@@ -1012,10 +1014,6 @@ internal static IntPtr PyInt_FromInt64(long value)
[DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl,
EntryPoint = "PyLong_FromString")]
internal static extern IntPtr PyInt_FromString(string value, IntPtr end, int radix);
-
- [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl,
- EntryPoint = "PyLong_GetMax")]
- internal static extern int PyInt_GetMax();
#elif PYTHON2
[DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr PyInt_FromLong(IntPtr value);
From f20bcf6498f336b684b8b5d515aaee5e3ebb24f4 Mon Sep 17 00:00:00 2001
From: ftreurni
Date: Fri, 2 Aug 2019 12:21:48 +0200
Subject: [PATCH 016/998] Fix so that Runtime.PyModuleType is retrieved via
Python.dll (#904) (#929)
---
AUTHORS.md | 1 +
CHANGELOG.md | 5 ++++-
src/runtime/runtime.cs | 2 +-
3 files changed, 6 insertions(+), 2 deletions(-)
diff --git a/AUTHORS.md b/AUTHORS.md
index a45cf6d78..6c2817aeb 100644
--- a/AUTHORS.md
+++ b/AUTHORS.md
@@ -27,6 +27,7 @@
- David Lassonde ([@lassond](https://github.com/lassond))
- David Lechner ([@dlech](https://github.com/dlech))
- Dmitriy Se ([@dmitriyse](https://github.com/dmitriyse))
+- Florian Treurniet ([@ftreurni](https://github.com/ftreurni))
- He-chien Tsai ([@t3476](https://github.com/t3476))
- Inna Wiesel ([@inna-w](https://github.com/inna-w))
- Ivan Cronyn ([@cronan](https://github.com/cronan))
diff --git a/CHANGELOG.md b/CHANGELOG.md
index b0d525b36..705b33b7d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -19,6 +19,9 @@ This document follows the conventions laid out in [Keep a CHANGELOG][].
### Fixed
+- Fixed runtime that fails loading when using pythonnet in an environment
+ together with Nuitka
+
## [2.4.0][]
### Added
@@ -61,7 +64,7 @@ This document follows the conventions laid out in [Keep a CHANGELOG][].
- Fixed conversion of 'float' and 'double' values ([#486][i486])
- Fixed 'clrmethod' for python 2 ([#492][i492])
- Fixed double calling of constructor when deriving from .NET class ([#495][i495])
-- Fixed `clr.GetClrType` when iterating over `System` members ([#607][p607])
+- Fixed `clr.GetClrType` when iterating over `System` members ([#607][p607])
- Fixed `LockRecursionException` when loading assemblies ([#627][i627])
- Fixed errors breaking .NET Remoting on method invoke ([#276][i276])
- Fixed PyObject.GetHashCode ([#676][i676])
diff --git a/src/runtime/runtime.cs b/src/runtime/runtime.cs
index a6bfca431..75f11492f 100644
--- a/src/runtime/runtime.cs
+++ b/src/runtime/runtime.cs
@@ -205,7 +205,6 @@ internal static void Initialize(bool initSigs = false)
PyNotImplemented = PyObject_GetAttrString(op, "NotImplemented");
PyBaseObjectType = PyObject_GetAttrString(op, "object");
- PyModuleType = PyObject_Type(op);
PyNone = PyObject_GetAttrString(op, "None");
PyTrue = PyObject_GetAttrString(op, "True");
PyFalse = PyObject_GetAttrString(op, "False");
@@ -302,6 +301,7 @@ internal static void Initialize(bool initSigs = false)
dllLocal = loader.Load(_PythonDll);
}
_PyObject_NextNotImplemented = loader.GetFunction(dllLocal, "_PyObject_NextNotImplemented");
+ PyModuleType = loader.GetFunction(dllLocal, "PyModule_Type");
if (dllLocal != IntPtr.Zero)
{
From c97a380bd38c28a055b7228028f01f5e8d1e5d8f Mon Sep 17 00:00:00 2001
From: Benedikt Reinartz
Date: Fri, 2 Aug 2019 14:10:09 +0200
Subject: [PATCH 017/998] Readd .NET implementations of GC slots again (#913)
Since I failed to provide properly working implementations of Return0 and Return1 for ARM, I'll just add the old behaviour back for all platforms but x86 and amd64.
---
src/runtime/typemanager.cs | 75 ++++++++++++++++----------------------
1 file changed, 31 insertions(+), 44 deletions(-)
diff --git a/src/runtime/typemanager.cs b/src/runtime/typemanager.cs
index 127e82eaa..9a98e9ebb 100644
--- a/src/runtime/typemanager.cs
+++ b/src/runtime/typemanager.cs
@@ -512,12 +512,8 @@ public static NativeCode Active
return I386;
case MachineType.x86_64:
return X86_64;
- case MachineType.armv7l:
- return Armv7l;
- case MachineType.armv8:
- return Armv8;
default:
- throw new NotImplementedException($"No support for {Runtime.MachineName}");
+ return null;
}
}
}
@@ -552,34 +548,6 @@ public static NativeCode Active
///
///
public static readonly NativeCode I386 = X86_64;
-
- public static readonly NativeCode Armv7l = new NativeCode()
- {
- Return0 = 0,
- Return1 = 0x08,
- Code = new byte[]
- {
- 0xe3, 0xa0, 0x00, 0x00, // mov r0, #0
- 0xe1, 0x2f, 0xff, 0x1e, // bx lr
-
- 0xe3, 0xa0, 0x00, 0x01, // mov r0, #1
- 0xe1, 0x2f, 0xff, 0x1e, // bx lr
- }
- };
-
- public static readonly NativeCode Armv8 = new NativeCode()
- {
- Return0 = 0,
- Return1 = 0x08,
- Code = new byte[]
- {
- 0x52, 0x80, 0x00, 0x00, // mov w0, #0x0
- 0xd6, 0x5f, 0x03, 0xc0, // ret
-
- 0x52, 0x80, 0x00, 0x20, // mov w0, #0x1
- 0xd6, 0x5f, 0x03, 0xc0, // ret
- }
- };
}
///
@@ -702,7 +670,7 @@ internal static void InitializeNativeCodePage()
Marshal.Copy(NativeCode.Active.Code, 0, NativeCodePage, codeLength);
mapper.SetReadExec(NativeCodePage, codeLength);
}
-#endregion
+ #endregion
///
/// Given a newly allocated Python type object and a managed Type that
@@ -745,21 +713,40 @@ internal static void InitializeSlots(IntPtr type, Type impl)
impl = impl.BaseType;
}
- // See the TestDomainReload test: there was a crash related to
- // the gc-related slots. They always return 0 or 1 because we don't
- // really support gc:
+ var native = NativeCode.Active;
+
+ // The garbage collection related slots always have to return 1 or 0
+ // since .NET objects don't take part in Python's gc:
// tp_traverse (returns 0)
// tp_clear (returns 0)
// tp_is_gc (returns 1)
- // We can't do without: python really wants those slots to exist.
- // We can't implement those in C# because the application domain
- // can be shut down and the memory released.
- InitializeNativeCodePage();
- InitializeSlot(type, NativeCodePage + NativeCode.Active.Return0, "tp_traverse");
- InitializeSlot(type, NativeCodePage + NativeCode.Active.Return0, "tp_clear");
- InitializeSlot(type, NativeCodePage + NativeCode.Active.Return1, "tp_is_gc");
+ // These have to be defined, though, so by default we fill these with
+ // static C# functions from this class.
+
+ var ret0 = Interop.GetThunk(((Func)Return0).Method);
+ var ret1 = Interop.GetThunk(((Func)Return1).Method);
+
+ if (native != null)
+ {
+ // If we want to support domain reload, the C# implementation
+ // cannot be used as the assembly may get released before
+ // CPython calls these functions. Instead, for amd64 and x86 we
+ // load them into a separate code page that is leaked
+ // intentionally.
+ InitializeNativeCodePage();
+ ret1 = NativeCodePage + native.Return1;
+ ret0 = NativeCodePage + native.Return0;
+ }
+
+ InitializeSlot(type, ret0, "tp_traverse");
+ InitializeSlot(type, ret0, "tp_clear");
+ InitializeSlot(type, ret1, "tp_is_gc");
}
+ static int Return1(IntPtr _) => 1;
+
+ static int Return0(IntPtr _) => 0;
+
///
/// Helper for InitializeSlots.
///
From 51a186858096577aef6a860656523d3194eee9a8 Mon Sep 17 00:00:00 2001
From: jmlidbetter <53430310+jmlidbetter@users.noreply.github.com>
Date: Mon, 5 Aug 2019 17:25:37 +0100
Subject: [PATCH 018/998] Adds support to convert iterators to arrays (#928)
---
AUTHORS.md | 1 +
CHANGELOG.md | 1 +
src/runtime/converter.cs | 37 ++++++++++++++++++++-----------------
src/tests/test_array.py | 31 +++++++++++++++++++++++++++++++
4 files changed, 53 insertions(+), 17 deletions(-)
diff --git a/AUTHORS.md b/AUTHORS.md
index 6c2817aeb..39c2eb180 100644
--- a/AUTHORS.md
+++ b/AUTHORS.md
@@ -34,6 +34,7 @@
- Jan Krivanek ([@jakrivan](https://github.com/jakrivan))
- Jeff Reback ([@jreback](https://github.com/jreback))
- Joe Frayne ([@jfrayne](https://github.com/jfrayne))
+- Joe Lidbetter ([@jmlidbetter](https://github.com/jmlidbetter))
- John Burnett ([@johnburnett](https://github.com/johnburnett))
- John Wilkes ([@jbw3](https://github.com/jbw3))
- Luke Stratman ([@lstratman](https://github.com/lstratman))
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 705b33b7d..941045aef 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -16,6 +16,7 @@ This document follows the conventions laid out in [Keep a CHANGELOG][].
- Added argument types information to "No method matches given arguments" message
- Moved wheel import in setup.py inside of a try/except to prevent pip collection failures
- Removes PyLong_GetMax and PyClass_New when targetting Python3
+- Added support for converting python iterators to C# arrays
### Fixed
diff --git a/src/runtime/converter.cs b/src/runtime/converter.cs
index 11c67bf82..5d8769a73 100644
--- a/src/runtime/converter.cs
+++ b/src/runtime/converter.cs
@@ -837,17 +837,20 @@ private static void SetConversionError(IntPtr value, Type target)
///
/// Convert a Python value to a correctly typed managed array instance.
- /// The Python value must support the Python sequence protocol and the
+ /// The Python value must support the Python iterator protocol or and the
/// items in the sequence must be convertible to the target array type.
///
private static bool ToArray(IntPtr value, Type obType, out object result, bool setError)
{
Type elementType = obType.GetElementType();
- var size = Runtime.PySequence_Size(value);
result = null;
- if (size < 0)
- {
+ bool IsSeqObj = Runtime.PySequence_Check(value);
+ var len = IsSeqObj ? Runtime.PySequence_Size(value) : -1;
+
+ IntPtr IterObject = Runtime.PyObject_GetIter(value);
+
+ if(IterObject==IntPtr.Zero) {
if (setError)
{
SetConversionError(value, obType);
@@ -855,21 +858,17 @@ private static bool ToArray(IntPtr value, Type obType, out object result, bool s
return false;
}
- Array items = Array.CreateInstance(elementType, size);
+ Array items;
+
+ var listType = typeof(List<>);
+ var constructedListType = listType.MakeGenericType(elementType);
+ IList list = IsSeqObj ? (IList) Activator.CreateInstance(constructedListType, new Object[] {(int) len}) :
+ (IList) Activator.CreateInstance(constructedListType);
+ IntPtr item;
- // XXX - is there a better way to unwrap this if it is a real array?
- for (var i = 0; i < size; i++)
+ while ((item = Runtime.PyIter_Next(IterObject)) != IntPtr.Zero)
{
object obj = null;
- IntPtr item = Runtime.PySequence_GetItem(value, i);
- if (item == IntPtr.Zero)
- {
- if (setError)
- {
- SetConversionError(value, obType);
- return false;
- }
- }
if (!Converter.ToManaged(item, elementType, out obj, true))
{
@@ -877,10 +876,14 @@ private static bool ToArray(IntPtr value, Type obType, out object result, bool s
return false;
}
- items.SetValue(obj, i);
+ list.Add(obj);
Runtime.XDecref(item);
}
+ Runtime.XDecref(IterObject);
+ items = Array.CreateInstance(elementType, list.Count);
+ list.CopyTo(items, 0);
+
result = items;
return true;
}
diff --git a/src/tests/test_array.py b/src/tests/test_array.py
index 7ccadddff..b492a66d3 100644
--- a/src/tests/test_array.py
+++ b/src/tests/test_array.py
@@ -1337,3 +1337,34 @@ def test_array_abuse():
with pytest.raises(TypeError):
desc = Test.PublicArrayTest.__dict__['__setitem__']
desc(0, 0, 0)
+
+
+@pytest.mark.skipif(PY2, reason="Only applies in Python 3")
+def test_iterator_to_array():
+ from System import Array, String
+
+ d = {"a": 1, "b": 2, "c": 3}
+ keys_iterator = iter(d.keys())
+ arr = Array[String](keys_iterator)
+
+ Array.Sort(arr)
+
+ assert arr[0] == "a"
+ assert arr[1] == "b"
+ assert arr[2] == "c"
+
+
+@pytest.mark.skipif(PY2, reason="Only applies in Python 3")
+def test_dict_keys_to_array():
+ from System import Array, String
+
+ d = {"a": 1, "b": 2, "c": 3}
+ d_keys = d.keys()
+ arr = Array[String](d_keys)
+
+ Array.Sort(arr)
+
+ assert arr[0] == "a"
+ assert arr[1] == "b"
+ assert arr[2] == "c"
+
From f1da55e5d13f7c82d2eb62e211afd93d574d5fe8 Mon Sep 17 00:00:00 2001
From: jmlidbetter <53430310+jmlidbetter@users.noreply.github.com>
Date: Mon, 26 Aug 2019 13:13:38 +0100
Subject: [PATCH 019/998] Fixes bug where there is casting on delegates -- this
is explicitly in the NET standard documentation for
GetDelegateForFunctionPointer (#936)
---
CHANGELOG.md | 2 ++
src/runtime/nativecall.cs | 8 +++++---
2 files changed, 7 insertions(+), 3 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 941045aef..a545f335c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -17,11 +17,13 @@ This document follows the conventions laid out in [Keep a CHANGELOG][].
- Moved wheel import in setup.py inside of a try/except to prevent pip collection failures
- Removes PyLong_GetMax and PyClass_New when targetting Python3
- Added support for converting python iterators to C# arrays
+- Changed usage of obselete function GetDelegateForFunctionPointer(IntPtr, Type) to GetDelegateForFunctionPointer(IntPtr)
### Fixed
- Fixed runtime that fails loading when using pythonnet in an environment
together with Nuitka
+- Fixes bug where delegates get casts (dotnetcore)
## [2.4.0][]
diff --git a/src/runtime/nativecall.cs b/src/runtime/nativecall.cs
index b5bf25dd7..4a7bf05c8 100644
--- a/src/runtime/nativecall.cs
+++ b/src/runtime/nativecall.cs
@@ -32,19 +32,21 @@ internal class NativeCall
public static void Void_Call_1(IntPtr fp, IntPtr a1)
{
- ((Void_1_Delegate)Marshal.GetDelegateForFunctionPointer(fp, typeof(Void_1_Delegate)))(a1);
+ var d = Marshal.GetDelegateForFunctionPointer(fp);
+ d(a1);
}
public static IntPtr Call_3(IntPtr fp, IntPtr a1, IntPtr a2, IntPtr a3)
{
- var d = (Interop.TernaryFunc)Marshal.GetDelegateForFunctionPointer(fp, typeof(Interop.TernaryFunc));
+ var d = Marshal.GetDelegateForFunctionPointer(fp);
return d(a1, a2, a3);
}
public static int Int_Call_3(IntPtr fp, IntPtr a1, IntPtr a2, IntPtr a3)
{
- return ((Int_3_Delegate)Marshal.GetDelegateForFunctionPointer(fp, typeof(Int_3_Delegate)))(a1, a2, a3);
+ var d = Marshal.GetDelegateForFunctionPointer(fp);
+ return d(a1, a2, a3);
}
#else
private static AssemblyBuilder aBuilder;
From 1bcbeb5d0e9bcc7f0304994a44a7485d6126873b Mon Sep 17 00:00:00 2001
From: Joe Savage
Date: Thu, 12 Sep 2019 03:26:06 -0500
Subject: [PATCH 020/998] Feature/named arg support (#953)
* Add support for named arguments (#849)
* Remove kwarg check since it breaks the python-derived CLR class use-case
* Add named parameter test cases
* Update changelog and authors
* Add default params tests
---
AUTHORS.md | 1 +
CHANGELOG.md | 1 +
src/runtime/methodbinder.cs | 118 +++++++++++++++++++++-----
src/testing/methodtest.cs | 33 ++++++++
src/tests/test_method.py | 162 ++++++++++++++++++++++++++++++++++++
5 files changed, 296 insertions(+), 19 deletions(-)
diff --git a/AUTHORS.md b/AUTHORS.md
index 39c2eb180..9e13ca569 100644
--- a/AUTHORS.md
+++ b/AUTHORS.md
@@ -35,6 +35,7 @@
- Jeff Reback ([@jreback](https://github.com/jreback))
- Joe Frayne ([@jfrayne](https://github.com/jfrayne))
- Joe Lidbetter ([@jmlidbetter](https://github.com/jmlidbetter))
+- Joe Savage ([@s4v4g3](https://github.com/s4v4g3))
- John Burnett ([@johnburnett](https://github.com/johnburnett))
- John Wilkes ([@jbw3](https://github.com/jbw3))
- Luke Stratman ([@lstratman](https://github.com/lstratman))
diff --git a/CHANGELOG.md b/CHANGELOG.md
index a545f335c..5cb0ea96c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -18,6 +18,7 @@ This document follows the conventions laid out in [Keep a CHANGELOG][].
- Removes PyLong_GetMax and PyClass_New when targetting Python3
- Added support for converting python iterators to C# arrays
- Changed usage of obselete function GetDelegateForFunctionPointer(IntPtr, Type) to GetDelegateForFunctionPointer(IntPtr)
+- Added support for kwarg parameters when calling .NET methods from Python
### Fixed
diff --git a/src/runtime/methodbinder.cs b/src/runtime/methodbinder.cs
index 95b953555..8a7fc1930 100644
--- a/src/runtime/methodbinder.cs
+++ b/src/runtime/methodbinder.cs
@@ -2,6 +2,8 @@
using System.Collections;
using System.Reflection;
using System.Text;
+using System.Collections.Generic;
+using System.Linq;
namespace Python.Runtime
{
@@ -280,6 +282,22 @@ internal Binding Bind(IntPtr inst, IntPtr args, IntPtr kw, MethodBase info, Meth
{
// loop to find match, return invoker w/ or /wo error
MethodBase[] _methods = null;
+
+ var kwargDict = new Dictionary();
+ if (kw != IntPtr.Zero)
+ {
+ var pynkwargs = (int)Runtime.PyDict_Size(kw);
+ IntPtr keylist = Runtime.PyDict_Keys(kw);
+ IntPtr valueList = Runtime.PyDict_Values(kw);
+ for (int i = 0; i < pynkwargs; ++i)
+ {
+ var keyStr = Runtime.GetManagedString(Runtime.PyList_GetItem(keylist, i));
+ kwargDict[keyStr] = Runtime.PyList_GetItem(valueList, i);
+ }
+ Runtime.XDecref(keylist);
+ Runtime.XDecref(valueList);
+ }
+
var pynargs = (int)Runtime.PyTuple_Size(args);
var isGeneric = false;
if (info != null)
@@ -303,11 +321,12 @@ internal Binding Bind(IntPtr inst, IntPtr args, IntPtr kw, MethodBase info, Meth
ArrayList defaultArgList;
bool paramsArray;
- if (!MatchesArgumentCount(pynargs, pi, out paramsArray, out defaultArgList)) {
+ if (!MatchesArgumentCount(pynargs, pi, kwargDict, out paramsArray, out defaultArgList))
+ {
continue;
}
var outs = 0;
- var margs = TryConvertArguments(pi, paramsArray, args, pynargs, defaultArgList,
+ var margs = TryConvertArguments(pi, paramsArray, args, pynargs, kwargDict, defaultArgList,
needsResolution: _methods.Length > 1,
outs: out outs);
@@ -351,19 +370,21 @@ internal Binding Bind(IntPtr inst, IntPtr args, IntPtr kw, MethodBase info, Meth
}
///
- /// Attempts to convert Python argument tuple into an array of managed objects,
- /// that can be passed to a method.
+ /// Attempts to convert Python positional argument tuple and keyword argument table
+ /// into an array of managed objects, that can be passed to a method.
///
/// Information about expected parameters
/// true, if the last parameter is a params array.
/// A pointer to the Python argument tuple
/// Number of arguments, passed by Python
+ /// Dictionary of keyword argument name to python object pointer
/// A list of default values for omitted parameters
/// true, if overloading resolution is required
/// Returns number of output parameters
/// An array of .NET arguments, that can be passed to a method.
static object[] TryConvertArguments(ParameterInfo[] pi, bool paramsArray,
IntPtr args, int pyArgCount,
+ Dictionary kwargDict,
ArrayList defaultArgList,
bool needsResolution,
out int outs)
@@ -374,7 +395,10 @@ static object[] TryConvertArguments(ParameterInfo[] pi, bool paramsArray,
for (int paramIndex = 0; paramIndex < pi.Length; paramIndex++)
{
- if (paramIndex >= pyArgCount)
+ var parameter = pi[paramIndex];
+ bool hasNamedParam = kwargDict.ContainsKey(parameter.Name);
+
+ if (paramIndex >= pyArgCount && !hasNamedParam)
{
if (defaultArgList != null)
{
@@ -384,12 +408,19 @@ static object[] TryConvertArguments(ParameterInfo[] pi, bool paramsArray,
continue;
}
- var parameter = pi[paramIndex];
- IntPtr op = (arrayStart == paramIndex)
- // map remaining Python arguments to a tuple since
- // the managed function accepts it - hopefully :]
- ? Runtime.PyTuple_GetSlice(args, arrayStart, pyArgCount)
- : Runtime.PyTuple_GetItem(args, paramIndex);
+ IntPtr op;
+ if (hasNamedParam)
+ {
+ op = kwargDict[parameter.Name];
+ }
+ else
+ {
+ op = (arrayStart == paramIndex)
+ // map remaining Python arguments to a tuple since
+ // the managed function accepts it - hopefully :]
+ ? Runtime.PyTuple_GetSlice(args, arrayStart, pyArgCount)
+ : Runtime.PyTuple_GetItem(args, paramIndex);
+ }
bool isOut;
if (!TryConvertArgument(op, parameter.ParameterType, needsResolution, out margs[paramIndex], out isOut))
@@ -505,7 +536,8 @@ static Type TryComputeClrArgumentType(Type parameterType, IntPtr argument, bool
return clrtype;
}
- static bool MatchesArgumentCount(int argumentCount, ParameterInfo[] parameters,
+ static bool MatchesArgumentCount(int positionalArgumentCount, ParameterInfo[] parameters,
+ Dictionary kwargDict,
out bool paramsArray,
out ArrayList defaultArgList)
{
@@ -513,21 +545,40 @@ static bool MatchesArgumentCount(int argumentCount, ParameterInfo[] parameters,
var match = false;
paramsArray = false;
- if (argumentCount == parameters.Length)
+ if (positionalArgumentCount == parameters.Length)
{
match = true;
- } else if (argumentCount < parameters.Length)
+ }
+ else if (positionalArgumentCount < parameters.Length)
{
+ // every parameter past 'positionalArgumentCount' must have either
+ // a corresponding keyword argument or a default parameter
match = true;
defaultArgList = new ArrayList();
- for (var v = argumentCount; v < parameters.Length; v++) {
- if (parameters[v].DefaultValue == DBNull.Value) {
+ for (var v = positionalArgumentCount; v < parameters.Length; v++)
+ {
+ if (kwargDict.ContainsKey(parameters[v].Name))
+ {
+ // we have a keyword argument for this parameter,
+ // no need to check for a default parameter, but put a null
+ // placeholder in defaultArgList
+ defaultArgList.Add(null);
+ }
+ else if (parameters[v].IsOptional)
+ {
+ // IsOptional will be true if the parameter has a default value,
+ // or if the parameter has the [Optional] attribute specified.
+ // The GetDefaultValue() extension method will return the value
+ // to be passed in as the parameter value
+ defaultArgList.Add(parameters[v].GetDefaultValue());
+ }
+ else
+ {
match = false;
- } else {
- defaultArgList.Add(parameters[v].DefaultValue);
}
}
- } else if (argumentCount > parameters.Length && parameters.Length > 0 &&
+ }
+ else if (positionalArgumentCount > parameters.Length && parameters.Length > 0 &&
Attribute.IsDefined(parameters[parameters.Length - 1], typeof(ParamArrayAttribute)))
{
// This is a `foo(params object[] bar)` style method
@@ -722,4 +773,33 @@ internal Binding(MethodBase info, object inst, object[] args, int outs)
this.outs = outs;
}
}
+
+
+ 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)
+ {
+ return parameterInfo.DefaultValue;
+ }
+ else
+ {
+ // [OptionalAttribute] was specified for the parameter.
+ // See https://stackoverflow.com/questions/3416216/optionalattribute-parameters-default-value
+ // for rules on determining the value to pass to the parameter
+ var type = parameterInfo.ParameterType;
+ if (type == typeof(object))
+ return Type.Missing;
+ else if (type.IsValueType)
+ return Activator.CreateInstance(type);
+ else
+ return null;
+ }
+ }
+ }
}
diff --git a/src/testing/methodtest.cs b/src/testing/methodtest.cs
index cf653f9f9..91836b727 100644
--- a/src/testing/methodtest.cs
+++ b/src/testing/methodtest.cs
@@ -1,5 +1,6 @@
using System;
using System.IO;
+using System.Runtime.InteropServices;
namespace Python.Test
{
@@ -651,6 +652,38 @@ public static string Casesensitive()
{
return "Casesensitive";
}
+
+ public static string DefaultParams(int a=0, int b=0, int c=0, int d=0)
+ {
+ return string.Format("{0}{1}{2}{3}", a, b, c, d);
+ }
+
+ public static string OptionalParams([Optional]int a, [Optional]int b, [Optional]int c, [Optional] int d)
+ {
+ return string.Format("{0}{1}{2}{3}", a, b, c, d);
+ }
+
+ public static bool OptionalParams_TestMissing([Optional]object a)
+ {
+ return a == Type.Missing;
+ }
+
+ public static bool OptionalParams_TestReferenceType([Optional]string a)
+ {
+ return a == null;
+ }
+
+ public static string OptionalAndDefaultParams([Optional]int a, [Optional]int b, int c=0, int d=0)
+ {
+ return string.Format("{0}{1}{2}{3}", a, b, c, d);
+ }
+
+ public static string OptionalAndDefaultParams2([Optional]int a, [Optional]int b, [Optional, DefaultParameterValue(1)]int c, int d = 2)
+ {
+ return string.Format("{0}{1}{2}{3}", a, b, c, d);
+ }
+
+
}
diff --git a/src/tests/test_method.py b/src/tests/test_method.py
index ad678611b..34f460d59 100644
--- a/src/tests/test_method.py
+++ b/src/tests/test_method.py
@@ -776,6 +776,9 @@ def test_no_object_in_param():
res = MethodTest.TestOverloadedNoObject(5)
assert res == "Got int"
+
+ res = MethodTest.TestOverloadedNoObject(i=7)
+ assert res == "Got int"
with pytest.raises(TypeError):
MethodTest.TestOverloadedNoObject("test")
@@ -787,9 +790,15 @@ def test_object_in_param():
res = MethodTest.TestOverloadedObject(5)
assert res == "Got int"
+
+ res = MethodTest.TestOverloadedObject(i=7)
+ assert res == "Got int"
res = MethodTest.TestOverloadedObject("test")
assert res == "Got object"
+
+ res = MethodTest.TestOverloadedObject(o="test")
+ assert res == "Got object"
def test_object_in_multiparam():
@@ -813,6 +822,42 @@ def test_object_in_multiparam():
res = MethodTest.TestOverloadedObjectTwo(7.24, 7.24)
assert res == "Got object-object"
+ res = MethodTest.TestOverloadedObjectTwo(a=5, b=5)
+ assert res == "Got int-int"
+
+ res = MethodTest.TestOverloadedObjectTwo(5, b=5)
+ assert res == "Got int-int"
+
+ res = MethodTest.TestOverloadedObjectTwo(a=5, b="foo")
+ assert res == "Got int-string"
+
+ res = MethodTest.TestOverloadedObjectTwo(5, b="foo")
+ assert res == "Got int-string"
+
+ res = MethodTest.TestOverloadedObjectTwo(a="foo", b=7.24)
+ assert res == "Got string-object"
+
+ res = MethodTest.TestOverloadedObjectTwo("foo", b=7.24)
+ assert res == "Got string-object"
+
+ res = MethodTest.TestOverloadedObjectTwo(a="foo", b="bar")
+ assert res == "Got string-string"
+
+ res = MethodTest.TestOverloadedObjectTwo("foo", b="bar")
+ assert res == "Got string-string"
+
+ res = MethodTest.TestOverloadedObjectTwo(a="foo", b=5)
+ assert res == "Got string-int"
+
+ res = MethodTest.TestOverloadedObjectTwo("foo", b=5)
+ assert res == "Got string-int"
+
+ res = MethodTest.TestOverloadedObjectTwo(a=7.24, b=7.24)
+ assert res == "Got object-object"
+
+ res = MethodTest.TestOverloadedObjectTwo(7.24, b=7.24)
+ assert res == "Got object-object"
+
def test_object_in_multiparam_exception():
"""Test method with object multiparams behaves"""
@@ -966,3 +1011,120 @@ def test_getting_overloaded_constructor_binding_does_not_leak_ref_count():
# simple test
refCount = sys.getrefcount(PlainOldClass.Overloads[int])
assert refCount == 1
+
+
+def test_default_params():
+ # all positional parameters
+ res = MethodTest.DefaultParams(1,2,3,4)
+ assert res == "1234"
+
+ res = MethodTest.DefaultParams(1, 2, 3)
+ assert res == "1230"
+
+ res = MethodTest.DefaultParams(1, 2)
+ assert res == "1200"
+
+ res = MethodTest.DefaultParams(1)
+ assert res == "1000"
+
+ res = MethodTest.DefaultParams(a=2)
+ assert res == "2000"
+
+ res = MethodTest.DefaultParams(b=3)
+ assert res == "0300"
+
+ res = MethodTest.DefaultParams(c=4)
+ assert res == "0040"
+
+ res = MethodTest.DefaultParams(d=7)
+ assert res == "0007"
+
+ res = MethodTest.DefaultParams(a=2, c=5)
+ assert res == "2050"
+
+ res = MethodTest.DefaultParams(1, d=7, c=3)
+ assert res == "1037"
+
+ with pytest.raises(TypeError):
+ MethodTest.DefaultParams(1,2,3,4,5)
+
+def test_optional_params():
+ res = MethodTest.OptionalParams(1, 2, 3, 4)
+ assert res == "1234"
+
+ res = MethodTest.OptionalParams(1, 2, 3)
+ assert res == "1230"
+
+ res = MethodTest.OptionalParams(1, 2)
+ assert res == "1200"
+
+ res = MethodTest.OptionalParams(1)
+ assert res == "1000"
+
+ res = MethodTest.OptionalParams(a=2)
+ assert res == "2000"
+
+ res = MethodTest.OptionalParams(b=3)
+ assert res == "0300"
+
+ res = MethodTest.OptionalParams(c=4)
+ assert res == "0040"
+
+ res = MethodTest.OptionalParams(d=7)
+ assert res == "0007"
+
+ res = MethodTest.OptionalParams(a=2, c=5)
+ assert res == "2050"
+
+ res = MethodTest.OptionalParams(1, d=7, c=3)
+ assert res == "1037"
+
+ res = MethodTest.OptionalParams_TestMissing()
+ assert res == True
+
+ res = MethodTest.OptionalParams_TestMissing(None)
+ assert res == False
+
+ res = MethodTest.OptionalParams_TestMissing(a = None)
+ assert res == False
+
+ res = MethodTest.OptionalParams_TestMissing(a='hi')
+ assert res == False
+
+ res = MethodTest.OptionalParams_TestReferenceType()
+ assert res == True
+
+ res = MethodTest.OptionalParams_TestReferenceType(None)
+ assert res == True
+
+ res = MethodTest.OptionalParams_TestReferenceType(a=None)
+ assert res == True
+
+ res = MethodTest.OptionalParams_TestReferenceType('hi')
+ assert res == False
+
+ res = MethodTest.OptionalParams_TestReferenceType(a='hi')
+ assert res == False
+
+def test_optional_and_default_params():
+
+ res = MethodTest.OptionalAndDefaultParams()
+ assert res == "0000"
+
+ res = MethodTest.OptionalAndDefaultParams(1)
+ assert res == "1000"
+
+ res = MethodTest.OptionalAndDefaultParams(1, c=4)
+ assert res == "1040"
+
+ res = MethodTest.OptionalAndDefaultParams(b=4, c=7)
+ assert res == "0470"
+
+ res = MethodTest.OptionalAndDefaultParams2()
+ assert res == "0012"
+
+ res = MethodTest.OptionalAndDefaultParams2(a=1,b=2,c=3,d=4)
+ assert res == "1234"
+
+ res = MethodTest.OptionalAndDefaultParams2(b=2, c=3)
+ assert res == "0232"
From 4e19a4ff82e9a50155f898366c506bfa337c022c Mon Sep 17 00:00:00 2001
From: amos402
Date: Wed, 18 Sep 2019 00:36:13 +0800
Subject: [PATCH 021/998] Add soft shutdown
---
src/embed_tests/TestDomainReload.cs | 3 +-
src/runtime/classbase.cs | 42 +++-
src/runtime/classmanager.cs | 49 ++++-
src/runtime/clrobject.cs | 3 +-
src/runtime/constructorbinding.cs | 22 ++
src/runtime/extensiontype.cs | 21 +-
src/runtime/fieldobject.cs | 10 +
src/runtime/genericutil.cs | 5 -
src/runtime/importhook.cs | 25 ++-
src/runtime/interop.cs | 58 +++--
src/runtime/managedtype.cs | 105 ++++++++++
src/runtime/methodbinding.cs | 18 +-
src/runtime/methodobject.cs | 24 ++-
src/runtime/methodwrapper.cs | 26 ++-
src/runtime/moduleobject.cs | 39 ++++
src/runtime/pythonengine.cs | 134 ++++++------
src/runtime/pythonexception.cs | 17 ++
src/runtime/runtime.cs | 314 +++++++++++++++++++++++++---
src/runtime/runtime_state.cs | 242 +++++++++++++++++++++
src/runtime/typemanager.cs | 74 +++++--
20 files changed, 1079 insertions(+), 152 deletions(-)
create mode 100644 src/runtime/runtime_state.cs
diff --git a/src/embed_tests/TestDomainReload.cs b/src/embed_tests/TestDomainReload.cs
index b162d4eb0..6084c4062 100644
--- a/src/embed_tests/TestDomainReload.cs
+++ b/src/embed_tests/TestDomainReload.cs
@@ -57,7 +57,7 @@ public static void DomainReloadAndGC()
RunAssemblyAndUnload(pythonRunner1, "test1");
// Verify that python is not initialized even though we ran it.
- Assert.That(Runtime.Runtime.Py_IsInitialized(), Is.Zero);
+ //Assert.That(Runtime.Runtime.Py_IsInitialized(), Is.Zero);
// This caused a crash because objects allocated in pythonRunner1
// still existed in memory, but the code to do python GC on those
@@ -83,6 +83,7 @@ public static void RunPython() {
AppDomain.CurrentDomain.DomainUnload += OnDomainUnload;
string name = AppDomain.CurrentDomain.FriendlyName;
Console.WriteLine(string.Format(""[{0} in .NET] In PythonRunner.RunPython"", name));
+ //PythonEngine.Initialize(softShutdown: true);
using (Py.GIL()) {
try {
var pyScript = string.Format(""import clr\n""
diff --git a/src/runtime/classbase.cs b/src/runtime/classbase.cs
index 5846fa82a..af00c58fe 100644
--- a/src/runtime/classbase.cs
+++ b/src/runtime/classbase.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections;
+using System.Diagnostics;
using System.Runtime.InteropServices;
namespace Python.Runtime
@@ -253,15 +254,48 @@ public static IntPtr tp_str(IntPtr ob)
public static void tp_dealloc(IntPtr ob)
{
ManagedType self = GetManagedObject(ob);
- IntPtr dict = Marshal.ReadIntPtr(ob, ObjectOffset.DictOffset(ob));
- if (dict != IntPtr.Zero)
+ if (self.pyHandle != self.tpHandle)
{
- Runtime.XDecref(dict);
+ ClearObjectDict(ob);
}
Runtime.PyObject_GC_UnTrack(self.pyHandle);
Runtime.PyObject_GC_Del(self.pyHandle);
Runtime.XDecref(self.tpHandle);
- self.gcHandle.Free();
+ self.FreeGCHandle();
+ }
+
+ public static int tp_clear(IntPtr ob)
+ {
+ ManagedType self = GetManagedObject(ob);
+ if (self.pyHandle != self.tpHandle)
+ {
+ ClearObjectDict(ob);
+ }
+ Runtime.XDecref(self.tpHandle);
+ self.tpHandle = IntPtr.Zero;
+ self.FreeGCHandle();
+ return 0;
+ }
+
+ private static IntPtr GetObjectDict(IntPtr ob)
+ {
+ return Marshal.ReadIntPtr(ob, ObjectOffset.DictOffset(ob));
+ }
+
+ private static void SetObjectDict(IntPtr ob, IntPtr value)
+ {
+ Marshal.WriteIntPtr(ob, ObjectOffset.DictOffset(ob), value);
+ }
+
+ private static void ClearObjectDict(IntPtr ob)
+ {
+ IntPtr dict = GetObjectDict(ob);
+ if (dict == IntPtr.Zero)
+ {
+ return;
+ }
+ SetObjectDict(ob, IntPtr.Zero);
+ Runtime.XDecref(dict);
}
}
}
diff --git a/src/runtime/classmanager.cs b/src/runtime/classmanager.cs
index 0b084a49d..49b732910 100644
--- a/src/runtime/classmanager.cs
+++ b/src/runtime/classmanager.cs
@@ -4,6 +4,7 @@
using System.Reflection;
using System.Runtime.InteropServices;
using System.Security;
+using System.Linq;
namespace Python.Runtime
{
@@ -26,7 +27,6 @@ private ClassManager()
static ClassManager()
{
- cache = new Dictionary(128);
// SEE: https://msdn.microsoft.com/en-us/library/96b1ayy4(v=vs.100).aspx
// ""All delegates inherit from MulticastDelegate, which inherits from Delegate.""
// Was Delegate, which caused a null MethodInfo returned from GetMethode("Invoke")
@@ -39,6 +39,49 @@ public static void Reset()
cache = new Dictionary(128);
}
+ public static IList GetManagedTypes()
+ {
+ return cache.Values.ToArray(); // Make a copy.
+ }
+
+ internal static void RemoveClasses()
+ {
+ var visited = new HashSet();
+ var visitedHandle = GCHandle.Alloc(visited);
+ var visitedPtr = (IntPtr)visitedHandle;
+ try
+ {
+ foreach (var cls in cache.Values)
+ {
+ cls.TypeTraverse(OnVisit, visitedPtr);
+ // XXX: Force release some resouces.
+ cls.TypeClear();
+ //Runtime.XDecref(cls.pyHandle);
+ }
+ }
+ finally
+ {
+ visitedHandle.Free();
+ }
+ }
+
+ private static int OnVisit(IntPtr ob, IntPtr arg)
+ {
+ var visited = (HashSet)GCHandle.FromIntPtr(arg).Target;
+ if (visited.Contains(ob))
+ {
+ return 0;
+ }
+ visited.Add(ob);
+ var clrObj = ManagedType.GetManagedObject(ob);
+ if (clrObj != null)
+ {
+ clrObj.TypeTraverse(OnVisit, arg);
+ clrObj.TypeClear();
+ }
+ return 0;
+ }
+
///
/// Return the ClassBase-derived instance that implements a particular
/// reflected managed type, creating it if it doesn't yet exist.
@@ -134,7 +177,6 @@ private static void InitClassBase(Type type, ClassBase impl)
IntPtr tp = TypeManager.GetTypeHandle(impl, type);
- impl.tpHandle = tp;
// Finally, initialize the class __dict__ and return the object.
IntPtr dict = Marshal.ReadIntPtr(tp, TypeOffset.tp_dict);
@@ -146,6 +188,9 @@ private static void InitClassBase(Type type, ClassBase impl)
var item = (ManagedType)iter.Value;
var name = (string)iter.Key;
Runtime.PyDict_SetItemString(dict, name, item.pyHandle);
+ // info.members are already useless
+ Runtime.XDecref(item.pyHandle);
+ item.pyHandle = IntPtr.Zero;
}
// If class has constructors, generate an __doc__ attribute.
diff --git a/src/runtime/clrobject.cs b/src/runtime/clrobject.cs
index 502677655..29c57b28f 100644
--- a/src/runtime/clrobject.cs
+++ b/src/runtime/clrobject.cs
@@ -22,11 +22,10 @@ internal CLRObject(object ob, IntPtr tp)
}
}
- GCHandle gc = GCHandle.Alloc(this);
+ GCHandle gc = AllocGCHandle();
Marshal.WriteIntPtr(py, ObjectOffset.magic(tp), (IntPtr)gc);
tpHandle = tp;
pyHandle = py;
- gcHandle = gc;
inst = ob;
// Fix the BaseException args (and __cause__ in case of Python 3)
diff --git a/src/runtime/constructorbinding.cs b/src/runtime/constructorbinding.cs
index 3908628b9..a9f6c961b 100644
--- a/src/runtime/constructorbinding.cs
+++ b/src/runtime/constructorbinding.cs
@@ -147,6 +147,17 @@ public static IntPtr tp_repr(IntPtr ob)
Runtime.XDecref(self.pyTypeHndl);
ExtensionType.FinalizeObject(self);
}
+
+ public static int tp_traverse(IntPtr ob, IntPtr visit, IntPtr arg)
+ {
+ var self = (ConstructorBinding)GetManagedObject(ob);
+ int res = PyVisit(self.pyTypeHndl, visit, arg);
+ if (res != 0) return res;
+
+ res = PyVisit(self.repr, visit, arg);
+ if (res != 0) return res;
+ return 0;
+ }
}
///
@@ -233,5 +244,16 @@ public static IntPtr tp_repr(IntPtr ob)
Runtime.XDecref(self.pyTypeHndl);
FinalizeObject(self);
}
+
+ public static int tp_traverse(IntPtr ob, IntPtr visit, IntPtr arg)
+ {
+ var self = (BoundContructor)GetManagedObject(ob);
+ int res = PyVisit(self.pyTypeHndl, visit, arg);
+ if (res != 0) return res;
+
+ res = PyVisit(self.repr, visit, arg);
+ if (res != 0) return res;
+ return 0;
+ }
}
}
diff --git a/src/runtime/extensiontype.cs b/src/runtime/extensiontype.cs
index 693a46f42..5e8ee6b59 100644
--- a/src/runtime/extensiontype.cs
+++ b/src/runtime/extensiontype.cs
@@ -28,7 +28,7 @@ public ExtensionType()
IntPtr py = Runtime.PyType_GenericAlloc(tp, 0);
- GCHandle gc = GCHandle.Alloc(this);
+ GCHandle gc = AllocGCHandle();
Marshal.WriteIntPtr(py, ObjectOffset.magic(tp), (IntPtr)gc);
// We have to support gc because the type machinery makes it very
@@ -40,7 +40,6 @@ public ExtensionType()
tpHandle = tp;
pyHandle = py;
- gcHandle = gc;
}
@@ -51,7 +50,7 @@ public static void FinalizeObject(ManagedType self)
{
Runtime.PyObject_GC_Del(self.pyHandle);
Runtime.XDecref(self.tpHandle);
- self.gcHandle.Free();
+ self.FreeGCHandle();
}
@@ -91,5 +90,21 @@ public static void tp_dealloc(IntPtr ob)
ManagedType self = GetManagedObject(ob);
FinalizeObject(self);
}
+
+ public static int tp_clear(IntPtr ob)
+ {
+ ManagedType self = GetManagedObject(ob);
+ Runtime.XDecref(self.tpHandle);
+ self.FreeGCHandle();
+ return 0;
+ }
+
+ //public static int tp_traverse(IntPtr ob, IntPtr visit, IntPtr arg)
+ //{
+ // ManagedType self = GetManagedObject(ob);
+ // int res = PyVisit(self.tpHandle, visit, arg);
+ // if (res != 0) return res;
+ // return 0;
+ //}
}
}
diff --git a/src/runtime/fieldobject.cs b/src/runtime/fieldobject.cs
index 7c9a466d5..c4675d723 100644
--- a/src/runtime/fieldobject.cs
+++ b/src/runtime/fieldobject.cs
@@ -55,6 +55,11 @@ public static IntPtr tp_descr_get(IntPtr ds, IntPtr ob, IntPtr tp)
try
{
var co = (CLRObject)GetManagedObject(ob);
+ if (co == null)
+ {
+ Exceptions.SetError(Exceptions.TypeError, "instance is not a clr object");
+ return IntPtr.Zero;
+ }
result = info.GetValue(co.inst);
return Converter.ToPython(result, info.FieldType);
}
@@ -115,6 +120,11 @@ public static IntPtr tp_descr_get(IntPtr ds, IntPtr ob, IntPtr tp)
if (!is_static)
{
var co = (CLRObject)GetManagedObject(ob);
+ if (co == null)
+ {
+ Exceptions.SetError(Exceptions.TypeError, "instance is not a clr object");
+ return -1;
+ }
info.SetValue(co.inst, newval);
}
else
diff --git a/src/runtime/genericutil.cs b/src/runtime/genericutil.cs
index 3a230e12c..df78d9899 100644
--- a/src/runtime/genericutil.cs
+++ b/src/runtime/genericutil.cs
@@ -16,11 +16,6 @@ private GenericUtil()
{
}
- static GenericUtil()
- {
- mapping = new Dictionary>>();
- }
-
public static void Reset()
{
mapping = new Dictionary>>();
diff --git a/src/runtime/importhook.cs b/src/runtime/importhook.cs
index 7e4a208f5..ef322648a 100644
--- a/src/runtime/importhook.cs
+++ b/src/runtime/importhook.cs
@@ -72,12 +72,29 @@ internal static void Initialize()
///
internal static void Shutdown()
{
- if (Runtime.Py_IsInitialized() != 0)
+ if (Runtime.Py_IsInitialized() == 0)
{
- Runtime.XDecref(py_clr_module);
- Runtime.XDecref(root.pyHandle);
- Runtime.XDecref(py_import);
+ return;
}
+ IntPtr dict = Runtime.PyImport_GetModuleDict();
+ IntPtr mod = Runtime.IsPython3 ?
+ Runtime.PyImport_ImportModule("builtins")
+ : Runtime.PyDict_GetItemString(dict, "__builtin__");
+ Runtime.PyObject_SetAttrString(mod, "__import__", py_import);
+
+ Runtime.XDecref(py_clr_module);
+ py_clr_module = IntPtr.Zero;
+
+ Runtime.XDecref(root.pyHandle);
+ root = null;
+
+ hook.Dispose();
+ hook = null;
+
+ Runtime.XDecref(py_import);
+ py_import = IntPtr.Zero;
+
+ CLRModule.Reset();
}
///
diff --git a/src/runtime/interop.cs b/src/runtime/interop.cs
index 4ae4b61e0..702bb7982 100644
--- a/src/runtime/interop.cs
+++ b/src/runtime/interop.cs
@@ -4,6 +4,7 @@
using System.Runtime.InteropServices;
using System.Reflection;
using System.Text;
+using System.Collections.Generic;
namespace Python.Runtime
{
@@ -334,7 +335,7 @@ internal class TypeFlags
internal class Interop
{
- private static ArrayList keepAlive;
+ private static Dictionary, Tuple> keepAlive;
private static Hashtable pmap;
static Interop()
@@ -351,8 +352,8 @@ static Interop()
p[item.Name] = item;
}
- keepAlive = new ArrayList();
- Marshal.AllocHGlobal(IntPtr.Size);
+ keepAlive = new Dictionary, Tuple>();
+ //Marshal.AllocHGlobal(IntPtr.Size);
pmap = new Hashtable();
pmap["tp_dealloc"] = p["DestructorFunc"];
@@ -450,6 +451,23 @@ internal static Type GetPrototype(string name)
}
internal static IntPtr GetThunk(MethodInfo method, string funcType = null)
+ {
+ var key = new KeyValuePair(method, funcType);
+ Tuple thunkPair;
+ if (keepAlive.TryGetValue(key, out thunkPair))
+ {
+ return thunkPair.Item2;
+ }
+ thunkPair = GetThunkImpl(method, funcType);
+ if (thunkPair == null)
+ {
+ return IntPtr.Zero;
+ }
+ keepAlive[key] = thunkPair;
+ return thunkPair.Item2;
+ }
+
+ private static Tuple GetThunkImpl(MethodInfo method, string funcType)
{
Type dt;
if (funcType != null)
@@ -457,18 +475,17 @@ internal static IntPtr GetThunk(MethodInfo method, string funcType = null)
else
dt = GetPrototype(method.Name);
- if (dt != null)
+ if (dt == null)
{
- IntPtr tmp = Marshal.AllocHGlobal(IntPtr.Size);
- Delegate d = Delegate.CreateDelegate(dt, method);
- Thunk cb = new Thunk(d);
- Marshal.StructureToPtr(cb, tmp, false);
- IntPtr fp = Marshal.ReadIntPtr(tmp, 0);
- Marshal.FreeHGlobal(tmp);
- keepAlive.Add(d);
- return fp;
+ return null;
}
- return IntPtr.Zero;
+ IntPtr tmp = Marshal.AllocHGlobal(IntPtr.Size);
+ Delegate d = Delegate.CreateDelegate(dt, method);
+ Thunk cb = new Thunk(d);
+ Marshal.StructureToPtr(cb, tmp, false);
+ IntPtr fp = Marshal.ReadIntPtr(tmp, 0);
+ Marshal.FreeHGlobal(tmp);
+ return new Tuple(d, fp);
}
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
@@ -522,4 +539,19 @@ public Thunk(Delegate d)
fn = d;
}
}
+
+
+ [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
+ struct PyGC_Node
+ {
+ public IntPtr gc_next;
+ public IntPtr gc_prev;
+ public IntPtr gc_refs;
+ }
+
+ [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
+ struct PyGC_Head
+ {
+ public PyGC_Node gc;
+ }
}
diff --git a/src/runtime/managedtype.cs b/src/runtime/managedtype.cs
index 3191da949..6f0e8b88b 100644
--- a/src/runtime/managedtype.cs
+++ b/src/runtime/managedtype.cs
@@ -1,4 +1,6 @@
using System;
+using System.Collections.Generic;
+using System.Diagnostics;
using System.Runtime.InteropServices;
namespace Python.Runtime
@@ -14,6 +16,48 @@ internal abstract class ManagedType
internal IntPtr pyHandle; // PyObject *
internal IntPtr tpHandle; // PyType *
+#if NPY_TRACK_OBJECT
+ private static readonly HashSet _managedObjs = new HashSet();
+#endif
+
+ internal void DecrRefCount()
+ {
+ Runtime.XDecref(pyHandle);
+ }
+
+ internal long RefCount
+ {
+ get
+ {
+ var gs = Runtime.PyGILState_Ensure();
+ try
+ {
+ return Runtime.Refcount(pyHandle);
+ }
+ finally
+ {
+ Runtime.PyGILState_Release(gs);
+ }
+ }
+ }
+
+ internal GCHandle AllocGCHandle()
+ {
+ gcHandle = GCHandle.Alloc(this);
+#if NPY_TRACK_OBJECT
+ _managedObjs.Add(this);
+#endif
+ return gcHandle;
+ }
+
+ internal void FreeGCHandle()
+ {
+ gcHandle.Free();
+#if NPY_TRACK_OBJECT
+ _managedObjs.Remove(this);
+#endif
+ gcHandle = new GCHandle();
+ }
///
/// Given a Python object, return the associated managed object or null.
@@ -75,5 +119,66 @@ internal static bool IsManagedType(IntPtr ob)
}
return false;
}
+
+#if NPY_TRACK_OBJECT
+ internal static void Reset()
+ {
+ _managedObjs.Clear();
+ }
+
+ internal static ICollection GetManagedObjects()
+ {
+ return _managedObjs;
+ }
+#endif
+
+ internal static int PyVisit(IntPtr ob, IntPtr visit, IntPtr arg)
+ {
+ if (ob == IntPtr.Zero)
+ {
+ return 0;
+ }
+ var visitFunc = (Interop.ObjObjFunc)Marshal.GetDelegateForFunctionPointer(visit, typeof(Interop.ObjObjFunc));
+ return visitFunc(ob, arg);
+ }
+
+ ///
+ /// Wrapper for calling tp_clear
+ ///
+ internal void TypeClear()
+ {
+ if (tpHandle == IntPtr.Zero || pyHandle == IntPtr.Zero)
+ {
+ return;
+ }
+ var clearPtr = Marshal.ReadIntPtr(tpHandle, TypeOffset.tp_clear);
+ if (clearPtr == IntPtr.Zero)
+ {
+ return;
+ }
+ var clearFunc = (Interop.InquiryFunc)Marshal.GetDelegateForFunctionPointer(clearPtr, typeof(Interop.InquiryFunc));
+ // TODO: Handle errors base on return value
+ clearFunc(pyHandle);
+ }
+
+ ///
+ /// Wrapper for calling tp_traverse
+ ///
+ internal void TypeTraverse(Interop.ObjObjFunc visitproc, IntPtr arg)
+ {
+ if (tpHandle == IntPtr.Zero || pyHandle == IntPtr.Zero)
+ {
+ return;
+ }
+ var traversePtr = Marshal.ReadIntPtr(tpHandle, TypeOffset.tp_traverse);
+ if (traversePtr == IntPtr.Zero)
+ {
+ return;
+ }
+ var traverseFunc = (Interop.ObjObjArgFunc)Marshal.GetDelegateForFunctionPointer(traversePtr, typeof(Interop.ObjObjArgFunc));
+ var visiPtr = Marshal.GetFunctionPointerForDelegate(visitproc);
+ // TODO: Handle errors base on return value
+ traverseFunc(pyHandle, visiPtr, arg);
+ }
}
}
diff --git a/src/runtime/methodbinding.cs b/src/runtime/methodbinding.cs
index f402f91f8..b917d9bb9 100644
--- a/src/runtime/methodbinding.cs
+++ b/src/runtime/methodbinding.cs
@@ -36,6 +36,14 @@ public MethodBinding(MethodObject m, IntPtr target) : this(m, target, IntPtr.Zer
{
}
+ private void ClearMembers()
+ {
+ Runtime.XDecref(target);
+ target = IntPtr.Zero;
+ Runtime.XDecref(targetType);
+ targetType = IntPtr.Zero;
+ }
+
///
/// Implement binding of generic methods using the subscript syntax [].
///
@@ -237,9 +245,15 @@ public static IntPtr tp_repr(IntPtr ob)
public new static void tp_dealloc(IntPtr ob)
{
var self = (MethodBinding)GetManagedObject(ob);
- Runtime.XDecref(self.target);
- Runtime.XDecref(self.targetType);
+ self.ClearMembers();
FinalizeObject(self);
}
+
+ public new static int tp_clear(IntPtr ob)
+ {
+ var self = (MethodBinding)GetManagedObject(ob);
+ self.ClearMembers();
+ return ExtensionType.tp_clear(ob);
+ }
}
}
diff --git a/src/runtime/methodobject.cs b/src/runtime/methodobject.cs
index 8df9c8029..2531bc285 100644
--- a/src/runtime/methodobject.cs
+++ b/src/runtime/methodobject.cs
@@ -109,6 +109,17 @@ internal bool IsStatic()
return is_static;
}
+ private void ClearMembers()
+ {
+ Runtime.XDecref(doc);
+ doc = IntPtr.Zero;
+ if (unbound != null)
+ {
+ Runtime.XDecref(unbound.pyHandle);
+ unbound = null;
+ }
+ }
+
///
/// Descriptor __getattribute__ implementation.
///
@@ -196,12 +207,15 @@ public static IntPtr tp_repr(IntPtr ob)
public new static void tp_dealloc(IntPtr ob)
{
var self = (MethodObject)GetManagedObject(ob);
- Runtime.XDecref(self.doc);
- if (self.unbound != null)
- {
- Runtime.XDecref(self.unbound.pyHandle);
- }
+ self.ClearMembers();
ExtensionType.FinalizeObject(self);
}
+
+ public new static int tp_clear(IntPtr ob)
+ {
+ var self = (MethodObject)GetManagedObject(ob);
+ self.ClearMembers();
+ return ExtensionType.tp_clear(ob);
+ }
}
}
diff --git a/src/runtime/methodwrapper.cs b/src/runtime/methodwrapper.cs
index 2f3ce3ef2..ebcea0709 100644
--- a/src/runtime/methodwrapper.cs
+++ b/src/runtime/methodwrapper.cs
@@ -8,10 +8,11 @@ namespace Python.Runtime
/// currently used mainly to implement special cases like the CLR
/// import hook.
///
- internal class MethodWrapper
+ internal class MethodWrapper : IDisposable
{
public IntPtr mdef;
public IntPtr ptr;
+ private bool _disposed = false;
public MethodWrapper(Type type, string name, string funcType = null)
{
@@ -27,9 +28,32 @@ public MethodWrapper(Type type, string name, string funcType = null)
ptr = Runtime.PyCFunction_NewEx(mdef, IntPtr.Zero, IntPtr.Zero);
}
+ ~MethodWrapper()
+ {
+ Dispose();
+ }
+
public IntPtr Call(IntPtr args, IntPtr kw)
{
return Runtime.PyCFunction_Call(ptr, args, kw);
}
+
+ public void Dispose()
+ {
+ if (_disposed)
+ {
+ return;
+ }
+ using (Py.GIL())
+ {
+ _disposed = true;
+ Runtime.XDecref(ptr);
+ if (mdef != IntPtr.Zero)
+ {
+ Runtime.PyMem_Free(mdef);
+ mdef = IntPtr.Zero;
+ }
+ }
+ }
}
}
diff --git a/src/runtime/moduleobject.cs b/src/runtime/moduleobject.cs
index 7a45c6c81..23488f676 100644
--- a/src/runtime/moduleobject.cs
+++ b/src/runtime/moduleobject.cs
@@ -300,6 +300,45 @@ public static IntPtr tp_repr(IntPtr ob)
var self = (ModuleObject)GetManagedObject(ob);
return Runtime.PyString_FromString($"");
}
+
+ public new static void tp_dealloc(IntPtr ob)
+ {
+ var self = (ModuleObject)GetManagedObject(ob);
+ Runtime.XDecref(self.dict);
+ self.dict = IntPtr.Zero;
+ foreach (var attr in self.cache.Values)
+ {
+ Runtime.XDecref(attr.pyHandle);
+ }
+ self.cache.Clear();
+ ExtensionType.tp_dealloc(ob);
+ }
+
+ public static int tp_traverse(IntPtr ob, IntPtr visit, IntPtr arg)
+ {
+ var self = (ModuleObject)GetManagedObject(ob);
+ int res = PyVisit(self.dict, visit, arg);
+ if (res != 0) return res;
+ foreach (var attr in self.cache.Values)
+ {
+ res = PyVisit(attr.pyHandle, visit, arg);
+ if (res != 0) return res;
+ }
+ return 0;
+ }
+
+ public new static int tp_clear(IntPtr ob)
+ {
+ var self = (ModuleObject)GetManagedObject(ob);
+ Runtime.XDecref(self.dict);
+ self.dict = IntPtr.Zero;
+ foreach (var attr in self.cache.Values)
+ {
+ Runtime.XDecref(attr.pyHandle);
+ }
+ self.cache.Clear();
+ return ExtensionType.tp_clear(ob);
+ }
}
///
diff --git a/src/runtime/pythonengine.cs b/src/runtime/pythonengine.cs
index c1b663d22..cf4482c85 100644
--- a/src/runtime/pythonengine.cs
+++ b/src/runtime/pythonengine.cs
@@ -12,6 +12,8 @@ namespace Python.Runtime
///
public class PythonEngine : IDisposable
{
+ public static bool SoftShutdown { get; private set; }
+
private static DelegateManager delegateManager;
private static bool initialized;
private static IntPtr _pythonHome = IntPtr.Zero;
@@ -140,9 +142,9 @@ public static void Initialize()
Initialize(setSysArgv: true);
}
- public static void Initialize(bool setSysArgv = true, bool initSigs = false)
+ public static void Initialize(bool setSysArgv = true, bool initSigs = false, bool softShutdown = false)
{
- Initialize(Enumerable.Empty(), setSysArgv: setSysArgv, initSigs: initSigs);
+ Initialize(Enumerable.Empty(), setSysArgv: setSysArgv, initSigs: initSigs, softShutdown);
}
///
@@ -155,35 +157,40 @@ public static void Initialize(bool setSysArgv = true, bool initSigs = false)
/// 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)
+ public static void Initialize(IEnumerable args, bool setSysArgv = true, bool initSigs = false, bool softShutdown = false)
{
- if (!initialized)
+ if (initialized)
{
- // Creating the delegateManager MUST happen before Runtime.Initialize
- // is called. If it happens afterwards, DelegateManager's CodeGenerator
- // throws an exception in its ctor. This exception is eaten somehow
- // 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);
- initialized = true;
- Exceptions.Clear();
-
- // Make sure we clean up properly on app domain unload.
- AppDomain.CurrentDomain.DomainUnload += OnDomainUnload;
-
- // Remember to shut down the runtime.
- AddShutdownHandler(Runtime.Shutdown);
-
- // The global scope gets used implicitly quite early on, remember
- // to clear it out when we shut down.
- AddShutdownHandler(PyScopeManager.Global.Clear);
-
- if (setSysArgv)
- {
- Py.SetArgv(args);
- }
+ return;
+ }
+ // Creating the delegateManager MUST happen before Runtime.Initialize
+ // is called. If it happens afterwards, DelegateManager's CodeGenerator
+ // throws an exception in its ctor. This exception is eaten somehow
+ // 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, softShutdown);
+ initialized = true;
+ SoftShutdown = softShutdown;
+ Exceptions.Clear();
+
+ // Make sure we clean up properly on app domain unload.
+ AppDomain.CurrentDomain.DomainUnload += OnDomainUnload;
+
+ // Remember to shut down the runtime.
+ AddShutdownHandler(() => Runtime.Shutdown(softShutdown));
+
+ // The global scope gets used implicitly quite early on, remember
+ // to clear it out when we shut down.
+ AddShutdownHandler(PyScopeManager.Global.Clear);
+
+ if (setSysArgv)
+ {
+ Py.SetArgv(args);
+ }
+ if (!softShutdown)
+ {
// register the atexit callback (this doesn't use Py_AtExit as the C atexit
// callbacks are called after python is fully finalized but the python ones
// are called while the python engine is still running).
@@ -191,46 +198,51 @@ public static void Initialize(IEnumerable args, bool setSysArgv = true,
"import atexit, clr\n" +
"atexit.register(clr._AtExit)\n";
PythonEngine.Exec(code);
+ }
- // Load the clr.py resource into the clr module
- IntPtr clr = Python.Runtime.ImportHook.GetCLRModule();
- IntPtr clr_dict = Runtime.PyModule_GetDict(clr);
+ // Load the clr.py resource into the clr module
+ IntPtr clr = Python.Runtime.ImportHook.GetCLRModule();
+ IntPtr clr_dict = Runtime.PyModule_GetDict(clr);
- var locals = new PyDict();
- try
+ var locals = new PyDict();
+ try
+ {
+ IntPtr module = Runtime.PyImport_AddModule("clr._extras");
+ IntPtr module_globals = Runtime.PyModule_GetDict(module);
+ IntPtr builtins = Runtime.PyEval_GetBuiltins();
+ Runtime.PyDict_SetItemString(module_globals, "__builtins__", builtins);
+
+ Assembly assembly = Assembly.GetExecutingAssembly();
+ Stream stream = assembly.GetManifestResourceStream("clr.py");
+ if (stream == null)
{
- IntPtr module = Runtime.PyImport_AddModule("clr._extras");
- IntPtr module_globals = Runtime.PyModule_GetDict(module);
- IntPtr builtins = Runtime.PyEval_GetBuiltins();
- Runtime.PyDict_SetItemString(module_globals, "__builtins__", builtins);
-
- Assembly assembly = Assembly.GetExecutingAssembly();
- using (Stream stream = assembly.GetManifestResourceStream("clr.py"))
- using (var reader = new StreamReader(stream))
- {
- // add the contents of clr.py to the module
- string clr_py = reader.ReadToEnd();
- Exec(clr_py, module_globals, locals.Handle);
- }
+ stream = File.OpenRead(@"I:\repos\pythonnet\src\runtime\resources\clr.py");
+ }
+ using (stream)
+ using (var reader = new StreamReader(stream))
+ {
+ // add the contents of clr.py to the module
+ string clr_py = reader.ReadToEnd();
+ Exec(clr_py, module_globals, locals.Handle);
+ }
- // 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);
- foreach (PyObject key in locals.Keys())
+ // 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);
+ foreach (PyObject key in locals.Keys())
+ {
+ if (!key.ToString().StartsWith("_") || key.ToString().Equals("__version__"))
{
- if (!key.ToString().StartsWith("_") || key.ToString().Equals("__version__"))
- {
- PyObject value = locals[key];
- Runtime.PyDict_SetItem(clr_dict, key.Handle, value.Handle);
- value.Dispose();
- }
- key.Dispose();
+ PyObject value = locals[key];
+ Runtime.PyDict_SetItem(clr_dict, key.Handle, value.Handle);
+ value.Dispose();
}
+ key.Dispose();
}
- finally
- {
- locals.Dispose();
- }
+ }
+ finally
+ {
+ locals.Dispose();
}
}
diff --git a/src/runtime/pythonexception.cs b/src/runtime/pythonexception.cs
index 295a63b3d..8a6a24799 100644
--- a/src/runtime/pythonexception.cs
+++ b/src/runtime/pythonexception.cs
@@ -1,4 +1,5 @@
using System;
+using System.Runtime.CompilerServices;
namespace Python.Runtime
{
@@ -190,5 +191,21 @@ public static bool Matches(IntPtr ob)
{
return Runtime.PyErr_ExceptionMatches(ob) != 0;
}
+
+ public static void ThrowIfIsNull(IntPtr ob)
+ {
+ if (ob == IntPtr.Zero)
+ {
+ throw new PythonException();
+ }
+ }
+
+ public static void ThrowIfIsNotZero(int value)
+ {
+ if (value != 0)
+ {
+ throw new PythonException();
+ }
+ }
}
}
diff --git a/src/runtime/runtime.cs b/src/runtime/runtime.cs
index 75f11492f..fdccdf5ab 100644
--- a/src/runtime/runtime.cs
+++ b/src/runtime/runtime.cs
@@ -165,22 +165,27 @@ public class Runtime
///
internal static readonly Encoding PyEncoding = _UCS == 2 ? Encoding.Unicode : Encoding.UTF32;
+ private static PyReferenceCollection _typeRefs;
+
///
/// Initialize the runtime...
///
- internal static void Initialize(bool initSigs = false)
+ internal static void Initialize(bool initSigs = false, bool softShutdown = false)
{
if (Py_IsInitialized() == 0)
{
Py_InitializeEx(initSigs ? 1 : 0);
+ if (PyEval_ThreadsInitialized() == 0)
+ {
+ PyEval_InitThreads();
+ }
+ if (softShutdown)
+ {
+ RuntimeState.Save();
+ }
MainManagedThreadId = Thread.CurrentThread.ManagedThreadId;
}
- if (PyEval_ThreadsInitialized() == 0)
- {
- PyEval_InitThreads();
- }
-
IsFinalizing = false;
CLRModule.Reset();
@@ -202,19 +207,22 @@ internal static void Initialize(bool initSigs = false)
dict = PyImport_GetModuleDict();
op = PyDict_GetItemString(dict, "__builtin__");
}
- PyNotImplemented = PyObject_GetAttrString(op, "NotImplemented");
- PyBaseObjectType = PyObject_GetAttrString(op, "object");
- PyNone = PyObject_GetAttrString(op, "None");
- PyTrue = PyObject_GetAttrString(op, "True");
- PyFalse = PyObject_GetAttrString(op, "False");
+ _typeRefs = new PyReferenceCollection();
+ SetPyMember(ref PyNotImplemented, PyObject_GetAttrString(op, "NotImplemented"));
- PyBoolType = PyObject_Type(PyTrue);
- PyNoneType = PyObject_Type(PyNone);
- PyTypeType = PyObject_Type(PyNoneType);
+ SetPyMember(ref PyBaseObjectType, PyObject_GetAttrString(op, "object"));
+
+ SetPyMember(ref PyNone, PyObject_GetAttrString(op, "None"));
+ SetPyMember(ref PyTrue, PyObject_GetAttrString(op, "True"));
+ SetPyMember(ref PyFalse, PyObject_GetAttrString(op, "False"));
+
+ SetPyMember(ref PyBoolType, PyObject_Type(PyTrue));
+ SetPyMember(ref PyNoneType, PyObject_Type(PyNone));
+ SetPyMember(ref PyTypeType, PyObject_Type(PyNoneType));
op = PyObject_GetAttrString(dict, "keys");
- PyMethodType = PyObject_Type(op);
+ SetPyMember(ref PyMethodType, PyObject_Type(op));
XDecref(op);
// For some arcane reason, builtins.__dict__.__setitem__ is *not*
@@ -222,49 +230,53 @@ internal static void Initialize(bool initSigs = false)
//
// object.__init__ seems safe, though.
op = PyObject_GetAttrString(PyBaseObjectType, "__init__");
- PyWrapperDescriptorType = PyObject_Type(op);
+ SetPyMember(ref PyWrapperDescriptorType, PyObject_Type(op));
XDecref(op);
+ op = PyDict_GetItemString(dict, "KeyError");
+ XIncref(op);
+ SetPyMember(ref PyExc_KeyError, op);
+
#if PYTHON3
XDecref(dict);
#endif
op = PyString_FromString("string");
- PyStringType = PyObject_Type(op);
+ SetPyMember(ref PyStringType, PyObject_Type(op));
XDecref(op);
op = PyUnicode_FromString("unicode");
- PyUnicodeType = PyObject_Type(op);
+ SetPyMember(ref PyUnicodeType, PyObject_Type(op));
XDecref(op);
#if PYTHON3
op = PyBytes_FromString("bytes");
- PyBytesType = PyObject_Type(op);
+ SetPyMember(ref PyBytesType, PyObject_Type(op));
XDecref(op);
#endif
op = PyTuple_New(0);
- PyTupleType = PyObject_Type(op);
+ SetPyMember(ref PyTupleType, PyObject_Type(op));
XDecref(op);
op = PyList_New(0);
- PyListType = PyObject_Type(op);
+ SetPyMember(ref PyListType, PyObject_Type(op));
XDecref(op);
op = PyDict_New();
- PyDictType = PyObject_Type(op);
+ SetPyMember(ref PyDictType, PyObject_Type(op));
XDecref(op);
op = PyInt_FromInt32(0);
- PyIntType = PyObject_Type(op);
+ SetPyMember(ref PyIntType, PyObject_Type(op));
XDecref(op);
op = PyLong_FromLong(0);
- PyLongType = PyObject_Type(op);
+ SetPyMember(ref PyLongType, PyObject_Type(op));
XDecref(op);
op = PyFloat_FromDouble(0);
- PyFloatType = PyObject_Type(op);
+ SetPyMember(ref PyFloatType, PyObject_Type(op));
XDecref(op);
#if PYTHON3
@@ -275,10 +287,10 @@ internal static void Initialize(bool initSigs = false)
IntPtr d = PyDict_New();
IntPtr c = PyClass_New(IntPtr.Zero, d, s);
- PyClassType = PyObject_Type(c);
+ SetPyObject(ref PyClassType, PyObject_Type(c));
IntPtr i = PyInstance_New(c, IntPtr.Zero, IntPtr.Zero);
- PyInstanceType = PyObject_Type(i);
+ SetPyObject(ref PyInstanceType, PyObject_Type(i));
XDecref(s);
XDecref(i);
@@ -310,7 +322,7 @@ internal static void Initialize(bool initSigs = false)
// Initialize modules that depend on the runtime class.
AssemblyManager.Initialize();
- PyCLRMetaType = MetaType.Initialize();
+ SetPyMember(ref PyCLRMetaType, MetaType.Initialize());
Exceptions.Initialize();
ImportHook.Initialize();
@@ -371,12 +383,65 @@ private static void InitializePlatformData()
Machine = MType;
}
- internal static void Shutdown()
+ internal static void Shutdown(bool soft)
{
+ if (Py_IsInitialized() == 0)
+ {
+ return;
+ }
AssemblyManager.Shutdown();
Exceptions.Shutdown();
ImportHook.Shutdown();
Finalizer.Shutdown();
+
+ ClearClrModules();
+ ClassManager.RemoveClasses();
+ TypeManager.RemoveTypes();
+ RemoveClrRootModule();
+
+ {
+ //var intialModules = PySys_GetObject("initial_modules");
+ //var modules = PyImport_GetModuleDict();
+ //foreach (var name in RuntimeState.GetModuleNames())
+ //{
+ // if (PySet_Contains(intialModules, name) == 1)
+ // {
+ // continue;
+ // }
+ // var module = PyDict_GetItem(modules, name);
+ // IntPtr clr_dict = Runtime._PyObject_GetDictPtr(module); // PyObject**
+ // clr_dict = (IntPtr)Marshal.PtrToStructure(clr_dict, typeof(IntPtr));
+ // PyDict_Clear(clr_dict);
+ // PyDict_DelItem(modules, name);
+ //}
+ }
+ if (soft)
+ {
+ PyGC_Collect();
+ RemoveClrObjects();
+ RuntimeState.Restore();
+ //var objs = ManagedType.GetManagedObjects();
+ //var subIterp = PyThreadState_Swap(_originIterp);
+ //var iterp = PyThreadState_Get();
+ //Py_EndInterpreter(iterp);
+ //PyGILState_Ensure();
+ ResetPyMembers();
+// Runtime.PyRun_SimpleString(@"
+//import gc, pprint, objgraph
+//print()
+//with open('R:/objsx.log', 'w', encoding='utf8') as f:
+// pprint.pprint(objgraph.show_growth(), f)
+// #pprint.pprint(objgraph.typestats(), f)
+
+//#lst = gc.get_objects()
+//#with open('R:/objs.log', 'w', encoding='utf8') as f:
+// # for obj in lst:
+// # pprint.pprint(obj, f)
+//#del lst
+//");
+ return;
+ }
+ ResetPyMembers();
Py_Finalize();
}
@@ -390,6 +455,69 @@ internal static int AtExit()
return 0;
}
+ private static void SetPyMember(ref IntPtr obj, IntPtr value)
+ {
+ PythonException.ThrowIfIsNull(value);
+ obj = value;
+ // XXX: Is it safe for all platforms?
+ unsafe
+ {
+ fixed (void* p = &obj)
+ {
+ _typeRefs.Add((IntPtr)p);
+ }
+ }
+ }
+
+ private static void ResetPyMembers()
+ {
+ _typeRefs.Release();
+ _typeRefs = null;
+ }
+
+ private static void ClearClrModules()
+ {
+ var modules = PyImport_GetModuleDict();
+ var items = PyDict_Items(modules);
+ long length = PyList_Size(items);
+ for (long i = 0; i < length; i++)
+ {
+ var item = PyList_GetItem(items, i);
+ var name = PyTuple_GetItem(item, 0);
+ var module = PyTuple_GetItem(item, 1);
+ var clrModule = ManagedType.GetManagedObject(module);
+ if (clrModule != null)
+ {
+ PyDict_DelItem(modules, name);
+ }
+ }
+ XDecref(items);
+ }
+
+ private static void RemoveClrRootModule()
+ {
+ var modules = PyImport_GetModuleDict();
+ PyDictTryDelItem(modules, "CLR");
+ PyDictTryDelItem(modules, "clr");
+ PyDictTryDelItem(modules, "clr._extra");
+ }
+
+ private static void PyDictTryDelItem(IntPtr dict, string key)
+ {
+ if (PyDict_DelItemString(dict, key) == 0)
+ {
+ return;
+ }
+ if (!PythonException.Matches(PyExc_KeyError))
+ {
+ throw new PythonException();
+ }
+ }
+
+ private static void RemoveClrObjects()
+ {
+ }
+
internal static IntPtr Py_single_input = (IntPtr)256;
internal static IntPtr Py_file_input = (IntPtr)257;
internal static IntPtr Py_eval_input = (IntPtr)258;
@@ -432,6 +560,8 @@ internal static int AtExit()
internal static IntPtr PyNone;
internal static IntPtr Error;
+ internal static IntPtr PyExc_KeyError;
+
///
/// Check if any Python Exceptions occurred.
/// If any exist throw new PythonException.
@@ -1068,6 +1198,19 @@ internal static bool PyFloat_Check(IntPtr ob)
return PyObject_TYPE(ob) == PyFloatType;
}
+ ///
+ /// Return value: New reference.
+ /// Create a Python integer from the pointer p. The pointer value can be retrieved from the resulting value using PyLong_AsVoidPtr().
+ ///
+ [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern IntPtr PyLong_FromVoidPtr(IntPtr p);
+
+ ///
+ /// Convert a Python integer pylong to a C void pointer. If pylong cannot be converted, an OverflowError will be raised. This is only assured to produce a usable void pointer for values created with PyLong_FromVoidPtr().
+ ///
+ [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern IntPtr PyLong_AsVoidPtr(IntPtr ob);
+
[DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)]
internal static extern IntPtr PyFloat_FromDouble(double value);
@@ -1464,9 +1607,16 @@ internal static bool PyDict_Check(IntPtr ob)
[DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)]
internal static extern IntPtr PyDictProxy_New(IntPtr dict);
+ ///
+ /// Return value: Borrowed reference.
+ /// Return NULL if the key key is not present, but without setting an exception.
+ ///
[DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)]
internal static extern IntPtr PyDict_GetItem(IntPtr pointer, IntPtr key);
+ ///
+ /// Return value: Borrowed reference.
+ ///
[DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)]
internal static extern IntPtr PyDict_GetItemString(IntPtr pointer, string key);
@@ -1512,6 +1662,21 @@ internal static long PyDict_Size(IntPtr pointer)
internal static extern IntPtr _PyDict_Size(IntPtr pointer);
+ ///
+ /// Return value: New reference.
+ ///
+ [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern IntPtr PySet_New(IntPtr iterable);
+
+ [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern int PySet_Add(IntPtr set, IntPtr key);
+
+ ///
+ /// Return 1 if found, 0 if not found, and -1 if an error is encountered.
+ ///
+ [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern int PySet_Contains(IntPtr anyset, IntPtr key);
+
//====================================================================
// Python list API
//====================================================================
@@ -1532,6 +1697,10 @@ internal static IntPtr PyList_New(long size)
[DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)]
internal static extern IntPtr PyList_AsTuple(IntPtr pointer);
+ ///
+ /// Return value: Borrowed reference.
+ /// If index is out of bounds, return NULL and set an IndexError exception.
+ ///
internal static IntPtr PyList_GetItem(IntPtr pointer, long index)
{
return PyList_GetItem(pointer, new IntPtr(index));
@@ -1711,6 +1880,10 @@ int updatepath
);
#endif
+ ///
+ /// Return value: Borrowed reference.
+ /// Return the object name from the sys module or NULL if it does not exist, without setting an exception.
+ ///
[DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)]
internal static extern IntPtr PySys_GetObject(string name);
@@ -1750,6 +1923,9 @@ internal static IntPtr PyType_GenericAlloc(IntPtr type, long n)
[DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr PyType_GenericAlloc(IntPtr type, IntPtr n);
+ ///
+ /// Finalize a type object. This should be called on all type objects to finish their initialization. This function is responsible for adding inherited slots from a type’s base class. Return 0 on success, or return -1 and sets an exception on error.
+ ///
[DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)]
internal static extern int PyType_Ready(IntPtr type);
@@ -1777,6 +1953,8 @@ internal static IntPtr PyType_GenericAlloc(IntPtr type, long n)
[DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)]
internal static extern void PyObject_GC_UnTrack(IntPtr tp);
+ [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern void _PyObject_Dump(IntPtr ob);
//====================================================================
// Python memory API
@@ -1842,6 +2020,54 @@ internal static IntPtr PyMem_Realloc(IntPtr ptr, long size)
[DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)]
internal static extern void PyErr_Print();
+ //====================================================================
+ // 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;
+
+
+ [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern IntPtr PyGC_Collect();
+
+ internal static IntPtr _Py_AS_GC(IntPtr ob)
+ {
+ // XXX: PyGC_Head has a force alignment depend on platform.
+ // See PyGC_Head in objimpl.h for more details.
+ return Is32Bit ? ob - 16 : ob - 24;
+ }
+
+ internal static IntPtr _Py_FROM_GC(IntPtr gc)
+ {
+ return Is32Bit ? gc + 16 : gc + 24;
+ }
+
+ internal static IntPtr _PyGCHead_REFS(IntPtr gc)
+ {
+ unsafe
+ {
+ var pGC = (PyGC_Head*)gc;
+ var refs = pGC->gc.gc_refs;
+ if (Is32Bit)
+ {
+ return new IntPtr(refs.ToInt32() >> _PyGC_REFS_SHIFT);
+ }
+ return new IntPtr(refs.ToInt64() >> _PyGC_REFS_SHIFT);
+ }
+ }
+
+ internal static IntPtr _PyGC_REFS(IntPtr ob)
+ {
+ return _PyGCHead_REFS(_Py_AS_GC(ob));
+ }
+
+ internal static bool _PyObject_GC_IS_TRACKED(IntPtr ob)
+ {
+ return (long)_PyGC_REFS(ob) != _PyGC_REFS_UNTRACKED;
+ }
//====================================================================
// Miscellaneous
@@ -1859,4 +2085,34 @@ internal static IntPtr PyMem_Realloc(IntPtr ptr, long size)
[DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)]
internal static extern int Py_MakePendingCalls();
}
+
+
+ class PyReferenceCollection
+ {
+ public List Objects { get; private set; }
+
+ public PyReferenceCollection()
+ {
+ Objects = new List();
+ }
+
+ public void Add(IntPtr obj)
+ {
+ Objects.Add(obj);
+ }
+
+ public void Release()
+ {
+ foreach (var objRef in Objects)
+ {
+ unsafe
+ {
+ var p = (void**)objRef;
+ Runtime.XDecref((IntPtr)(*p));
+ *p = null;
+ }
+ }
+ Objects.Clear();
+ }
+ }
}
diff --git a/src/runtime/runtime_state.cs b/src/runtime/runtime_state.cs
new file mode 100644
index 000000000..69291453e
--- /dev/null
+++ b/src/runtime/runtime_state.cs
@@ -0,0 +1,242 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+using System.Linq;
+using static Python.Runtime.Runtime;
+
+namespace Python.Runtime
+{
+ class RuntimeState
+ {
+ public static void Save()
+ {
+ if (PySys_GetObject("dummy_gc") != IntPtr.Zero)
+ {
+ throw new Exception("Runtime State set already");
+ }
+
+ var modules = PySet_New(IntPtr.Zero);
+ foreach (var name in GetModuleNames())
+ {
+ int res = PySet_Add(modules, name);
+ PythonException.ThrowIfIsNotZero(res);
+ }
+
+ var objs = PySet_New(IntPtr.Zero);
+ foreach (var obj in PyGCGetObjects())
+ {
+ AddObjPtrToSet(objs, obj);
+ }
+
+ var dummyGCHead = PyMem_Malloc(Marshal.SizeOf(typeof(PyGC_Head)));
+ unsafe
+ {
+ var head = (PyGC_Head*)dummyGCHead;
+ head->gc.gc_next = dummyGCHead;
+ head->gc.gc_prev = dummyGCHead;
+ head->gc.gc_refs = IntPtr.Zero;
+ }
+ {
+ var pyDummyGC = PyLong_FromVoidPtr(dummyGCHead);
+ int res = PySys_SetObject("dummy_gc", pyDummyGC);
+ PythonException.ThrowIfIsNotZero(res);
+ XDecref(pyDummyGC);
+
+ AddObjPtrToSet(objs, modules);
+ try
+ {
+ res = PySys_SetObject("initial_modules", modules);
+ PythonException.ThrowIfIsNotZero(res);
+ }
+ finally
+ {
+ XDecref(modules);
+ }
+
+ AddObjPtrToSet(objs, objs);
+ try
+ {
+ res = PySys_SetObject("initial_objs", objs);
+ PythonException.ThrowIfIsNotZero(res);
+ }
+ finally
+ {
+ XDecref(objs);
+ }
+
+ }
+ }
+
+ public static void Restore()
+ {
+ var dummyGCAddr = PySys_GetObject("dummy_gc");
+ if (dummyGCAddr == IntPtr.Zero)
+ {
+ throw new Exception("Runtime state have not set");
+ }
+ var dummyGC = PyLong_AsVoidPtr(dummyGCAddr);
+ //foreach (var abandomedNode in IterGCNodes(dummyGC))
+ //{
+ // if (abandomedNode == IntPtr.Zero)
+ // {
+ // continue;
+ // }
+ // //PyObject_GC_Del(_Py_FROM_GC(abandomedNode));
+ //}
+
+ //unsafe
+ //{
+ // var head = (PyGC_Head*)dummyGC;
+ // head->gc.gc_next = dummyGC;
+ // head->gc.gc_prev = dummyGC;
+ // head->gc.gc_refs = IntPtr.Zero;
+ //}
+
+ ResotreModules(dummyGC);
+ RestoreObjects(dummyGC);
+ foreach (var obj in IterObjects(dummyGC))
+ {
+ //if (ManagedType.IsManagedType(obj))
+ {
+ //var clrobj = ManagedType.GetManagedObject(obj);
+ //Console.WriteLine(clrobj);
+ }
+ }
+ //Console.WriteLine($"abndom {IterGCNodes(dummyGC).Count()}");
+ }
+
+ private static void ResotreModules(IntPtr dummyGC)
+ {
+ var intialModules = PySys_GetObject("initial_modules");
+ Debug.Assert(intialModules != null);
+ var modules = PyImport_GetModuleDict();
+ foreach (var name in GetModuleNames())
+ {
+ if (PySet_Contains(intialModules, name) == 1)
+ {
+ continue;
+ }
+ var module = PyDict_GetItem(modules, name);
+ if (_PyObject_GC_IS_TRACKED(module))
+ {
+ ExchangeGCChain(module, dummyGC);
+ }
+ PyDict_DelItem(modules, name);
+ }
+ }
+
+ private static void RestoreObjects(IntPtr dummyGC)
+ {
+ var intialObjs = PySys_GetObject("initial_objs");
+ Debug.Assert(intialObjs != null);
+ foreach (var obj in PyGCGetObjects())
+ {
+ var p = PyLong_FromVoidPtr(obj);
+ try
+ {
+ if (PySet_Contains(intialObjs, p) == 1)
+ {
+ continue;
+ }
+ }
+ finally
+ {
+ XDecref(p);
+ }
+ Debug.Assert(_PyObject_GC_IS_TRACKED(obj), "A GC object must be tracked");
+ ExchangeGCChain(obj, dummyGC);
+ }
+ }
+
+ public static IEnumerable PyGCGetObjects()
+ {
+ var gc = PyImport_ImportModule("gc");
+ var get_objects = PyObject_GetAttrString(gc, "get_objects");
+ var objs = PyObject_CallObject(get_objects, IntPtr.Zero);
+ var length = PyList_Size(objs);
+ for (long i = 0; i < length; i++)
+ {
+ var obj = PyList_GetItem(objs, i);
+ yield return obj;
+ }
+ XDecref(objs);
+ XDecref(gc);
+ }
+
+ public static IEnumerable GetModuleNames()
+ {
+ var modules = PyImport_GetModuleDict();
+ var names = PyDict_Keys(modules);
+ var length = PyList_Size(names);
+ for (int i = 0; i < length; i++)
+ {
+ var name = PyList_GetItem(names, i);
+ yield return name;
+ }
+ }
+
+ private static void AddObjPtrToSet(IntPtr set, IntPtr obj)
+ {
+ var p = PyLong_FromVoidPtr(obj);
+ XIncref(obj);
+ try
+ {
+ int res = PySet_Add(set, p);
+ PythonException.ThrowIfIsNotZero(res);
+ }
+ finally
+ {
+ XDecref(p);
+ }
+ }
+ ///
+ /// Exchange gc to a dummy gc prevent nullptr error in _PyObject_GC_UnTrack macro.
+ ///
+ private static void ExchangeGCChain(IntPtr obj, IntPtr gc)
+ {
+ var head = _Py_AS_GC(obj);
+ if ((long)_PyGCHead_REFS(head) == _PyGC_REFS_UNTRACKED)
+ {
+ throw new Exception("GC object untracked");
+ }
+ unsafe
+ {
+ var g = (PyGC_Head*)head;
+ var newGCGen = (PyGC_Head*)gc;
+
+ ((PyGC_Head*)g->gc.gc_prev)->gc.gc_next = g->gc.gc_next;
+ ((PyGC_Head*)g->gc.gc_next)->gc.gc_prev = g->gc.gc_prev;
+
+ g->gc.gc_next = gc;
+ g->gc.gc_prev = newGCGen->gc.gc_prev;
+ ((PyGC_Head*)g->gc.gc_prev)->gc.gc_next = head;
+ newGCGen->gc.gc_prev = head;
+ }
+ }
+
+ private static IEnumerable IterGCNodes(IntPtr gc)
+ {
+ var node = GetNextGCNode(gc);
+ while (node != gc)
+ {
+ var next = GetNextGCNode(node);
+ yield return node;
+ node = next;
+ }
+ }
+
+ private static IEnumerable IterObjects(IntPtr gc)
+ {
+ foreach (var node in IterGCNodes(gc))
+ {
+ yield return _Py_FROM_GC(node);
+ }
+ }
+
+ private static unsafe IntPtr GetNextGCNode(IntPtr node)
+ {
+ return ((PyGC_Head*)node)->gc.gc_next;
+ }
+ }
+}
diff --git a/src/runtime/typemanager.cs b/src/runtime/typemanager.cs
index 9a98e9ebb..f82260d37 100644
--- a/src/runtime/typemanager.cs
+++ b/src/runtime/typemanager.cs
@@ -4,6 +4,7 @@
using System.Reflection;
using System.Runtime.InteropServices;
using Python.Runtime.Platform;
+using System.Linq;
namespace Python.Runtime
{
@@ -20,7 +21,6 @@ internal class TypeManager
static TypeManager()
{
tbFlags = BindingFlags.Public | BindingFlags.Static;
- cache = new Dictionary(128);
}
public static void Reset()
@@ -28,7 +28,22 @@ public static void Reset()
cache = new Dictionary(128);
}
+ public static IList GetManagedTypes()
+ {
+ return cache.Values.ToArray();
+ }
+
+ internal static void RemoveTypes()
+ {
+ foreach (var tpHandle in cache.Values)
+ {
+ Runtime.XDecref(tpHandle);
+ }
+ cache.Clear();
+ }
+
///
+ /// Return value: Borrowed reference.
/// Given a managed Type derived from ExtensionType, get the handle to
/// a Python type object that delegates its implementation to the Type
/// object. These Python type instances are used to implement internal
@@ -94,11 +109,15 @@ internal static IntPtr CreateType(Type impl)
TypeFlags.HeapType | TypeFlags.HaveGC;
Util.WriteCLong(type, TypeOffset.tp_flags, flags);
- Runtime.PyType_Ready(type);
+ if (Runtime.PyType_Ready(type) != 0)
+ {
+ throw new PythonException();
+ }
IntPtr dict = Marshal.ReadIntPtr(type, TypeOffset.tp_dict);
IntPtr mod = Runtime.PyString_FromString("CLR");
Runtime.PyDict_SetItemString(dict, "__module__", mod);
+ Runtime.XDecref(mod);
InitMethods(type, impl);
@@ -172,20 +191,23 @@ internal static IntPtr CreateType(ManagedType impl, Type clrType)
// that the type of the new type must PyType_Type at the time we
// call this, else PyType_Ready will skip some slot initialization.
- Runtime.PyType_Ready(type);
+ if (Runtime.PyType_Ready(type) != 0)
+ {
+ throw new PythonException();
+ }
IntPtr dict = Marshal.ReadIntPtr(type, TypeOffset.tp_dict);
string mn = clrType.Namespace ?? "";
IntPtr mod = Runtime.PyString_FromString(mn);
Runtime.PyDict_SetItemString(dict, "__module__", mod);
+ Runtime.XDecref(mod);
// Hide the gchandle of the implementation in a magic type slot.
- GCHandle gc = GCHandle.Alloc(impl);
+ GCHandle gc = impl.AllocGCHandle();
Marshal.WriteIntPtr(type, TypeOffset.magic(), (IntPtr)gc);
// Set the handle attributes on the implementing instance.
- impl.tpHandle = Runtime.PyCLRMetaType;
- impl.gcHandle = gc;
+ impl.tpHandle = type;
impl.pyHandle = type;
//DebugUtil.DumpType(type);
@@ -309,17 +331,17 @@ internal static IntPtr CreateMetaType(Type impl)
Marshal.WriteIntPtr(type, TypeOffset.tp_base, py_type);
Runtime.XIncref(py_type);
+ // Slots will inherit from TypeType, it's not neccesary for setting them.
// Copy gc and other type slots from the base Python metatype.
+ //CopySlot(py_type, type, TypeOffset.tp_basicsize);
+ //CopySlot(py_type, type, TypeOffset.tp_itemsize);
- CopySlot(py_type, type, TypeOffset.tp_basicsize);
- CopySlot(py_type, type, TypeOffset.tp_itemsize);
-
- CopySlot(py_type, type, TypeOffset.tp_dictoffset);
- CopySlot(py_type, type, TypeOffset.tp_weaklistoffset);
+ //CopySlot(py_type, type, TypeOffset.tp_dictoffset);
+ //CopySlot(py_type, type, TypeOffset.tp_weaklistoffset);
- CopySlot(py_type, type, TypeOffset.tp_traverse);
- CopySlot(py_type, type, TypeOffset.tp_clear);
- CopySlot(py_type, type, TypeOffset.tp_is_gc);
+ //CopySlot(py_type, type, TypeOffset.tp_traverse);
+ //CopySlot(py_type, type, TypeOffset.tp_clear);
+ //CopySlot(py_type, type, TypeOffset.tp_is_gc);
// Override type slots with those of the managed implementation.
@@ -352,7 +374,10 @@ internal static IntPtr CreateMetaType(Type impl)
Marshal.WriteIntPtr(type, TypeOffset.tp_methods, mdefStart);
- Runtime.PyType_Ready(type);
+ if (Runtime.PyType_Ready(type) != 0)
+ {
+ throw new PythonException();
+ }
IntPtr dict = Marshal.ReadIntPtr(type, TypeOffset.tp_dict);
IntPtr mod = Runtime.PyString_FromString("CLR");
@@ -394,7 +419,10 @@ internal static IntPtr BasicSubType(string name, IntPtr base_, Type impl)
InitializeSlots(type, impl);
- Runtime.PyType_Ready(type);
+ if (Runtime.PyType_Ready(type) != 0)
+ {
+ throw new PythonException();
+ }
IntPtr tp_dict = Marshal.ReadIntPtr(type, TypeOffset.tp_dict);
IntPtr mod = Runtime.PyString_FromString("CLR");
@@ -738,9 +766,10 @@ internal static void InitializeSlots(IntPtr type, Type impl)
ret0 = NativeCodePage + native.Return0;
}
- InitializeSlot(type, ret0, "tp_traverse");
- InitializeSlot(type, ret0, "tp_clear");
- InitializeSlot(type, ret1, "tp_is_gc");
+ bool canOverride = !PythonEngine.SoftShutdown;
+ InitializeSlot(type, ret0, "tp_traverse", canOverride);
+ InitializeSlot(type, ret0, "tp_clear", canOverride);
+ // InitializeSlot(type, ret1, "tp_is_gc");
}
static int Return1(IntPtr _) => 1;
@@ -757,12 +786,17 @@ internal static void InitializeSlots(IntPtr type, Type impl)
/// Type being initialized.
/// Function pointer.
/// Name of the method.
- static void InitializeSlot(IntPtr type, IntPtr slot, string name)
+ /// Can override the slot when it existed
+ static void InitializeSlot(IntPtr type, IntPtr slot, string name, bool canOverride = true)
{
Type typeOffset = typeof(TypeOffset);
FieldInfo fi = typeOffset.GetField(name);
var offset = (int)fi.GetValue(typeOffset);
+ if (!canOverride && Marshal.ReadIntPtr(type, offset) != IntPtr.Zero)
+ {
+ return;
+ }
Marshal.WriteIntPtr(type, offset, slot);
}
From 657452e7d4b207612862d53ab647b8afd7906db7 Mon Sep 17 00:00:00 2001
From: amos402
Date: Thu, 19 Sep 2019 23:48:56 +0800
Subject: [PATCH 022/998] * Fix refcnt error * Keep delegate of Thunk in
tp_dict * Remove useless code
---
src/runtime/Python.Runtime.csproj | 19 ++--
src/runtime/classmanager.cs | 5 +-
src/runtime/extensiontype.cs | 4 +-
src/runtime/importhook.cs | 3 +-
src/runtime/interop.cs | 38 ++++----
src/runtime/methodwrapper.cs | 27 +++---
src/runtime/pythonengine.cs | 7 +-
src/runtime/runtime.cs | 122 ++++++++++--------------
src/runtime/runtime_state.cs | 26 ------
src/runtime/typemanager.cs | 149 +++++++++++++++++++++++++-----
10 files changed, 221 insertions(+), 179 deletions(-)
diff --git a/src/runtime/Python.Runtime.csproj b/src/runtime/Python.Runtime.csproj
index ac6b59150..1f3a0e392 100644
--- a/src/runtime/Python.Runtime.csproj
+++ b/src/runtime/Python.Runtime.csproj
@@ -1,4 +1,4 @@
-
+
Debug
@@ -34,7 +34,7 @@
pdbonly
- PYTHON3;PYTHON37;UCS4
+ PYTHON3;PYTHON37;UCS4
true
pdbonly
@@ -46,7 +46,7 @@
true
- PYTHON3;PYTHON37;UCS4;TRACE;DEBUG
+ PYTHON3;PYTHON37;UCS4;TRACE;DEBUG
false
full
@@ -56,7 +56,7 @@
pdbonly
- PYTHON3;PYTHON37;UCS2
+ PYTHON3;PYTHON37;UCS2
true
pdbonly
@@ -68,7 +68,7 @@
true
- PYTHON3;PYTHON37;UCS2;TRACE;DEBUG
+ PYTHON3;PYTHON37;UCS2;TRACE;DEBUG
false
full
@@ -137,11 +137,12 @@
+
-
-
+
+
@@ -151,7 +152,7 @@
-
+
@@ -170,4 +171,4 @@
-
+
\ No newline at end of file
diff --git a/src/runtime/classmanager.cs b/src/runtime/classmanager.cs
index 49b732910..3343371f1 100644
--- a/src/runtime/classmanager.cs
+++ b/src/runtime/classmanager.cs
@@ -56,7 +56,6 @@ internal static void RemoveClasses()
cls.TypeTraverse(OnVisit, visitedPtr);
// XXX: Force release some resouces.
cls.TypeClear();
- //Runtime.XDecref(cls.pyHandle);
}
}
finally
@@ -189,8 +188,7 @@ private static void InitClassBase(Type type, ClassBase impl)
var name = (string)iter.Key;
Runtime.PyDict_SetItemString(dict, name, item.pyHandle);
// info.members are already useless
- Runtime.XDecref(item.pyHandle);
- item.pyHandle = IntPtr.Zero;
+ item.DecrRefCount();
}
// If class has constructors, generate an __doc__ attribute.
@@ -225,6 +223,7 @@ private static void InitClassBase(Type type, ClassBase impl)
// TODO: deprecate __overloads__ soon...
Runtime.PyDict_SetItemString(dict, "__overloads__", ctors.pyHandle);
Runtime.PyDict_SetItemString(dict, "Overloads", ctors.pyHandle);
+ ctors.DecrRefCount();
}
// don't generate the docstring if one was already set from a DocStringAttribute.
diff --git a/src/runtime/extensiontype.cs b/src/runtime/extensiontype.cs
index 5e8ee6b59..634b1d1a5 100644
--- a/src/runtime/extensiontype.cs
+++ b/src/runtime/extensiontype.cs
@@ -38,6 +38,7 @@ public ExtensionType()
Runtime.PyObject_GC_UnTrack(py);
+ // Steals a ref to tpHandle.
tpHandle = tp;
pyHandle = py;
}
@@ -49,7 +50,8 @@ public ExtensionType()
public static void FinalizeObject(ManagedType self)
{
Runtime.PyObject_GC_Del(self.pyHandle);
- Runtime.XDecref(self.tpHandle);
+ // Not necessary decref for `tpHandle`.
+ // Runtime.XDecref(self.tpHandle);
self.FreeGCHandle();
}
diff --git a/src/runtime/importhook.cs b/src/runtime/importhook.cs
index ef322648a..22443c9ad 100644
--- a/src/runtime/importhook.cs
+++ b/src/runtime/importhook.cs
@@ -43,7 +43,6 @@ internal static void Initialize()
py_import = Runtime.PyObject_GetAttrString(mod, "__import__");
hook = new MethodWrapper(typeof(ImportHook), "__import__", "TernaryFunc");
Runtime.PyObject_SetAttrString(mod, "__import__", hook.ptr);
- Runtime.XDecref(hook.ptr);
root = new CLRModule();
@@ -88,7 +87,7 @@ internal static void Shutdown()
Runtime.XDecref(root.pyHandle);
root = null;
- hook.Dispose();
+ hook.Release();
hook = null;
Runtime.XDecref(py_import);
diff --git a/src/runtime/interop.cs b/src/runtime/interop.cs
index 702bb7982..7b03c58a2 100644
--- a/src/runtime/interop.cs
+++ b/src/runtime/interop.cs
@@ -335,7 +335,6 @@ internal class TypeFlags
internal class Interop
{
- private static Dictionary, Tuple> keepAlive;
private static Hashtable pmap;
static Interop()
@@ -352,8 +351,6 @@ static Interop()
p[item.Name] = item;
}
- keepAlive = new Dictionary, Tuple>();
- //Marshal.AllocHGlobal(IntPtr.Size);
pmap = new Hashtable();
pmap["tp_dealloc"] = p["DestructorFunc"];
@@ -450,24 +447,12 @@ internal static Type GetPrototype(string name)
return pmap[name] as Type;
}
- internal static IntPtr GetThunk(MethodInfo method, string funcType = null)
+ internal static ThunkInfo GetThunk(MethodInfo method, string funcType = null)
{
- var key = new KeyValuePair(method, funcType);
- Tuple thunkPair;
- if (keepAlive.TryGetValue(key, out thunkPair))
- {
- return thunkPair.Item2;
- }
- thunkPair = GetThunkImpl(method, funcType);
- if (thunkPair == null)
- {
- return IntPtr.Zero;
- }
- keepAlive[key] = thunkPair;
- return thunkPair.Item2;
+ return GetThunkImpl(method, funcType);
}
- private static Tuple GetThunkImpl(MethodInfo method, string funcType)
+ private static ThunkInfo GetThunkImpl(MethodInfo method, string funcType)
{
Type dt;
if (funcType != null)
@@ -477,7 +462,7 @@ private static Tuple GetThunkImpl(MethodInfo method, string fu
if (dt == null)
{
- return null;
+ return ThunkInfo.Empty;
}
IntPtr tmp = Marshal.AllocHGlobal(IntPtr.Size);
Delegate d = Delegate.CreateDelegate(dt, method);
@@ -485,7 +470,7 @@ private static Tuple GetThunkImpl(MethodInfo method, string fu
Marshal.StructureToPtr(cb, tmp, false);
IntPtr fp = Marshal.ReadIntPtr(tmp, 0);
Marshal.FreeHGlobal(tmp);
- return new Tuple(d, fp);
+ return new ThunkInfo(d, fp);
}
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
@@ -540,6 +525,19 @@ public Thunk(Delegate d)
}
}
+ internal struct ThunkInfo
+ {
+ public Delegate Target;
+ public IntPtr Address;
+
+ public static readonly ThunkInfo Empty = new ThunkInfo(null, IntPtr.Zero);
+
+ public ThunkInfo(Delegate target, IntPtr addr)
+ {
+ Target = target;
+ Address = addr;
+ }
+ }
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
struct PyGC_Node
diff --git a/src/runtime/methodwrapper.cs b/src/runtime/methodwrapper.cs
index ebcea0709..214a48dc6 100644
--- a/src/runtime/methodwrapper.cs
+++ b/src/runtime/methodwrapper.cs
@@ -8,51 +8,46 @@ namespace Python.Runtime
/// currently used mainly to implement special cases like the CLR
/// import hook.
///
- internal class MethodWrapper : IDisposable
+ internal class MethodWrapper
{
public IntPtr mdef;
public IntPtr ptr;
+ public ThunkInfo _thunk;
private bool _disposed = false;
public MethodWrapper(Type type, string name, string funcType = null)
{
// Turn the managed method into a function pointer
- IntPtr fp = Interop.GetThunk(type.GetMethod(name), funcType);
+ _thunk = Interop.GetThunk(type.GetMethod(name), funcType);
// Allocate and initialize a PyMethodDef structure to represent
// the managed method, then create a PyCFunction.
mdef = Runtime.PyMem_Malloc(4 * IntPtr.Size);
- TypeManager.WriteMethodDef(mdef, name, fp, 0x0003);
+ TypeManager.WriteMethodDef(mdef, name, _thunk.Address, 0x0003);
ptr = Runtime.PyCFunction_NewEx(mdef, IntPtr.Zero, IntPtr.Zero);
}
- ~MethodWrapper()
- {
- Dispose();
- }
public IntPtr Call(IntPtr args, IntPtr kw)
{
return Runtime.PyCFunction_Call(ptr, args, kw);
}
- public void Dispose()
+ public void Release()
{
if (_disposed)
{
return;
}
- using (Py.GIL())
+ _disposed = true;
+ bool freeDef = Runtime.Refcount(ptr) == 1;
+ Runtime.XDecref(ptr);
+ if (freeDef && mdef != IntPtr.Zero)
{
- _disposed = true;
- Runtime.XDecref(ptr);
- if (mdef != IntPtr.Zero)
- {
- Runtime.PyMem_Free(mdef);
- mdef = IntPtr.Zero;
- }
+ Runtime.PyMem_Free(mdef);
+ mdef = IntPtr.Zero;
}
}
}
diff --git a/src/runtime/pythonengine.cs b/src/runtime/pythonengine.cs
index cf4482c85..f427d4a44 100644
--- a/src/runtime/pythonengine.cs
+++ b/src/runtime/pythonengine.cs
@@ -213,12 +213,7 @@ public static void Initialize(IEnumerable args, bool setSysArgv = true,
Runtime.PyDict_SetItemString(module_globals, "__builtins__", builtins);
Assembly assembly = Assembly.GetExecutingAssembly();
- Stream stream = assembly.GetManifestResourceStream("clr.py");
- if (stream == null)
- {
- stream = File.OpenRead(@"I:\repos\pythonnet\src\runtime\resources\clr.py");
- }
- using (stream)
+ using (Stream stream = assembly.GetManifestResourceStream("clr.py"))
using (var reader = new StreamReader(stream))
{
// add the contents of clr.py to the module
diff --git a/src/runtime/runtime.cs b/src/runtime/runtime.cs
index fdccdf5ab..2b8460e14 100644
--- a/src/runtime/runtime.cs
+++ b/src/runtime/runtime.cs
@@ -196,50 +196,39 @@ internal static void Initialize(bool initSigs = false, bool softShutdown = false
TypeManager.Reset();
IntPtr op;
- IntPtr dict;
- if (IsPython3)
- {
- op = PyImport_ImportModule("builtins");
- dict = PyObject_GetAttrString(op, "__dict__");
- }
- else // Python2
+ _typeRefs = new PyReferenceCollection();
{
- dict = PyImport_GetModuleDict();
- op = PyDict_GetItemString(dict, "__builtin__");
- }
+ var builtins = IsPython3 ? PyImport_ImportModule("builtins")
+ : PyImport_ImportModule("__builtin__");
+ SetPyMember(ref PyNotImplemented, PyObject_GetAttrString(builtins, "NotImplemented"));
- _typeRefs = new PyReferenceCollection();
- SetPyMember(ref PyNotImplemented, PyObject_GetAttrString(op, "NotImplemented"));
+ SetPyMember(ref PyBaseObjectType, PyObject_GetAttrString(builtins, "object"));
- SetPyMember(ref PyBaseObjectType, PyObject_GetAttrString(op, "object"));
+ SetPyMember(ref PyNone, PyObject_GetAttrString(builtins, "None"));
+ SetPyMember(ref PyTrue, PyObject_GetAttrString(builtins, "True"));
+ SetPyMember(ref PyFalse, PyObject_GetAttrString(builtins, "False"));
- SetPyMember(ref PyNone, PyObject_GetAttrString(op, "None"));
- SetPyMember(ref PyTrue, PyObject_GetAttrString(op, "True"));
- SetPyMember(ref PyFalse, PyObject_GetAttrString(op, "False"));
+ SetPyMember(ref PyBoolType, PyObject_Type(PyTrue));
+ SetPyMember(ref PyNoneType, PyObject_Type(PyNone));
+ SetPyMember(ref PyTypeType, PyObject_Type(PyNoneType));
- SetPyMember(ref PyBoolType, PyObject_Type(PyTrue));
- SetPyMember(ref PyNoneType, PyObject_Type(PyNone));
- SetPyMember(ref PyTypeType, PyObject_Type(PyNoneType));
+ op = PyObject_GetAttrString(builtins, "len");
+ SetPyMember(ref PyMethodType, PyObject_Type(op));
+ XDecref(op);
- op = PyObject_GetAttrString(dict, "keys");
- SetPyMember(ref PyMethodType, PyObject_Type(op));
- XDecref(op);
+ // For some arcane reason, builtins.__dict__.__setitem__ is *not*
+ // a wrapper_descriptor, even though dict.__setitem__ is.
+ //
+ // object.__init__ seems safe, though.
+ op = PyObject_GetAttrString(PyBaseObjectType, "__init__");
+ SetPyMember(ref PyWrapperDescriptorType, PyObject_Type(op));
+ XDecref(op);
- // For some arcane reason, builtins.__dict__.__setitem__ is *not*
- // a wrapper_descriptor, even though dict.__setitem__ is.
- //
- // object.__init__ seems safe, though.
- op = PyObject_GetAttrString(PyBaseObjectType, "__init__");
- SetPyMember(ref PyWrapperDescriptorType, PyObject_Type(op));
- XDecref(op);
+ op = PyObject_GetAttrString(builtins, "KeyError");
+ SetPyMember(ref PyExc_KeyError, op);
- op = PyDict_GetItemString(dict, "KeyError");
- XIncref(op);
- SetPyMember(ref PyExc_KeyError, op);
-
-#if PYTHON3
- XDecref(dict);
-#endif
+ XDecref(builtins);
+ }
op = PyString_FromString("string");
SetPyMember(ref PyStringType, PyObject_Type(op));
@@ -287,10 +276,10 @@ internal static void Initialize(bool initSigs = false, bool softShutdown = false
IntPtr d = PyDict_New();
IntPtr c = PyClass_New(IntPtr.Zero, d, s);
- SetPyObject(ref PyClassType, PyObject_Type(c));
+ SetPyMember(ref PyClassType, PyObject_Type(c));
IntPtr i = PyInstance_New(c, IntPtr.Zero, IntPtr.Zero);
- SetPyObject(ref PyInstanceType, PyObject_Type(i));
+ SetPyMember(ref PyInstanceType, PyObject_Type(i));
XDecref(s);
XDecref(i);
@@ -322,7 +311,9 @@ internal static void Initialize(bool initSigs = false, bool softShutdown = false
// Initialize modules that depend on the runtime class.
AssemblyManager.Initialize();
- SetPyMember(ref PyCLRMetaType, MetaType.Initialize());
+ var metaType = MetaType.Initialize();
+ XIncref(metaType);
+ SetPyMember(ref PyCLRMetaType, metaType);
Exceptions.Initialize();
ImportHook.Initialize();
@@ -398,47 +389,12 @@ internal static void Shutdown(bool soft)
ClassManager.RemoveClasses();
TypeManager.RemoveTypes();
RemoveClrRootModule();
-
- {
- //var intialModules = PySys_GetObject("initial_modules");
- //var modules = PyImport_GetModuleDict();
- //foreach (var name in RuntimeState.GetModuleNames())
- //{
- // if (PySet_Contains(intialModules, name) == 1)
- // {
- // continue;
- // }
- // var module = PyDict_GetItem(modules, name);
- // IntPtr clr_dict = Runtime._PyObject_GetDictPtr(module); // PyObject**
- // clr_dict = (IntPtr)Marshal.PtrToStructure(clr_dict, typeof(IntPtr));
- // PyDict_Clear(clr_dict);
- // PyDict_DelItem(modules, name);
- //}
- }
if (soft)
{
PyGC_Collect();
RemoveClrObjects();
RuntimeState.Restore();
- //var objs = ManagedType.GetManagedObjects();
- //var subIterp = PyThreadState_Swap(_originIterp);
- //var iterp = PyThreadState_Get();
- //Py_EndInterpreter(iterp);
- //PyGILState_Ensure();
ResetPyMembers();
-// Runtime.PyRun_SimpleString(@"
-//import gc, pprint, objgraph
-//print()
-//with open('R:/objsx.log', 'w', encoding='utf8') as f:
-// pprint.pprint(objgraph.show_growth(), f)
-// #pprint.pprint(objgraph.typestats(), f)
-
-//#lst = gc.get_objects()
-//#with open('R:/objs.log', 'w', encoding='utf8') as f:
-// # for obj in lst:
-// # pprint.pprint(obj, f)
-//#del lst
-//");
return;
}
ResetPyMembers();
@@ -512,6 +468,7 @@ private static void PyDictTryDelItem(IntPtr dict, string key)
{
throw new PythonException();
}
+ PyErr_Clear();
}
private static void RemoveClrObjects()
@@ -1620,9 +1577,15 @@ internal static bool PyDict_Check(IntPtr ob)
[DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)]
internal static extern IntPtr PyDict_GetItemString(IntPtr pointer, string key);
+ ///
+ /// Return 0 on success or -1 on failure.
+ ///
[DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)]
internal static extern int PyDict_SetItem(IntPtr pointer, IntPtr key, IntPtr value);
+ ///
+ /// Return 0 on success or -1 on failure.
+ ///
[DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)]
internal static extern int PyDict_SetItemString(IntPtr pointer, string key, IntPtr value);
@@ -2069,6 +2032,17 @@ internal static bool _PyObject_GC_IS_TRACKED(IntPtr ob)
return (long)_PyGC_REFS(ob) != _PyGC_REFS_UNTRACKED;
}
+ //====================================================================
+ // Python Capsules API
+ //====================================================================
+
+ [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern IntPtr PyCapsule_New(IntPtr pointer, string name, IntPtr destructor);
+
+ [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern IntPtr PyCapsule_GetPointer(IntPtr capsule, string name);
+
+
//====================================================================
// Miscellaneous
//====================================================================
diff --git a/src/runtime/runtime_state.cs b/src/runtime/runtime_state.cs
index 69291453e..44729bd62 100644
--- a/src/runtime/runtime_state.cs
+++ b/src/runtime/runtime_state.cs
@@ -76,34 +76,8 @@ public static void Restore()
throw new Exception("Runtime state have not set");
}
var dummyGC = PyLong_AsVoidPtr(dummyGCAddr);
- //foreach (var abandomedNode in IterGCNodes(dummyGC))
- //{
- // if (abandomedNode == IntPtr.Zero)
- // {
- // continue;
- // }
- // //PyObject_GC_Del(_Py_FROM_GC(abandomedNode));
- //}
-
- //unsafe
- //{
- // var head = (PyGC_Head*)dummyGC;
- // head->gc.gc_next = dummyGC;
- // head->gc.gc_prev = dummyGC;
- // head->gc.gc_refs = IntPtr.Zero;
- //}
-
ResotreModules(dummyGC);
RestoreObjects(dummyGC);
- foreach (var obj in IterObjects(dummyGC))
- {
- //if (ManagedType.IsManagedType(obj))
- {
- //var clrobj = ManagedType.GetManagedObject(obj);
- //Console.WriteLine(clrobj);
- }
- }
- //Console.WriteLine($"abndom {IterGCNodes(dummyGC).Count()}");
}
private static void ResotreModules(IntPtr dummyGC)
diff --git a/src/runtime/typemanager.cs b/src/runtime/typemanager.cs
index f82260d37..97bfc89ad 100644
--- a/src/runtime/typemanager.cs
+++ b/src/runtime/typemanager.cs
@@ -103,7 +103,8 @@ internal static IntPtr CreateType(Type impl)
var offset = (IntPtr)ObjectOffset.DictOffset(type);
Marshal.WriteIntPtr(type, TypeOffset.tp_dictoffset, offset);
- InitializeSlots(type, impl);
+ SlotsHolder slotsHolder = new SlotsHolder();
+ InitializeSlots(type, impl, slotsHolder);
int flags = TypeFlags.Default | TypeFlags.Managed |
TypeFlags.HeapType | TypeFlags.HaveGC;
@@ -119,8 +120,11 @@ internal static IntPtr CreateType(Type impl)
Runtime.PyDict_SetItemString(dict, "__module__", mod);
Runtime.XDecref(mod);
- InitMethods(type, impl);
+ IntPtr capsule = slotsHolder.ToCapsule();
+ Runtime.PyDict_SetItemString(dict, "_slotsHodler", capsule);
+ Runtime.XDecref(capsule);
+ InitMethods(type, impl);
return type;
}
@@ -172,7 +176,8 @@ internal static IntPtr CreateType(ManagedType impl, Type clrType)
Marshal.WriteIntPtr(type, TypeOffset.tp_itemsize, IntPtr.Zero);
Marshal.WriteIntPtr(type, TypeOffset.tp_dictoffset, (IntPtr)tp_dictoffset);
- InitializeSlots(type, impl.GetType());
+ SlotsHolder slotsHolder = new SlotsHolder();
+ InitializeSlots(type, impl.GetType(), slotsHolder);
if (base_ != IntPtr.Zero)
{
@@ -202,6 +207,10 @@ internal static IntPtr CreateType(ManagedType impl, Type clrType)
Runtime.PyDict_SetItemString(dict, "__module__", mod);
Runtime.XDecref(mod);
+ IntPtr capsule = slotsHolder.ToCapsule();
+ Runtime.PyDict_SetItemString(dict, "_slotsHodler", capsule);
+ Runtime.XDecref(capsule);
+
// Hide the gchandle of the implementation in a magic type slot.
GCHandle gc = impl.AllocGCHandle();
Marshal.WriteIntPtr(type, TypeOffset.magic(), (IntPtr)gc);
@@ -343,9 +352,9 @@ internal static IntPtr CreateMetaType(Type impl)
//CopySlot(py_type, type, TypeOffset.tp_clear);
//CopySlot(py_type, type, TypeOffset.tp_is_gc);
+ SlotsHolder slotsHolder = new SlotsHolder();
// Override type slots with those of the managed implementation.
-
- InitializeSlots(type, impl);
+ InitializeSlots(type, impl, slotsHolder);
int flags = TypeFlags.Default;
flags |= TypeFlags.Managed;
@@ -357,16 +366,20 @@ internal static IntPtr CreateMetaType(Type impl)
// 4 int-ptrs in size.
IntPtr mdef = Runtime.PyMem_Malloc(3 * 4 * IntPtr.Size);
IntPtr mdefStart = mdef;
+ ThunkInfo thunk = Interop.GetThunk(typeof(MetaType).GetMethod("__instancecheck__"), "BinaryFunc");
+ slotsHolder.Add(mdef, thunk.Target);
mdef = WriteMethodDef(
mdef,
"__instancecheck__",
- Interop.GetThunk(typeof(MetaType).GetMethod("__instancecheck__"), "BinaryFunc")
+ thunk.Address
);
+ thunk = Interop.GetThunk(typeof(MetaType).GetMethod("__subclasscheck__"), "BinaryFunc");
+ slotsHolder.Add(mdef, thunk.Target);
mdef = WriteMethodDef(
mdef,
"__subclasscheck__",
- Interop.GetThunk(typeof(MetaType).GetMethod("__subclasscheck__"), "BinaryFunc")
+ thunk.Address
);
// FIXME: mdef is not used
@@ -383,6 +396,10 @@ internal static IntPtr CreateMetaType(Type impl)
IntPtr mod = Runtime.PyString_FromString("CLR");
Runtime.PyDict_SetItemString(dict, "__module__", mod);
+ IntPtr capsule = slotsHolder.ToCapsule();
+ Runtime.PyDict_SetItemString(dict, "_slotsHodler", capsule);
+ Runtime.XDecref(capsule);
+
//DebugUtil.DumpType(type);
return type;
@@ -417,7 +434,8 @@ internal static IntPtr BasicSubType(string name, IntPtr base_, Type impl)
CopySlot(base_, type, TypeOffset.tp_clear);
CopySlot(base_, type, TypeOffset.tp_is_gc);
- InitializeSlots(type, impl);
+ SlotsHolder slotsHolder = new SlotsHolder();
+ InitializeSlots(type, impl, slotsHolder);
if (Runtime.PyType_Ready(type) != 0)
{
@@ -428,6 +446,9 @@ internal static IntPtr BasicSubType(string name, IntPtr base_, Type impl)
IntPtr mod = Runtime.PyString_FromString("CLR");
Runtime.PyDict_SetItemString(tp_dict, "__module__", mod);
+ IntPtr capsule = slotsHolder.ToCapsule();
+ Runtime.PyDict_SetItemString(tp_dict, "_slotsHodler", capsule);
+ Runtime.XDecref(capsule);
return type;
}
@@ -705,7 +726,7 @@ internal static void InitializeNativeCodePage()
/// provides the implementation for the type, connect the type slots of
/// the Python object to the managed methods of the implementing Type.
///
- internal static void InitializeSlots(IntPtr type, Type impl)
+ internal static void InitializeSlots(IntPtr type, Type impl, SlotsHolder slotsHolder = null)
{
// We work from the most-derived class up; make sure to get
// the most-derived slot and not to override it with a base
@@ -733,7 +754,7 @@ internal static void InitializeSlots(IntPtr type, Type impl)
continue;
}
- InitializeSlot(type, Interop.GetThunk(method), name);
+ InitializeSlot(type, Interop.GetThunk(method), name, slotsHolder);
seen.Add(name);
}
@@ -741,6 +762,7 @@ internal static void InitializeSlots(IntPtr type, Type impl)
impl = impl.BaseType;
}
+ bool canOverride = !PythonEngine.SoftShutdown;
var native = NativeCode.Active;
// The garbage collection related slots always have to return 1 or 0
@@ -750,10 +772,6 @@ internal static void InitializeSlots(IntPtr type, Type impl)
// tp_is_gc (returns 1)
// These have to be defined, though, so by default we fill these with
// static C# functions from this class.
-
- var ret0 = Interop.GetThunk(((Func)Return0).Method);
- var ret1 = Interop.GetThunk(((Func)Return1).Method);
-
if (native != null)
{
// If we want to support domain reload, the C# implementation
@@ -762,14 +780,35 @@ internal static void InitializeSlots(IntPtr type, Type impl)
// load them into a separate code page that is leaked
// intentionally.
InitializeNativeCodePage();
- ret1 = NativeCodePage + native.Return1;
- ret0 = NativeCodePage + native.Return0;
- }
+ IntPtr ret1 = NativeCodePage + native.Return1;
+ IntPtr ret0 = NativeCodePage + native.Return0;
- bool canOverride = !PythonEngine.SoftShutdown;
- InitializeSlot(type, ret0, "tp_traverse", canOverride);
- InitializeSlot(type, ret0, "tp_clear", canOverride);
- // InitializeSlot(type, ret1, "tp_is_gc");
+ InitializeSlot(type, ret0, "tp_traverse", canOverride);
+ InitializeSlot(type, ret0, "tp_clear", canOverride);
+ }
+ else
+ {
+ if (!IsSlotSet(type, "tp_traverse"))
+ {
+ var thunkRet0 = Interop.GetThunk(((Func)Return0).Method);
+ var offset = GetSlotOffset("tp_traverse");
+ Marshal.WriteIntPtr(type, offset, thunkRet0.Address);
+ if (slotsHolder != null)
+ {
+ slotsHolder.Add(type + offset, thunkRet0.Target);
+ }
+ }
+ if (!IsSlotSet(type, "tp_clear"))
+ {
+ var thunkRet0 = Interop.GetThunk(((Func)Return0).Method);
+ var offset = GetSlotOffset("tp_clear");
+ Marshal.WriteIntPtr(type, offset, thunkRet0.Address);
+ if (slotsHolder != null)
+ {
+ slotsHolder.Add(type + offset, thunkRet0.Target);
+ }
+ }
+ }
}
static int Return1(IntPtr _) => 1;
@@ -788,6 +827,16 @@ internal static void InitializeSlots(IntPtr type, Type impl)
/// Name of the method.
/// Can override the slot when it existed
static void InitializeSlot(IntPtr type, IntPtr slot, string name, bool canOverride = true)
+ {
+ var offset = GetSlotOffset(name);
+ if (!canOverride && Marshal.ReadIntPtr(type, offset) != IntPtr.Zero)
+ {
+ return;
+ }
+ Marshal.WriteIntPtr(type, offset, slot);
+ }
+
+ static void InitializeSlot(IntPtr type, ThunkInfo thunk, string name, SlotsHolder slotsHolder = null, bool canOverride = true)
{
Type typeOffset = typeof(TypeOffset);
FieldInfo fi = typeOffset.GetField(name);
@@ -797,7 +846,22 @@ static void InitializeSlot(IntPtr type, IntPtr slot, string name, bool canOverri
{
return;
}
- Marshal.WriteIntPtr(type, offset, slot);
+ Marshal.WriteIntPtr(type, offset, thunk.Address);
+ slotsHolder.Add(type + offset, thunk.Target);
+ }
+
+ static int GetSlotOffset(string name)
+ {
+ Type typeOffset = typeof(TypeOffset);
+ FieldInfo fi = typeOffset.GetField(name);
+ var offset = (int)fi.GetValue(typeOffset);
+ return offset;
+ }
+
+ static bool IsSlotSet(IntPtr type, string name)
+ {
+ int offset = GetSlotOffset(name);
+ return Marshal.ReadIntPtr(type, offset) != IntPtr.Zero;
}
///
@@ -846,4 +910,45 @@ internal static void CopySlot(IntPtr from, IntPtr to, int offset)
Marshal.WriteIntPtr(to, offset, fp);
}
}
+
+
+ class SlotsHolder
+ {
+ private List> _slots = new List>();
+ private GCHandle _handle;
+ private Interop.DestructorFunc _destructor;
+ private IntPtr _capsule;
+
+ public void Add(IntPtr slotPtr, Delegate d)
+ {
+ _slots.Add(new KeyValuePair(slotPtr, d));
+ }
+
+ public IntPtr ToCapsule()
+ {
+ if (_capsule != IntPtr.Zero)
+ {
+ Runtime.XIncref(_capsule);
+ return _capsule;
+ }
+ _handle = GCHandle.Alloc(this);
+ _destructor = OnDestruct;
+ var fp = Marshal.GetFunctionPointerForDelegate(_destructor);
+ _capsule = Runtime.PyCapsule_New((IntPtr)_handle, null, fp);
+ return _capsule;
+ }
+
+ private static void OnDestruct(IntPtr ob)
+ {
+ var ptr = Runtime.PyCapsule_GetPointer(ob, null);
+ PythonException.ThrowIfIsNull(ptr);
+ var handle = GCHandle.FromIntPtr(ptr);
+ var self = (SlotsHolder)handle.Target;
+ handle.Free();
+ foreach (var item in self._slots)
+ {
+ Marshal.WriteIntPtr(item.Key, IntPtr.Zero);
+ }
+ }
+ }
}
From 91f64b94a027a13d7b97113cd648123018516a30 Mon Sep 17 00:00:00 2001
From: amos402
Date: Fri, 20 Sep 2019 15:56:43 +0800
Subject: [PATCH 023/998] Fixed leaking of tp_name
---
src/runtime/runtime.cs | 4 ++++
src/runtime/typemanager.cs | 8 ++------
2 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/src/runtime/runtime.cs b/src/runtime/runtime.cs
index 2b8460e14..25c520e38 100644
--- a/src/runtime/runtime.cs
+++ b/src/runtime/runtime.cs
@@ -1408,6 +1408,10 @@ internal static IntPtr PyUnicode_FromStringAndSize(IntPtr value, long size)
[DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr PyUnicode_FromStringAndSize(IntPtr value, IntPtr size);
+
+ [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern IntPtr PyUnicode_AsUTF8(IntPtr unicode);
+
#elif PYTHON2
internal static IntPtr PyString_FromStringAndSize(string value, long size)
{
diff --git a/src/runtime/typemanager.cs b/src/runtime/typemanager.cs
index 97bfc89ad..f50aed37f 100644
--- a/src/runtime/typemanager.cs
+++ b/src/runtime/typemanager.cs
@@ -464,12 +464,8 @@ internal static IntPtr AllocateTypeObject(string name)
// the Python version of the type name - otherwise we'd have to
// allocate the tp_name and would have no way to free it.
#if PYTHON3
- // For python3 we leak two objects. One for the ASCII representation
- // required for tp_name, and another for the Unicode representation
- // for ht_name.
- IntPtr temp = Runtime.PyBytes_FromString(name);
- IntPtr raw = Runtime.PyBytes_AS_STRING(temp);
- temp = Runtime.PyUnicode_FromString(name);
+ IntPtr temp = Runtime.PyUnicode_FromString(name);
+ IntPtr raw = Runtime.PyUnicode_AsUTF8(temp);
#elif PYTHON2
IntPtr temp = Runtime.PyString_FromString(name);
IntPtr raw = Runtime.PyString_AsString(temp);
From 46c7597eb18f3586de76a66c3a5ec3d185b64010 Mon Sep 17 00:00:00 2001
From: Mark Visser
Date: Fri, 20 Sep 2019 10:01:24 -0400
Subject: [PATCH 024/998] Update readme with resources (#955)
Added resources section with mailing list and chat
---
README.rst | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/README.rst b/README.rst
index 5366649ae..84bf93d84 100644
--- a/README.rst
+++ b/README.rst
@@ -113,3 +113,8 @@ https://github.com/pythonnet/pythonnet/wiki
:target: http://stackoverflow.com/questions/tagged/python.net
.. |conda-forge version| image:: https://img.shields.io/conda/vn/conda-forge/pythonnet.svg
:target: https://anaconda.org/conda-forge/pythonnet
+
+Resources
+---------
+Mailing list: https://mail.python.org/mailman/listinfo/pythondotnet
+Chat: https://gitter.im/pythonnet/pythonnet
From b07b844908c56da0bf9152f2e39e4b2b52dc82a8 Mon Sep 17 00:00:00 2001
From: amos402
Date: Fri, 20 Sep 2019 23:20:27 +0800
Subject: [PATCH 025/998] * Reset type slots * Fix ref error at
PythoneEngine.With
---
src/runtime/classbase.cs | 17 +---
src/runtime/classmanager.cs | 2 +-
src/runtime/extensiontype.cs | 16 ---
src/runtime/finalizer.cs | 2 +-
src/runtime/interop.cs | 25 ++++-
src/runtime/metatype.cs | 9 ++
src/runtime/methodbinding.cs | 10 +-
src/runtime/methodobject.cs | 4 +-
src/runtime/moduleobject.cs | 12 +--
src/runtime/pythonengine.cs | 11 ++-
src/runtime/runtime.cs | 18 ++--
src/runtime/typemanager.cs | 187 +++++++++++++++++++++++++++++++----
12 files changed, 226 insertions(+), 87 deletions(-)
diff --git a/src/runtime/classbase.cs b/src/runtime/classbase.cs
index af00c58fe..10882009d 100644
--- a/src/runtime/classbase.cs
+++ b/src/runtime/classbase.cs
@@ -29,13 +29,6 @@ internal virtual bool CanSubclass()
return !type.IsEnum;
}
- ///
- /// Implements __init__ for reflected classes and value types.
- ///
- public static int tp_init(IntPtr ob, IntPtr args, IntPtr kw)
- {
- return 0;
- }
///
/// Default implementation of [] semantics for reflected types.
@@ -254,13 +247,9 @@ public static IntPtr tp_str(IntPtr ob)
public static void tp_dealloc(IntPtr ob)
{
ManagedType self = GetManagedObject(ob);
- if (self.pyHandle != self.tpHandle)
- {
- ClearObjectDict(ob);
- }
+ tp_clear(ob);
Runtime.PyObject_GC_UnTrack(self.pyHandle);
Runtime.PyObject_GC_Del(self.pyHandle);
- Runtime.XDecref(self.tpHandle);
self.FreeGCHandle();
}
@@ -271,9 +260,7 @@ public static int tp_clear(IntPtr ob)
{
ClearObjectDict(ob);
}
- Runtime.XDecref(self.tpHandle);
- self.tpHandle = IntPtr.Zero;
- self.FreeGCHandle();
+ Runtime.Py_CLEAR(ref self.tpHandle);
return 0;
}
diff --git a/src/runtime/classmanager.cs b/src/runtime/classmanager.cs
index 3343371f1..43892aabc 100644
--- a/src/runtime/classmanager.cs
+++ b/src/runtime/classmanager.cs
@@ -54,7 +54,7 @@ internal static void RemoveClasses()
foreach (var cls in cache.Values)
{
cls.TypeTraverse(OnVisit, visitedPtr);
- // XXX: Force release some resouces.
+ // XXX: Force release instance resources but not dealloc itself.
cls.TypeClear();
}
}
diff --git a/src/runtime/extensiontype.cs b/src/runtime/extensiontype.cs
index 634b1d1a5..8d30da16e 100644
--- a/src/runtime/extensiontype.cs
+++ b/src/runtime/extensiontype.cs
@@ -92,21 +92,5 @@ public static void tp_dealloc(IntPtr ob)
ManagedType self = GetManagedObject(ob);
FinalizeObject(self);
}
-
- public static int tp_clear(IntPtr ob)
- {
- ManagedType self = GetManagedObject(ob);
- Runtime.XDecref(self.tpHandle);
- self.FreeGCHandle();
- return 0;
- }
-
- //public static int tp_traverse(IntPtr ob, IntPtr visit, IntPtr arg)
- //{
- // ManagedType self = GetManagedObject(ob);
- // int res = PyVisit(self.tpHandle, visit, arg);
- // if (res != 0) return res;
- // return 0;
- //}
}
}
diff --git a/src/runtime/finalizer.cs b/src/runtime/finalizer.cs
index dd5c0b4dd..7630b843f 100644
--- a/src/runtime/finalizer.cs
+++ b/src/runtime/finalizer.cs
@@ -59,7 +59,7 @@ public IncorrectRefCountException(IntPtr ptr)
IntPtr pyname = Runtime.PyObject_Unicode(PyPtr);
string name = Runtime.GetManagedString(pyname);
Runtime.XDecref(pyname);
- _message = $"{name} may has a incorrect ref count";
+ _message = $"<{name}> may has a incorrect ref count";
}
}
diff --git a/src/runtime/interop.cs b/src/runtime/interop.cs
index 7b03c58a2..fd811d1b1 100644
--- a/src/runtime/interop.cs
+++ b/src/runtime/interop.cs
@@ -5,6 +5,7 @@
using System.Reflection;
using System.Text;
using System.Collections.Generic;
+using System.Linq;
namespace Python.Runtime
{
@@ -262,6 +263,14 @@ public static void FreeModuleDef(IntPtr ptr)
}
#endif // PYTHON3
+ static class TypeOffsetHelper
+ {
+ public static string GetSlotNameByOffset(int offset)
+ {
+ return typeof(TypeOffset).GetFields().First(fi => (int)fi.GetValue(null) == offset).Name;
+ }
+ }
+
///
/// TypeFlags(): The actual bit values for the Type Flags stored
/// in a class.
@@ -448,11 +457,6 @@ internal static Type GetPrototype(string name)
}
internal static ThunkInfo GetThunk(MethodInfo method, string funcType = null)
- {
- return GetThunkImpl(method, funcType);
- }
-
- private static ThunkInfo GetThunkImpl(MethodInfo method, string funcType)
{
Type dt;
if (funcType != null)
@@ -473,6 +477,7 @@ private static ThunkInfo GetThunkImpl(MethodInfo method, string funcType)
return new ThunkInfo(d, fp);
}
+
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate IntPtr UnaryFunc(IntPtr ob);
@@ -552,4 +557,14 @@ struct PyGC_Head
{
public PyGC_Node gc;
}
+
+
+ [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
+ struct PyMethodDef
+ {
+ public IntPtr ml_name;
+ public IntPtr ml_meth;
+ public int ml_flags;
+ public IntPtr ml_doc;
+ }
}
diff --git a/src/runtime/metatype.cs b/src/runtime/metatype.cs
index 8853c2d5e..112566d7f 100644
--- a/src/runtime/metatype.cs
+++ b/src/runtime/metatype.cs
@@ -22,6 +22,15 @@ public static IntPtr Initialize()
return PyCLRMetaType;
}
+ public static void Release()
+ {
+ if (Runtime.Refcount(PyCLRMetaType) > 1)
+ {
+ SlotsHolder.ReleaseTypeSlots(PyCLRMetaType);
+ }
+ Runtime.XDecref(PyCLRMetaType);
+ PyCLRMetaType = IntPtr.Zero;
+ }
///
/// Metatype __new__ implementation. This is called to create a new
diff --git a/src/runtime/methodbinding.cs b/src/runtime/methodbinding.cs
index b917d9bb9..5c4164067 100644
--- a/src/runtime/methodbinding.cs
+++ b/src/runtime/methodbinding.cs
@@ -38,10 +38,8 @@ public MethodBinding(MethodObject m, IntPtr target) : this(m, target, IntPtr.Zer
private void ClearMembers()
{
- Runtime.XDecref(target);
- target = IntPtr.Zero;
- Runtime.XDecref(targetType);
- targetType = IntPtr.Zero;
+ Runtime.Py_CLEAR(ref target);
+ Runtime.Py_CLEAR(ref targetType);
}
///
@@ -249,11 +247,11 @@ public static IntPtr tp_repr(IntPtr ob)
FinalizeObject(self);
}
- public new static int tp_clear(IntPtr ob)
+ public static int tp_clear(IntPtr ob)
{
var self = (MethodBinding)GetManagedObject(ob);
self.ClearMembers();
- return ExtensionType.tp_clear(ob);
+ return 0;
}
}
}
diff --git a/src/runtime/methodobject.cs b/src/runtime/methodobject.cs
index 2531bc285..847183c73 100644
--- a/src/runtime/methodobject.cs
+++ b/src/runtime/methodobject.cs
@@ -211,11 +211,11 @@ public static IntPtr tp_repr(IntPtr ob)
ExtensionType.FinalizeObject(self);
}
- public new static int tp_clear(IntPtr ob)
+ public static int tp_clear(IntPtr ob)
{
var self = (MethodObject)GetManagedObject(ob);
self.ClearMembers();
- return ExtensionType.tp_clear(ob);
+ return 0;
}
}
}
diff --git a/src/runtime/moduleobject.cs b/src/runtime/moduleobject.cs
index 23488f676..717cc5028 100644
--- a/src/runtime/moduleobject.cs
+++ b/src/runtime/moduleobject.cs
@@ -304,13 +304,7 @@ public static IntPtr tp_repr(IntPtr ob)
public new static void tp_dealloc(IntPtr ob)
{
var self = (ModuleObject)GetManagedObject(ob);
- Runtime.XDecref(self.dict);
- self.dict = IntPtr.Zero;
- foreach (var attr in self.cache.Values)
- {
- Runtime.XDecref(attr.pyHandle);
- }
- self.cache.Clear();
+ tp_clear(ob);
ExtensionType.tp_dealloc(ob);
}
@@ -327,7 +321,7 @@ public static int tp_traverse(IntPtr ob, IntPtr visit, IntPtr arg)
return 0;
}
- public new static int tp_clear(IntPtr ob)
+ public static int tp_clear(IntPtr ob)
{
var self = (ModuleObject)GetManagedObject(ob);
Runtime.XDecref(self.dict);
@@ -337,7 +331,7 @@ public static int tp_traverse(IntPtr ob, IntPtr visit, IntPtr arg)
Runtime.XDecref(attr.pyHandle);
}
self.cache.Clear();
- return ExtensionType.tp_clear(ob);
+ return 0;
}
}
diff --git a/src/runtime/pythonengine.cs b/src/runtime/pythonengine.cs
index f427d4a44..0ad928caa 100644
--- a/src/runtime/pythonengine.cs
+++ b/src/runtime/pythonengine.cs
@@ -194,10 +194,10 @@ public static void Initialize(IEnumerable args, bool setSysArgv = true,
// register the atexit callback (this doesn't use Py_AtExit as the C atexit
// callbacks are called after python is fully finalized but the python ones
// are called while the python engine is still running).
- string code =
- "import atexit, clr\n" +
- "atexit.register(clr._AtExit)\n";
- PythonEngine.Exec(code);
+ //string code =
+ // "import atexit, clr\n" +
+ // "atexit.register(clr._AtExit)\n";
+ //PythonEngine.Exec(code);
}
// Load the clr.py resource into the clr module
@@ -756,6 +756,9 @@ public static void With(PyObject obj, Action Body)
traceBack = ex.PyTB;
}
+ Runtime.XIncref(type);
+ Runtime.XIncref(val);
+ Runtime.XIncref(traceBack);
var exitResult = obj.InvokeMethod("__exit__", new PyObject(type), new PyObject(val), new PyObject(traceBack));
if (ex != null && !exitResult.IsTrue()) throw ex;
diff --git a/src/runtime/runtime.cs b/src/runtime/runtime.cs
index 25c520e38..484649694 100644
--- a/src/runtime/runtime.cs
+++ b/src/runtime/runtime.cs
@@ -311,9 +311,7 @@ internal static void Initialize(bool initSigs = false, bool softShutdown = false
// Initialize modules that depend on the runtime class.
AssemblyManager.Initialize();
- var metaType = MetaType.Initialize();
- XIncref(metaType);
- SetPyMember(ref PyCLRMetaType, metaType);
+ PyCLRMetaType = MetaType.Initialize(); // Steal a reference
Exceptions.Initialize();
ImportHook.Initialize();
@@ -388,11 +386,14 @@ internal static void Shutdown(bool soft)
ClearClrModules();
ClassManager.RemoveClasses();
TypeManager.RemoveTypes();
+
+ MetaType.Release();
+ PyCLRMetaType = IntPtr.Zero;
+
RemoveClrRootModule();
if (soft)
{
PyGC_Collect();
- RemoveClrObjects();
RuntimeState.Restore();
ResetPyMembers();
return;
@@ -471,9 +472,6 @@ private static void PyDictTryDelItem(IntPtr dict, string key)
PyErr_Clear();
}
- private static void RemoveClrObjects()
- {
- }
internal static IntPtr Py_single_input = (IntPtr)256;
internal static IntPtr Py_file_input = (IntPtr)257;
@@ -2036,6 +2034,12 @@ internal static bool _PyObject_GC_IS_TRACKED(IntPtr ob)
return (long)_PyGC_REFS(ob) != _PyGC_REFS_UNTRACKED;
}
+ internal static void Py_CLEAR(ref IntPtr ob)
+ {
+ XDecref(ob);
+ ob = IntPtr.Zero;
+ }
+
//====================================================================
// Python Capsules API
//====================================================================
diff --git a/src/runtime/typemanager.cs b/src/runtime/typemanager.cs
index f50aed37f..0663ad081 100644
--- a/src/runtime/typemanager.cs
+++ b/src/runtime/typemanager.cs
@@ -5,6 +5,7 @@
using System.Runtime.InteropServices;
using Python.Runtime.Platform;
using System.Linq;
+using System.Diagnostics;
namespace Python.Runtime
{
@@ -37,6 +38,12 @@ internal static void RemoveTypes()
{
foreach (var tpHandle in cache.Values)
{
+ // If refcount > 1, it needs to reset the managed slot,
+ // otherwise it can dealloc without any trick.
+ if (Runtime.Refcount(tpHandle) > 1)
+ {
+ SlotsHolder.ReleaseTypeSlots(tpHandle);
+ }
Runtime.XDecref(tpHandle);
}
cache.Clear();
@@ -103,7 +110,7 @@ internal static IntPtr CreateType(Type impl)
var offset = (IntPtr)ObjectOffset.DictOffset(type);
Marshal.WriteIntPtr(type, TypeOffset.tp_dictoffset, offset);
- SlotsHolder slotsHolder = new SlotsHolder();
+ SlotsHolder slotsHolder = new SlotsHolder(type);
InitializeSlots(type, impl, slotsHolder);
int flags = TypeFlags.Default | TypeFlags.Managed |
@@ -121,7 +128,7 @@ internal static IntPtr CreateType(Type impl)
Runtime.XDecref(mod);
IntPtr capsule = slotsHolder.ToCapsule();
- Runtime.PyDict_SetItemString(dict, "_slotsHodler", capsule);
+ Runtime.PyDict_SetItemString(dict, SlotsHolder.HolderKeyName, capsule);
Runtime.XDecref(capsule);
InitMethods(type, impl);
@@ -176,7 +183,7 @@ internal static IntPtr CreateType(ManagedType impl, Type clrType)
Marshal.WriteIntPtr(type, TypeOffset.tp_itemsize, IntPtr.Zero);
Marshal.WriteIntPtr(type, TypeOffset.tp_dictoffset, (IntPtr)tp_dictoffset);
- SlotsHolder slotsHolder = new SlotsHolder();
+ SlotsHolder slotsHolder = new SlotsHolder(type);
InitializeSlots(type, impl.GetType(), slotsHolder);
if (base_ != IntPtr.Zero)
@@ -208,7 +215,7 @@ internal static IntPtr CreateType(ManagedType impl, Type clrType)
Runtime.XDecref(mod);
IntPtr capsule = slotsHolder.ToCapsule();
- Runtime.PyDict_SetItemString(dict, "_slotsHodler", capsule);
+ Runtime.PyDict_SetItemString(dict, SlotsHolder.HolderKeyName, capsule);
Runtime.XDecref(capsule);
// Hide the gchandle of the implementation in a magic type slot.
@@ -327,6 +334,24 @@ internal static IntPtr WriteMethodDefSentinel(IntPtr mdef)
return WriteMethodDef(mdef, IntPtr.Zero, IntPtr.Zero, 0, IntPtr.Zero);
}
+ internal static void FreeMethodDef(IntPtr mdef)
+ {
+ unsafe
+ {
+ var def = (PyMethodDef*)mdef;
+ if (def->ml_name != IntPtr.Zero)
+ {
+ Marshal.FreeHGlobal(def->ml_name);
+ def->ml_name = IntPtr.Zero;
+ }
+ if (def->ml_doc != IntPtr.Zero)
+ {
+ Marshal.FreeHGlobal(def->ml_doc);
+ def->ml_doc = IntPtr.Zero;
+ }
+ }
+ }
+
internal static IntPtr CreateMetaType(Type impl)
{
// The managed metatype is functionally little different than the
@@ -352,7 +377,7 @@ internal static IntPtr CreateMetaType(Type impl)
//CopySlot(py_type, type, TypeOffset.tp_clear);
//CopySlot(py_type, type, TypeOffset.tp_is_gc);
- SlotsHolder slotsHolder = new SlotsHolder();
+ SlotsHolder slotsHolder = new SlotsHolder(type);
// Override type slots with those of the managed implementation.
InitializeSlots(type, impl, slotsHolder);
@@ -365,9 +390,18 @@ internal static IntPtr CreateMetaType(Type impl)
// We need space for 3 PyMethodDef structs, each of them
// 4 int-ptrs in size.
IntPtr mdef = Runtime.PyMem_Malloc(3 * 4 * IntPtr.Size);
+ Debug.Assert(4 * IntPtr.Size == Marshal.SizeOf(typeof(PyMethodDef)));
IntPtr mdefStart = mdef;
ThunkInfo thunk = Interop.GetThunk(typeof(MetaType).GetMethod("__instancecheck__"), "BinaryFunc");
- slotsHolder.Add(mdef, thunk.Target);
+ slotsHolder.KeeapAlive(thunk.Target);
+
+ {
+ IntPtr mdefAddr = mdef;
+ slotsHolder.AddDealloctor(() =>
+ {
+ FreeMethodDef(mdefAddr);
+ });
+ }
mdef = WriteMethodDef(
mdef,
"__instancecheck__",
@@ -375,7 +409,14 @@ internal static IntPtr CreateMetaType(Type impl)
);
thunk = Interop.GetThunk(typeof(MetaType).GetMethod("__subclasscheck__"), "BinaryFunc");
- slotsHolder.Add(mdef, thunk.Target);
+ slotsHolder.KeeapAlive(thunk.Target);
+ {
+ IntPtr mdefAddr = mdef;
+ slotsHolder.AddDealloctor(() =>
+ {
+ FreeMethodDef(mdefAddr);
+ });
+ }
mdef = WriteMethodDef(
mdef,
"__subclasscheck__",
@@ -386,6 +427,12 @@ internal static IntPtr CreateMetaType(Type impl)
mdef = WriteMethodDefSentinel(mdef);
Marshal.WriteIntPtr(type, TypeOffset.tp_methods, mdefStart);
+ slotsHolder.Add(TypeOffset.tp_methods, (t, offset) =>
+ {
+ var p = Marshal.ReadIntPtr(t, offset);
+ Runtime.PyMem_Free(p);
+ Marshal.WriteIntPtr(t, offset, IntPtr.Zero);
+ });
if (Runtime.PyType_Ready(type) != 0)
{
@@ -397,7 +444,7 @@ internal static IntPtr CreateMetaType(Type impl)
Runtime.PyDict_SetItemString(dict, "__module__", mod);
IntPtr capsule = slotsHolder.ToCapsule();
- Runtime.PyDict_SetItemString(dict, "_slotsHodler", capsule);
+ Runtime.PyDict_SetItemString(dict, SlotsHolder.HolderKeyName, capsule);
Runtime.XDecref(capsule);
//DebugUtil.DumpType(type);
@@ -434,7 +481,7 @@ internal static IntPtr BasicSubType(string name, IntPtr base_, Type impl)
CopySlot(base_, type, TypeOffset.tp_clear);
CopySlot(base_, type, TypeOffset.tp_is_gc);
- SlotsHolder slotsHolder = new SlotsHolder();
+ SlotsHolder slotsHolder = new SlotsHolder(type);
InitializeSlots(type, impl, slotsHolder);
if (Runtime.PyType_Ready(type) != 0)
@@ -447,7 +494,7 @@ internal static IntPtr BasicSubType(string name, IntPtr base_, Type impl)
Runtime.PyDict_SetItemString(tp_dict, "__module__", mod);
IntPtr capsule = slotsHolder.ToCapsule();
- Runtime.PyDict_SetItemString(tp_dict, "_slotsHodler", capsule);
+ Runtime.PyDict_SetItemString(tp_dict, SlotsHolder.HolderKeyName, capsule);
Runtime.XDecref(capsule);
return type;
}
@@ -791,7 +838,7 @@ internal static void InitializeSlots(IntPtr type, Type impl, SlotsHolder slotsHo
Marshal.WriteIntPtr(type, offset, thunkRet0.Address);
if (slotsHolder != null)
{
- slotsHolder.Add(type + offset, thunkRet0.Target);
+ slotsHolder.Add(offset, thunkRet0);
}
}
if (!IsSlotSet(type, "tp_clear"))
@@ -801,7 +848,7 @@ internal static void InitializeSlots(IntPtr type, Type impl, SlotsHolder slotsHo
Marshal.WriteIntPtr(type, offset, thunkRet0.Address);
if (slotsHolder != null)
{
- slotsHolder.Add(type + offset, thunkRet0.Target);
+ slotsHolder.Add(offset, thunkRet0);
}
}
}
@@ -843,7 +890,7 @@ static void InitializeSlot(IntPtr type, ThunkInfo thunk, string name, SlotsHolde
return;
}
Marshal.WriteIntPtr(type, offset, thunk.Address);
- slotsHolder.Add(type + offset, thunk.Target);
+ slotsHolder.Add(offset, thunk);
}
static int GetSlotOffset(string name)
@@ -910,14 +957,45 @@ internal static void CopySlot(IntPtr from, IntPtr to, int offset)
class SlotsHolder
{
- private List> _slots = new List>();
+ public const string HolderKeyName = "_slots_holder";
+ public delegate void Resetor(IntPtr type, int offset);
+
private GCHandle _handle;
private Interop.DestructorFunc _destructor;
private IntPtr _capsule;
+ private IntPtr _type;
+ private Dictionary _slots = new Dictionary();
+ private List _keepalive = new List();
+ private Dictionary _customRestors = new Dictionary();
+ private List _deallocators = new List();
+
+ ///
+ /// Create slots holder for holding the delegate of slots and be able to reset them.
+ ///
+ /// Steals a reference to target type
+ public SlotsHolder(IntPtr type)
+ {
+ _type = type;
+ }
+
+ public void Add(int offset, ThunkInfo thunk)
+ {
+ _slots.Add(offset, thunk);
+ }
+
+ public void Add(int offset, Resetor resetor)
+ {
+ _customRestors[offset] = resetor;
+ }
+
+ public void AddDealloctor(Action deallocate)
+ {
+ _deallocators.Add(deallocate);
+ }
- public void Add(IntPtr slotPtr, Delegate d)
+ public void KeeapAlive(Delegate d)
{
- _slots.Add(new KeyValuePair(slotPtr, d));
+ _keepalive.Add(d);
}
public IntPtr ToCapsule()
@@ -934,17 +1012,84 @@ public IntPtr ToCapsule()
return _capsule;
}
+ public static void ReleaseTypeSlots(IntPtr type)
+ {
+ IntPtr capsule = Runtime.PyObject_GetAttrString(type, HolderKeyName);
+ if (capsule == IntPtr.Zero)
+ {
+ return;
+ }
+ var self = RecoverFromCapsule(capsule);
+ self.ResetSlots();
+ }
+
+ private void ResetSlots()
+ {
+ IntPtr tp_name = Marshal.ReadIntPtr(_type, TypeOffset.tp_name);
+ string typeName = Marshal.PtrToStringAnsi(tp_name);
+ foreach (var offset in _slots.Keys)
+ {
+ IntPtr ptr = GetDefaultSlot(offset);
+ //DebugUtil.Print($"Set slot<{TypeOffsetHelper.GetSlotNameByOffset(offset)}> to 0x{ptr.ToString("X")} at {typeName}<0x{_type}>");
+ Marshal.WriteIntPtr(_type, offset, ptr);
+ }
+
+ foreach (var action in _deallocators)
+ {
+ action();
+ }
+
+ foreach (var pair in _customRestors)
+ {
+ int offset = pair.Key;
+ var resetor = pair.Value;
+ resetor?.Invoke(_type, offset);
+ }
+
+ _customRestors.Clear();
+ _slots.Clear();
+ _keepalive.Clear();
+ _deallocators.Clear();
+
+ // Custom reset
+ IntPtr tp_base = Marshal.ReadIntPtr(_type, TypeOffset.tp_base);
+ Runtime.XDecref(tp_base);
+ Marshal.WriteIntPtr(_type, TypeOffset.tp_base, IntPtr.Zero);
+ }
+
private static void OnDestruct(IntPtr ob)
+ {
+ var self = RecoverFromCapsule(ob);
+ self._handle.Free();
+ self.ResetSlots();
+ }
+
+ private static SlotsHolder RecoverFromCapsule(IntPtr ob)
{
var ptr = Runtime.PyCapsule_GetPointer(ob, null);
PythonException.ThrowIfIsNull(ptr);
- var handle = GCHandle.FromIntPtr(ptr);
- var self = (SlotsHolder)handle.Target;
- handle.Free();
- foreach (var item in self._slots)
+ GCHandle handle = GCHandle.FromIntPtr(ptr);
+ return (SlotsHolder)handle.Target;
+ }
+
+ private static IntPtr GetDefaultSlot(int offset)
+ {
+ if (offset == TypeOffset.tp_clear
+ || offset == TypeOffset.tp_traverse)
+ {
+ return TypeManager.NativeCodePage + TypeManager.NativeCode.Active.Return0;
+ }
+ else if (offset == TypeOffset.tp_dealloc)
+ {
+ // tp_free of PyTypeType is point to PyObejct_GC_Del.
+ return Marshal.ReadIntPtr(Runtime.PyTypeType, TypeOffset.tp_free);
+ }
+ else if (offset == TypeOffset.tp_call)
{
- Marshal.WriteIntPtr(item.Key, IntPtr.Zero);
+ return IntPtr.Zero;
}
+
+ return Marshal.ReadIntPtr(Runtime.PyTypeType, offset);
}
}
}
From 7db724ed9c93ecf67b5f9fc3ee94c217597f0f57 Mon Sep 17 00:00:00 2001
From: amos402
Date: Sat, 21 Sep 2019 01:59:13 +0800
Subject: [PATCH 026/998] Clear ExtensionType
---
src/runtime/extensiontype.cs | 2 +-
src/runtime/managedtype.cs | 31 +++++++++++++++----------------
src/runtime/runtime.cs | 23 ++++++++++++++++++++++-
3 files changed, 38 insertions(+), 18 deletions(-)
diff --git a/src/runtime/extensiontype.cs b/src/runtime/extensiontype.cs
index 8d30da16e..02789e359 100644
--- a/src/runtime/extensiontype.cs
+++ b/src/runtime/extensiontype.cs
@@ -28,7 +28,7 @@ public ExtensionType()
IntPtr py = Runtime.PyType_GenericAlloc(tp, 0);
- GCHandle gc = AllocGCHandle();
+ GCHandle gc = AllocGCHandle(true);
Marshal.WriteIntPtr(py, ObjectOffset.magic(tp), (IntPtr)gc);
// We have to support gc because the type machinery makes it very
diff --git a/src/runtime/managedtype.cs b/src/runtime/managedtype.cs
index 6f0e8b88b..04e59a115 100644
--- a/src/runtime/managedtype.cs
+++ b/src/runtime/managedtype.cs
@@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices;
+using System.Linq;
namespace Python.Runtime
{
@@ -16,9 +17,7 @@ internal abstract class ManagedType
internal IntPtr pyHandle; // PyObject *
internal IntPtr tpHandle; // PyType *
-#if NPY_TRACK_OBJECT
private static readonly HashSet _managedObjs = new HashSet();
-#endif
internal void DecrRefCount()
{
@@ -41,22 +40,24 @@ internal long RefCount
}
}
- internal GCHandle AllocGCHandle()
+ internal GCHandle AllocGCHandle(bool track = false)
{
gcHandle = GCHandle.Alloc(this);
-#if NPY_TRACK_OBJECT
- _managedObjs.Add(this);
-#endif
+ if (track)
+ {
+ _managedObjs.Add(this);
+ }
return gcHandle;
}
internal void FreeGCHandle()
{
- gcHandle.Free();
-#if NPY_TRACK_OBJECT
_managedObjs.Remove(this);
-#endif
- gcHandle = new GCHandle();
+ if (gcHandle.IsAllocated)
+ {
+ gcHandle.Free();
+ gcHandle = new GCHandle();
+ }
}
///
@@ -120,17 +121,15 @@ internal static bool IsManagedType(IntPtr ob)
return false;
}
-#if NPY_TRACK_OBJECT
- internal static void Reset()
+ internal static ICollection GetManagedObjects()
{
- _managedObjs.Clear();
+ return _managedObjs;
}
- internal static ICollection GetManagedObjects()
+ internal static void ClearTrackedObjects()
{
- return _managedObjs;
+ _managedObjs.Clear();
}
-#endif
internal static int PyVisit(IntPtr ob, IntPtr visit, IntPtr arg)
{
diff --git a/src/runtime/runtime.cs b/src/runtime/runtime.cs
index 484649694..1f9731855 100644
--- a/src/runtime/runtime.cs
+++ b/src/runtime/runtime.cs
@@ -5,6 +5,7 @@
using System.Threading;
using System.Collections.Generic;
using Python.Runtime.Platform;
+using System.Linq;
namespace Python.Runtime
{
@@ -384,13 +385,15 @@ internal static void Shutdown(bool soft)
Finalizer.Shutdown();
ClearClrModules();
+ RemoveClrRootModule();
+
+ MoveClrInstancesOnwershipToPython();
ClassManager.RemoveClasses();
TypeManager.RemoveTypes();
MetaType.Release();
PyCLRMetaType = IntPtr.Zero;
- RemoveClrRootModule();
if (soft)
{
PyGC_Collect();
@@ -472,6 +475,24 @@ private static void PyDictTryDelItem(IntPtr dict, string key)
PyErr_Clear();
}
+ private static void MoveClrInstancesOnwershipToPython()
+ {
+ var copyObjs = ManagedType.GetManagedObjects().ToArray();
+ var objs = ManagedType.GetManagedObjects();
+ foreach (var obj in copyObjs)
+ {
+ if (objs.Contains(obj))
+ {
+ continue;
+ }
+ obj.TypeClear();
+ PyObject_GC_Track(obj.pyHandle);
+ obj.gcHandle.Free();
+ obj.gcHandle = new GCHandle();
+ }
+ ManagedType.ClearTrackedObjects();
+ }
+
internal static IntPtr Py_single_input = (IntPtr)256;
internal static IntPtr Py_file_input = (IntPtr)257;
From 2a88be47a0fded6b1cb7e1aa71925d2d312a31fa Mon Sep 17 00:00:00 2001
From: amos402
Date: Sat, 21 Sep 2019 04:00:01 +0800
Subject: [PATCH 027/998] Del SlotsHolder from tp_dict when shutting down
---
src/runtime/metatype.cs | 4 +++-
src/runtime/runtime.cs | 8 +++++++-
src/runtime/typemanager.cs | 26 ++++++++++++++++++++++++--
3 files changed, 34 insertions(+), 4 deletions(-)
diff --git a/src/runtime/metatype.cs b/src/runtime/metatype.cs
index 112566d7f..7cbab76c2 100644
--- a/src/runtime/metatype.cs
+++ b/src/runtime/metatype.cs
@@ -28,7 +28,8 @@ public static void Release()
{
SlotsHolder.ReleaseTypeSlots(PyCLRMetaType);
}
- Runtime.XDecref(PyCLRMetaType);
+ // FIXME: Crash on .netcore if decref PyCLRMetaType
+ // Runtime.XDecref(PyCLRMetaType);
PyCLRMetaType = IntPtr.Zero;
}
@@ -275,6 +276,7 @@ private static IntPtr DoInstanceCheck(IntPtr tp, IntPtr args, bool checkType)
return Runtime.PyFalse;
}
+ Runtime.XIncref(args);
using (var argsObj = new PyList(args))
{
if (argsObj.Length() != 1)
diff --git a/src/runtime/runtime.cs b/src/runtime/runtime.cs
index 1f9731855..5e3c6e549 100644
--- a/src/runtime/runtime.cs
+++ b/src/runtime/runtime.cs
@@ -225,6 +225,8 @@ internal static void Initialize(bool initSigs = false, bool softShutdown = false
SetPyMember(ref PyWrapperDescriptorType, PyObject_Type(op));
XDecref(op);
+ SetPyMember(ref PySuper_Type, PyObject_GetAttrString(builtins, "super"));
+
op = PyObject_GetAttrString(builtins, "KeyError");
SetPyMember(ref PyExc_KeyError, op);
@@ -481,11 +483,13 @@ private static void MoveClrInstancesOnwershipToPython()
var objs = ManagedType.GetManagedObjects();
foreach (var obj in copyObjs)
{
- if (objs.Contains(obj))
+ if (!objs.Contains(obj))
{
continue;
}
obj.TypeClear();
+ // obj's tp_type will degenerate to a pure Python type after TypeManager.RemoveTypes(),
+ // thus just be safe to give it back to GC chain.
PyObject_GC_Track(obj.pyHandle);
obj.gcHandle.Free();
obj.gcHandle = new GCHandle();
@@ -502,6 +506,7 @@ private static void MoveClrInstancesOnwershipToPython()
internal static IntPtr PyModuleType;
internal static IntPtr PyClassType;
internal static IntPtr PyInstanceType;
+ internal static IntPtr PySuper_Type;
internal static IntPtr PyCLRMetaType;
internal static IntPtr PyMethodType;
internal static IntPtr PyWrapperDescriptorType;
@@ -895,6 +900,7 @@ public static extern int Py_Main(
//====================================================================
///
+ /// Return value: Borrowed reference.
/// A macro-like method to get the type of a Python object. This is
/// designed to be lean and mean in IL & avoid managed <-> unmanaged
/// transitions. Note that this does not incref the type object.
diff --git a/src/runtime/typemanager.cs b/src/runtime/typemanager.cs
index 0663ad081..401099ec0 100644
--- a/src/runtime/typemanager.cs
+++ b/src/runtime/typemanager.cs
@@ -826,8 +826,8 @@ internal static void InitializeSlots(IntPtr type, Type impl, SlotsHolder slotsHo
IntPtr ret1 = NativeCodePage + native.Return1;
IntPtr ret0 = NativeCodePage + native.Return0;
- InitializeSlot(type, ret0, "tp_traverse", canOverride);
- InitializeSlot(type, ret0, "tp_clear", canOverride);
+ InitializeSlot(type, ret0, "tp_traverse", false);
+ InitializeSlot(type, ret0, "tp_clear", false);
}
else
{
@@ -968,6 +968,7 @@ class SlotsHolder
private List _keepalive = new List();
private Dictionary _customRestors = new Dictionary();
private List _deallocators = new List();
+ private bool _alredyReset = false;
///
/// Create slots holder for holding the delegate of slots and be able to reset them.
@@ -1021,10 +1022,22 @@ public static void ReleaseTypeSlots(IntPtr type)
}
var self = RecoverFromCapsule(capsule);
self.ResetSlots();
+ Runtime.XDecref(capsule);
+
+ IntPtr tp_dict = Marshal.ReadIntPtr(type, TypeOffset.tp_dict);
+ if (Runtime.PyDict_DelItemString(tp_dict, HolderKeyName) != 0)
+ {
+ throw new PythonException();
+ }
}
private void ResetSlots()
{
+ if (_alredyReset)
+ {
+ return;
+ }
+ _alredyReset = true;
IntPtr tp_name = Marshal.ReadIntPtr(_type, TypeOffset.tp_name);
string typeName = Marshal.PtrToStringAnsi(tp_name);
foreach (var offset in _slots.Keys)
@@ -1055,6 +1068,10 @@ private void ResetSlots()
IntPtr tp_base = Marshal.ReadIntPtr(_type, TypeOffset.tp_base);
Runtime.XDecref(tp_base);
Marshal.WriteIntPtr(_type, TypeOffset.tp_base, IntPtr.Zero);
+
+ IntPtr tp_bases = Marshal.ReadIntPtr(_type, TypeOffset.tp_bases);
+ Runtime.XDecref(tp_bases);
+ Marshal.WriteIntPtr(_type, TypeOffset.tp_bases, IntPtr.Zero);
}
private static void OnDestruct(IntPtr ob)
@@ -1088,6 +1105,11 @@ private static IntPtr GetDefaultSlot(int offset)
{
return IntPtr.Zero;
}
+ else if (offset == TypeOffset.tp_new)
+ {
+ // PyType_GenericNew
+ return Marshal.ReadIntPtr(Runtime.PySuper_Type, TypeOffset.tp_new);
+ }
return Marshal.ReadIntPtr(Runtime.PyTypeType, offset);
}
From 294097347dcf96924f64c246230402bbcdbbff03 Mon Sep 17 00:00:00 2001
From: amos402
Date: Sat, 21 Sep 2019 14:30:29 +0800
Subject: [PATCH 028/998] Fix refcnt error of qualname
---
src/runtime/metatype.cs | 3 +--
src/runtime/typemanager.cs | 28 ++++++++++++++++++++++++++++
2 files changed, 29 insertions(+), 2 deletions(-)
diff --git a/src/runtime/metatype.cs b/src/runtime/metatype.cs
index 7cbab76c2..51f3eddd7 100644
--- a/src/runtime/metatype.cs
+++ b/src/runtime/metatype.cs
@@ -28,8 +28,7 @@ public static void Release()
{
SlotsHolder.ReleaseTypeSlots(PyCLRMetaType);
}
- // FIXME: Crash on .netcore if decref PyCLRMetaType
- // Runtime.XDecref(PyCLRMetaType);
+ Runtime.XDecref(PyCLRMetaType);
PyCLRMetaType = IntPtr.Zero;
}
diff --git a/src/runtime/typemanager.cs b/src/runtime/typemanager.cs
index 401099ec0..0660258c5 100644
--- a/src/runtime/typemanager.cs
+++ b/src/runtime/typemanager.cs
@@ -399,6 +399,12 @@ internal static IntPtr CreateMetaType(Type impl)
IntPtr mdefAddr = mdef;
slotsHolder.AddDealloctor(() =>
{
+ IntPtr t = type;
+ IntPtr tp_dict = Marshal.ReadIntPtr(t, TypeOffset.tp_dict);
+ if (Runtime.PyDict_DelItemString(tp_dict, "__instancecheck__") != 0)
+ {
+ Runtime.PyErr_Print();
+ }
FreeMethodDef(mdefAddr);
});
}
@@ -414,6 +420,12 @@ internal static IntPtr CreateMetaType(Type impl)
IntPtr mdefAddr = mdef;
slotsHolder.AddDealloctor(() =>
{
+ IntPtr t = type;
+ IntPtr tp_dict = Marshal.ReadIntPtr(t, TypeOffset.tp_dict);
+ if (Runtime.PyDict_DelItemString(tp_dict, "__subclasscheck__") != 0)
+ {
+ Runtime.PyErr_Print();
+ }
FreeMethodDef(mdefAddr);
});
}
@@ -521,6 +533,7 @@ internal static IntPtr AllocateTypeObject(string name)
Marshal.WriteIntPtr(type, TypeOffset.name, temp);
#if PYTHON3
+ Runtime.XIncref(temp);
Marshal.WriteIntPtr(type, TypeOffset.qualname, temp);
#endif
@@ -1101,6 +1114,11 @@ private static IntPtr GetDefaultSlot(int offset)
// tp_free of PyTypeType is point to PyObejct_GC_Del.
return Marshal.ReadIntPtr(Runtime.PyTypeType, TypeOffset.tp_free);
}
+ else if (offset == TypeOffset.tp_free)
+ {
+ // PyObject_GC_Del
+ return Marshal.ReadIntPtr(Runtime.PyTypeType, TypeOffset.tp_free);
+ }
else if (offset == TypeOffset.tp_call)
{
return IntPtr.Zero;
@@ -1110,6 +1128,16 @@ private static IntPtr GetDefaultSlot(int offset)
// PyType_GenericNew
return Marshal.ReadIntPtr(Runtime.PySuper_Type, TypeOffset.tp_new);
}
+ else if (offset == TypeOffset.tp_getattro)
+ {
+ // PyObject_GenericGetAttr
+ return Marshal.ReadIntPtr(Runtime.PyBaseObjectType, TypeOffset.tp_getattro);
+ }
+ else if (offset == TypeOffset.tp_setattro)
+ {
+ // PyObject_GenericSetAttr
+ return Marshal.ReadIntPtr(Runtime.PyBaseObjectType, TypeOffset.tp_setattro);
+ }
return Marshal.ReadIntPtr(Runtime.PyTypeType, offset);
}
From d1089138d409f66181e7f50fcc9b8e8b52369349 Mon Sep 17 00:00:00 2001
From: amos402
Date: Mon, 23 Sep 2019 00:02:30 +0800
Subject: [PATCH 029/998] Since ClassBase not override tp_init for
BaseException, repr(ExceptionObject) will not care 'u' on Python2.
---
src/runtime/runtime.cs | 8 ++------
src/tests/test_exceptions.py | 2 +-
2 files changed, 3 insertions(+), 7 deletions(-)
diff --git a/src/runtime/runtime.cs b/src/runtime/runtime.cs
index 5e3c6e549..21ca50bc2 100644
--- a/src/runtime/runtime.cs
+++ b/src/runtime/runtime.cs
@@ -227,9 +227,6 @@ internal static void Initialize(bool initSigs = false, bool softShutdown = false
SetPyMember(ref PySuper_Type, PyObject_GetAttrString(builtins, "super"));
- op = PyObject_GetAttrString(builtins, "KeyError");
- SetPyMember(ref PyExc_KeyError, op);
-
XDecref(builtins);
}
@@ -345,6 +342,7 @@ private static void InitializePlatformData()
fn = PyObject_GetAttrString(platformModule, "system");
op = PyObject_Call(fn, emptyTuple, IntPtr.Zero);
+ PythonException.ThrowIfIsNull(op);
OperatingSystemName = GetManagedString(op);
XDecref(op);
XDecref(fn);
@@ -470,7 +468,7 @@ private static void PyDictTryDelItem(IntPtr dict, string key)
{
return;
}
- if (!PythonException.Matches(PyExc_KeyError))
+ if (!PythonException.Matches(Exceptions.KeyError))
{
throw new PythonException();
}
@@ -541,8 +539,6 @@ private static void MoveClrInstancesOnwershipToPython()
internal static IntPtr PyNone;
internal static IntPtr Error;
- internal static IntPtr PyExc_KeyError;
-
///
/// Check if any Python Exceptions occurred.
/// If any exist throw new PythonException.
diff --git a/src/tests/test_exceptions.py b/src/tests/test_exceptions.py
index a10d9a183..b03c974d1 100644
--- a/src/tests/test_exceptions.py
+++ b/src/tests/test_exceptions.py
@@ -286,7 +286,7 @@ def test_python_compat_of_managed_exceptions():
strexp = "OverflowException('Simple message"
assert repr(e)[:len(strexp)] == strexp
elif PY2:
- assert repr(e) == "OverflowException(u'Simple message',)"
+ assert repr(e) == "OverflowException('Simple message',)"
def test_exception_is_instance_of_system_object():
From cc9e7e5cd813755d0bce0938b438417cc0aa2c31 Mon Sep 17 00:00:00 2001
From: amos402
Date: Mon, 23 Sep 2019 00:32:14 +0800
Subject: [PATCH 030/998] * Use environment variable `PYTHONNET_SOFT_SHUTDOWN`
to enable soft shutdown forcedly * Disable restore objects on default
---
.travis.yml | 2 ++
appveyor.yml | 7 +++--
src/runtime/pythonengine.cs | 3 +-
src/runtime/runtime.cs | 7 +++++
src/runtime/runtime_state.cs | 56 ++++++++++++++++++++++++------------
src/runtime/typemanager.cs | 1 -
6 files changed, 53 insertions(+), 23 deletions(-)
diff --git a/.travis.yml b/.travis.yml
index 9689c0422..047a2810c 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -11,6 +11,8 @@ env:
matrix:
- BUILD_OPTS=--xplat NUNIT_PATH="~/.nuget/packages/nunit.consolerunner/3.*/tools/nunit3-console.exe" RUN_TESTS=dotnet EMBED_TESTS_PATH=netcoreapp2.0_publish/
- BUILD_OPTS="" NUNIT_PATH="./packages/NUnit.*/tools/nunit3-console.exe" RUN_TESTS="mono $NUNIT_PATH" EMBED_TESTS_PATH=""
+ - PYTHONNET_SOFT_SHUTDOWN="1" BUILD_OPTS=--xplat NUNIT_PATH="~/.nuget/packages/nunit.consolerunner/3.*/tools/nunit3-console.exe" RUN_TESTS=dotnet EMBED_TESTS_PATH=netcoreapp2.0_publish/
+ - PYTHONNET_SOFT_SHUTDOWN="1" BUILD_OPTS="" NUNIT_PATH="./packages/NUnit.*/tools/nunit3-console.exe" RUN_TESTS="mono $NUNIT_PATH" EMBED_TESTS_PATH=""
global:
- LD_PRELOAD=/lib/x86_64-linux-gnu/libSegFault.so
diff --git a/appveyor.yml b/appveyor.yml
index 445f9bb5a..6d4d5eacb 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -3,7 +3,7 @@ build: off
image:
- Visual Studio 2017
-
+
platform:
- x86
- x64
@@ -15,7 +15,7 @@ environment:
CODECOV_ENV: PYTHON_VERSION, PLATFORM
matrix:
- - PYTHON_VERSION: 2.7
+ - PYTHON_VERSION: 2.7
BUILD_OPTS: --xplat
- PYTHON_VERSION: 3.5
BUILD_OPTS: --xplat
@@ -28,6 +28,9 @@ environment:
- PYTHON_VERSION: 3.6
- PYTHON_VERSION: 3.7
+ - PYTHON_VERSION: 3.7
+ PYTHONNET_SOFT_SHUTDOWN: 1
+
matrix:
allow_failures:
- PYTHON_VERSION: 3.4
diff --git a/src/runtime/pythonengine.cs b/src/runtime/pythonengine.cs
index 0ad928caa..3c6475610 100644
--- a/src/runtime/pythonengine.cs
+++ b/src/runtime/pythonengine.cs
@@ -12,7 +12,7 @@ namespace Python.Runtime
///
public class PythonEngine : IDisposable
{
- public static bool SoftShutdown { get; private set; }
+ public static bool SoftShutdown => Runtime.SoftShutdown;
private static DelegateManager delegateManager;
private static bool initialized;
@@ -171,7 +171,6 @@ public static void Initialize(IEnumerable args, bool setSysArgv = true,
delegateManager = new DelegateManager();
Runtime.Initialize(initSigs, softShutdown);
initialized = true;
- SoftShutdown = softShutdown;
Exceptions.Clear();
// Make sure we clean up properly on app domain unload.
diff --git a/src/runtime/runtime.cs b/src/runtime/runtime.cs
index 21ca50bc2..e125629a1 100644
--- a/src/runtime/runtime.cs
+++ b/src/runtime/runtime.cs
@@ -166,6 +166,7 @@ public class Runtime
///
internal static readonly Encoding PyEncoding = _UCS == 2 ? Encoding.Unicode : Encoding.UTF32;
+ public static bool SoftShutdown { get; private set; }
private static PyReferenceCollection _typeRefs;
///
@@ -173,6 +174,12 @@ public class Runtime
///
internal static void Initialize(bool initSigs = false, bool softShutdown = false)
{
+ if (Environment.GetEnvironmentVariable("PYTHONNET_SOFT_SHUTDOWN") == "1")
+ {
+ softShutdown = true;
+ }
+
+ SoftShutdown = softShutdown;
if (Py_IsInitialized() == 0)
{
Py_InitializeEx(initSigs ? 1 : 0);
diff --git a/src/runtime/runtime_state.cs b/src/runtime/runtime_state.cs
index 44729bd62..372a6d25b 100644
--- a/src/runtime/runtime_state.cs
+++ b/src/runtime/runtime_state.cs
@@ -9,6 +9,9 @@ namespace Python.Runtime
{
class RuntimeState
{
+ public static bool ShouldRestoreObjects { get; set; } = false;
+ public static bool UseDummyGC { get; set; } = false;
+
public static void Save()
{
if (PySys_GetObject("dummy_gc") != IntPtr.Zero)
@@ -16,6 +19,16 @@ public static void Save()
throw new Exception("Runtime State set already");
}
+ IntPtr objs = IntPtr.Zero;
+ if (ShouldRestoreObjects)
+ {
+ objs = PySet_New(IntPtr.Zero);
+ foreach (var obj in PyGCGetObjects())
+ {
+ AddObjPtrToSet(objs, obj);
+ }
+ }
+
var modules = PySet_New(IntPtr.Zero);
foreach (var name in GetModuleNames())
{
@@ -23,11 +36,6 @@ public static void Save()
PythonException.ThrowIfIsNotZero(res);
}
- var objs = PySet_New(IntPtr.Zero);
- foreach (var obj in PyGCGetObjects())
- {
- AddObjPtrToSet(objs, obj);
- }
var dummyGCHead = PyMem_Malloc(Marshal.SizeOf(typeof(PyGC_Head)));
unsafe
@@ -43,7 +51,6 @@ public static void Save()
PythonException.ThrowIfIsNotZero(res);
XDecref(pyDummyGC);
- AddObjPtrToSet(objs, modules);
try
{
res = PySys_SetObject("initial_modules", modules);
@@ -54,17 +61,19 @@ public static void Save()
XDecref(modules);
}
- AddObjPtrToSet(objs, objs);
- try
- {
- res = PySys_SetObject("initial_objs", objs);
- PythonException.ThrowIfIsNotZero(res);
- }
- finally
+ if (ShouldRestoreObjects)
{
- XDecref(objs);
+ AddObjPtrToSet(objs, modules);
+ try
+ {
+ res = PySys_SetObject("initial_objs", objs);
+ PythonException.ThrowIfIsNotZero(res);
+ }
+ finally
+ {
+ XDecref(objs);
+ }
}
-
}
}
@@ -77,7 +86,10 @@ public static void Restore()
}
var dummyGC = PyLong_AsVoidPtr(dummyGCAddr);
ResotreModules(dummyGC);
- RestoreObjects(dummyGC);
+ if (ShouldRestoreObjects)
+ {
+ RestoreObjects(dummyGC);
+ }
}
private static void ResotreModules(IntPtr dummyGC)
@@ -92,16 +104,24 @@ private static void ResotreModules(IntPtr dummyGC)
continue;
}
var module = PyDict_GetItem(modules, name);
- if (_PyObject_GC_IS_TRACKED(module))
+
+ if (UseDummyGC && _PyObject_GC_IS_TRACKED(module))
{
ExchangeGCChain(module, dummyGC);
}
- PyDict_DelItem(modules, name);
+ if (PyDict_DelItem(modules, name) != 0)
+ {
+ PyErr_Print();
+ }
}
}
private static void RestoreObjects(IntPtr dummyGC)
{
+ if (!UseDummyGC)
+ {
+ throw new Exception("To prevent crash by _PyObject_GC_UNTRACK in Python internal, UseDummyGC should be enabled when using ResotreObjects");
+ }
var intialObjs = PySys_GetObject("initial_objs");
Debug.Assert(intialObjs != null);
foreach (var obj in PyGCGetObjects())
diff --git a/src/runtime/typemanager.cs b/src/runtime/typemanager.cs
index 0660258c5..1fbefc973 100644
--- a/src/runtime/typemanager.cs
+++ b/src/runtime/typemanager.cs
@@ -818,7 +818,6 @@ internal static void InitializeSlots(IntPtr type, Type impl, SlotsHolder slotsHo
impl = impl.BaseType;
}
- bool canOverride = !PythonEngine.SoftShutdown;
var native = NativeCode.Active;
// The garbage collection related slots always have to return 1 or 0
From 60e6045f6873495170502f0247205a9c923fc1fe Mon Sep 17 00:00:00 2001
From: jmlidbetter <53430310+jmlidbetter@users.noreply.github.com>
Date: Tue, 1 Oct 2019 16:13:34 +0100
Subject: [PATCH 031/998] Detect py arch (#961)
* Gets size of C long from Is32Bit and IsWindows
---
CHANGELOG.md | 1 +
src/runtime/converter.cs | 15 ++++++++++++++-
src/runtime/runtime.cs | 34 ++++++++++++++++++++++++++++++----
3 files changed, 45 insertions(+), 5 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5cb0ea96c..b5b11cd77 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -25,6 +25,7 @@ This document follows the conventions laid out in [Keep a CHANGELOG][].
- Fixed runtime that fails loading when using pythonnet in an environment
together with Nuitka
- Fixes bug where delegates get casts (dotnetcore)
+- Determine size of interpreter longs at runtime
## [2.4.0][]
diff --git a/src/runtime/converter.cs b/src/runtime/converter.cs
index 5d8769a73..e7e047419 100644
--- a/src/runtime/converter.cs
+++ b/src/runtime/converter.cs
@@ -728,7 +728,20 @@ private static bool ToPrimitive(IntPtr value, Type obType, out object result, bo
}
goto type_error;
}
- uint ui = (uint)Runtime.PyLong_AsUnsignedLong(op);
+
+ uint ui;
+ try
+ {
+ ui = Convert.ToUInt32(Runtime.PyLong_AsUnsignedLong(op));
+ } catch (OverflowException)
+ {
+ // Probably wasn't an overflow in python but was in C# (e.g. if cpython
+ // longs are 64 bit then 0xFFFFFFFF + 1 will not overflow in
+ // PyLong_AsUnsignedLong)
+ Runtime.XDecref(op);
+ goto overflow;
+ }
+
if (Exceptions.ErrorOccurred())
{
diff --git a/src/runtime/runtime.cs b/src/runtime/runtime.cs
index 75f11492f..4985a57f5 100644
--- a/src/runtime/runtime.cs
+++ b/src/runtime/runtime.cs
@@ -1036,8 +1036,21 @@ internal static bool PyLong_Check(IntPtr ob)
[DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)]
internal static extern IntPtr PyLong_FromLong(long value);
- [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)]
- internal static extern IntPtr PyLong_FromUnsignedLong(uint value);
+ [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl,
+ EntryPoint = "PyLong_FromUnsignedLong")]
+ internal static extern IntPtr PyLong_FromUnsignedLong32(uint value);
+
+ [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl,
+ EntryPoint = "PyLong_FromUnsignedLong")]
+ internal static extern IntPtr PyLong_FromUnsignedLong64(ulong value);
+
+ internal static IntPtr PyLong_FromUnsignedLong(object value)
+ {
+ if(Is32Bit || IsWindows)
+ return PyLong_FromUnsignedLong32(Convert.ToUInt32(value));
+ else
+ return PyLong_FromUnsignedLong64(Convert.ToUInt64(value));
+ }
[DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)]
internal static extern IntPtr PyLong_FromDouble(double value);
@@ -1054,8 +1067,21 @@ internal static bool PyLong_Check(IntPtr ob)
[DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)]
internal static extern int PyLong_AsLong(IntPtr value);
- [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)]
- internal static extern uint PyLong_AsUnsignedLong(IntPtr value);
+ [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl,
+ EntryPoint = "PyLong_AsUnsignedLong")]
+ internal static extern uint PyLong_AsUnsignedLong32(IntPtr value);
+
+ [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl,
+ EntryPoint = "PyLong_AsUnsignedLong")]
+ internal static extern ulong PyLong_AsUnsignedLong64(IntPtr value);
+
+ internal static object PyLong_AsUnsignedLong(IntPtr value)
+ {
+ if (Is32Bit || IsWindows)
+ return PyLong_AsUnsignedLong32(value);
+ else
+ return PyLong_AsUnsignedLong64(value);
+ }
[DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)]
internal static extern long PyLong_AsLongLong(IntPtr value);
From 1cb8e8cfcc1c168c11fe429747dd7af3e06aec8b Mon Sep 17 00:00:00 2001
From: amos402
Date: Sun, 13 Oct 2019 17:37:43 +0800
Subject: [PATCH 032/998] Fixed deadlock on finalizer after shutdown
---
src/runtime/runtime.cs | 15 +++++++++++++++
src/runtime/typemanager.cs | 2 ++
2 files changed, 17 insertions(+)
diff --git a/src/runtime/runtime.cs b/src/runtime/runtime.cs
index e125629a1..fc4b5484d 100644
--- a/src/runtime/runtime.cs
+++ b/src/runtime/runtime.cs
@@ -193,6 +193,11 @@ internal static void Initialize(bool initSigs = false, bool softShutdown = false
}
MainManagedThreadId = Thread.CurrentThread.ManagedThreadId;
}
+ else
+ {
+ PyGILState_Ensure();
+ MainManagedThreadId = Thread.CurrentThread.ManagedThreadId;
+ }
IsFinalizing = false;
@@ -406,6 +411,16 @@ internal static void Shutdown(bool soft)
PyGC_Collect();
RuntimeState.Restore();
ResetPyMembers();
+ GC.Collect();
+ try
+ {
+ GC.WaitForFullGCComplete();
+ }
+ catch (NotImplementedException)
+ {
+ // Some clr runtime didn't implement GC.WaitForFullGCComplete yet.
+ }
+ PyEval_SaveThread();
return;
}
ResetPyMembers();
diff --git a/src/runtime/typemanager.cs b/src/runtime/typemanager.cs
index 1fbefc973..72fe9cd67 100644
--- a/src/runtime/typemanager.cs
+++ b/src/runtime/typemanager.cs
@@ -1050,8 +1050,10 @@ private void ResetSlots()
return;
}
_alredyReset = true;
+#if DEBUG
IntPtr tp_name = Marshal.ReadIntPtr(_type, TypeOffset.tp_name);
string typeName = Marshal.PtrToStringAnsi(tp_name);
+#endif
foreach (var offset in _slots.Keys)
{
IntPtr ptr = GetDefaultSlot(offset);
From 4a9457fed54d2eeee3860a10f2b8c59f48aef043 Mon Sep 17 00:00:00 2001
From: Mohamed Koubaa
Date: Fri, 18 Oct 2019 03:07:57 -0500
Subject: [PATCH 033/998] Provide hook to implement __repr__ (#808)
Provide hook to implement __repr__
---
AUTHORS.md | 1 +
CHANGELOG.md | 1 +
src/runtime/classbase.cs | 38 ++++++++++++
src/runtime/exceptions.cs | 23 +++++++
src/testing/Python.Test.csproj | 3 +-
src/testing/ReprTest.cs | 108 +++++++++++++++++++++++++++++++++
src/tests/test_exceptions.py | 7 +--
src/tests/test_repr.py | 68 +++++++++++++++++++++
src/tests/tests.pyproj | 1 +
9 files changed, 244 insertions(+), 6 deletions(-)
create mode 100644 src/testing/ReprTest.cs
create mode 100644 src/tests/test_repr.py
diff --git a/AUTHORS.md b/AUTHORS.md
index 9e13ca569..9253c7e55 100644
--- a/AUTHORS.md
+++ b/AUTHORS.md
@@ -41,6 +41,7 @@
- Luke Stratman ([@lstratman](https://github.com/lstratman))
- Konstantin Posudevskiy ([@konstantin-posudevskiy](https://github.com/konstantin-posudevskiy))
- Matthias Dittrich ([@matthid](https://github.com/matthid))
+- Mohamed Koubaa ([@koubaa](https://github.com/koubaa))
- Patrick Stewart ([@patstew](https://github.com/patstew))
- Raphael Nestler ([@rnestler](https://github.com/rnestler))
- Rickard Holmberg ([@rickardraysearch](https://github.com/rickardraysearch))
diff --git a/CHANGELOG.md b/CHANGELOG.md
index b5b11cd77..e24a46904 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -46,6 +46,7 @@ This document follows the conventions laid out in [Keep a CHANGELOG][].
- Added PyObject finalizer support, Python objects referred by C# can be auto collect now ([#692][p692]).
- Added detailed comments about aproaches and dangers to handle multi-app-domains ([#625][p625])
- Python 3.7 support, builds and testing added. Defaults changed from Python 3.6 to 3.7 ([#698][p698])
+- Added support for C# types to provide `__repr__` ([#680][p680])
### Changed
diff --git a/src/runtime/classbase.cs b/src/runtime/classbase.cs
index 5846fa82a..41636c404 100644
--- a/src/runtime/classbase.cs
+++ b/src/runtime/classbase.cs
@@ -246,6 +246,44 @@ public static IntPtr tp_str(IntPtr ob)
}
}
+ public static IntPtr tp_repr(IntPtr ob)
+ {
+ var co = GetManagedObject(ob) as CLRObject;
+ if (co == null)
+ {
+ return Exceptions.RaiseTypeError("invalid object");
+ }
+ try
+ {
+ //if __repr__ is defined, use it
+ var instType = co.inst.GetType();
+ System.Reflection.MethodInfo methodInfo = instType.GetMethod("__repr__");
+ if (methodInfo != null && methodInfo.IsPublic)
+ {
+ var reprString = methodInfo.Invoke(co.inst, null) as string;
+ return Runtime.PyString_FromString(reprString);
+ }
+
+ //otherwise use the standard object.__repr__(inst)
+ IntPtr args = Runtime.PyTuple_New(1);
+ Runtime.PyTuple_SetItem(args, 0, ob);
+ IntPtr reprFunc = Runtime.PyObject_GetAttrString(Runtime.PyBaseObjectType, "__repr__");
+ var output = Runtime.PyObject_Call(reprFunc, args, IntPtr.Zero);
+ Runtime.XDecref(args);
+ Runtime.XDecref(reprFunc);
+ return output;
+ }
+ catch (Exception e)
+ {
+ if (e.InnerException != null)
+ {
+ e = e.InnerException;
+ }
+ Exceptions.SetError(e);
+ return IntPtr.Zero;
+ }
+ }
+
///
/// Standard dealloc implementation for instances of reflected types.
diff --git a/src/runtime/exceptions.cs b/src/runtime/exceptions.cs
index 8bed0abfd..31c367eb2 100644
--- a/src/runtime/exceptions.cs
+++ b/src/runtime/exceptions.cs
@@ -36,6 +36,29 @@ internal static Exception ToException(IntPtr ob)
return e;
}
+ ///
+ /// Exception __repr__ implementation
+ ///
+ public new static IntPtr tp_repr(IntPtr 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.PyUnicode_FromString(message);
+ }
+
///
/// Exception __str__ implementation
///
diff --git a/src/testing/Python.Test.csproj b/src/testing/Python.Test.csproj
index 27639ed5a..6bf5c2d22 100644
--- a/src/testing/Python.Test.csproj
+++ b/src/testing/Python.Test.csproj
@@ -91,6 +91,7 @@
+
@@ -111,4 +112,4 @@
-
\ No newline at end of file
+
diff --git a/src/testing/ReprTest.cs b/src/testing/ReprTest.cs
new file mode 100644
index 000000000..48e93683a
--- /dev/null
+++ b/src/testing/ReprTest.cs
@@ -0,0 +1,108 @@
+using System;
+using System.Text;
+
+namespace Python.Test
+{
+ ///
+ /// Supports repr unit tests.
+ ///
+ public class ReprTest
+ {
+ public class Point
+ {
+ public Point(double x, double y)
+ {
+ X = x;
+ Y = y;
+ }
+
+ public double X { get; set; }
+ public double Y { get; set; }
+
+ public override string ToString()
+ {
+ return base.ToString() + ": X=" + X.ToString() + ", Y=" + Y.ToString();
+ }
+
+ public string __repr__()
+ {
+ return "Point(" + X.ToString() + "," + Y.ToString() + ")";
+ }
+ }
+
+ public class Foo
+ {
+ public string __repr__()
+ {
+ return "I implement __repr__() but not ToString()!";
+ }
+ }
+
+ public class Bar
+ {
+ public override string ToString()
+ {
+ return "I implement ToString() but not __repr__()!";
+ }
+ }
+
+ public class BazBase
+ {
+ public override string ToString()
+ {
+ return "Base class implementing ToString()!";
+ }
+ }
+
+ public class BazMiddle : BazBase
+ {
+ public override string ToString()
+ {
+ return "Middle class implementing ToString()!";
+ }
+ }
+
+ //implements ToString via BazMiddle
+ public class Baz : BazMiddle
+ {
+
+ }
+
+ public class Quux
+ {
+ public string ToString(string format)
+ {
+ return "I implement ToString() with an argument!";
+ }
+ }
+
+ public class QuuzBase
+ {
+ protected string __repr__()
+ {
+ return "I implement __repr__ but it isn't public!";
+ }
+ }
+
+ public class Quuz : QuuzBase
+ {
+
+ }
+
+ public class Corge
+ {
+ public string __repr__(int i)
+ {
+ return "__repr__ implemention with input parameter!";
+ }
+ }
+
+ public class Grault
+ {
+ public int __repr__()
+ {
+ return "__repr__ implemention with wrong return type!".Length;
+ }
+ }
+ }
+}
diff --git a/src/tests/test_exceptions.py b/src/tests/test_exceptions.py
index a10d9a183..c2f18d443 100644
--- a/src/tests/test_exceptions.py
+++ b/src/tests/test_exceptions.py
@@ -282,11 +282,8 @@ def test_python_compat_of_managed_exceptions():
assert e.args == (msg,)
assert isinstance(e.args, tuple)
- if PY3:
- strexp = "OverflowException('Simple message"
- assert repr(e)[:len(strexp)] == strexp
- elif PY2:
- assert repr(e) == "OverflowException(u'Simple message',)"
+ strexp = "OverflowException('Simple message"
+ assert repr(e)[:len(strexp)] == strexp
def test_exception_is_instance_of_system_object():
diff --git a/src/tests/test_repr.py b/src/tests/test_repr.py
new file mode 100644
index 000000000..d120b0c4c
--- /dev/null
+++ b/src/tests/test_repr.py
@@ -0,0 +1,68 @@
+# -*- coding: utf-8 -*-
+
+"""Test __repr__ output"""
+
+import System
+import pytest
+from Python.Test import ReprTest
+
+def test_basic():
+ """Test Point class which implements both ToString and __repr__ without inheritance"""
+ ob = ReprTest.Point(1,2)
+ # point implements ToString() and __repr__()
+ assert ob.__repr__() == "Point(1,2)"
+ assert str(ob) == "Python.Test.ReprTest+Point: X=1, Y=2"
+
+def test_system_string():
+ """Test system string"""
+ ob = System.String("hello")
+ assert str(ob) == "hello"
+ assert "
+
From f2e6f6f89dde86b2043d6945fdffc2858eed2348 Mon Sep 17 00:00:00 2001
From: Alex Helms
Date: Wed, 23 Oct 2019 01:32:44 -0700
Subject: [PATCH 034/998] Add function to set Py_NoSiteFlag global variable to
1 (#971)
* Add function to set Py_NoSiteFlag global variable to 1.
* Add myself to authors, update changelog.
---
AUTHORS.md | 1 +
CHANGELOG.md | 1 +
src/runtime/pythonengine.cs | 10 ++++++++++
src/runtime/runtime.cs | 26 ++++++++++++++++++++++++++
4 files changed, 38 insertions(+)
diff --git a/AUTHORS.md b/AUTHORS.md
index 9253c7e55..efa04c8f0 100644
--- a/AUTHORS.md
+++ b/AUTHORS.md
@@ -12,6 +12,7 @@
## Contributors
+- Alex Helms ([@alexhelms](https://github.com/alexhelms))
- Alexandre Catarino([@AlexCatarino](https://github.com/AlexCatarino))
- Arvid JB ([@ArvidJB](https://github.com/ArvidJB))
- Benoît Hudson ([@benoithudson](https://github.com/benoithudson))
diff --git a/CHANGELOG.md b/CHANGELOG.md
index e24a46904..c7cad9567 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,6 +10,7 @@ This document follows the conventions laid out in [Keep a CHANGELOG][].
### Added
- Added automatic NuGet package generation in appveyor and local builds
+- Added function that sets Py_NoSiteFlag to 1.
### Changed
diff --git a/src/runtime/pythonengine.cs b/src/runtime/pythonengine.cs
index c1b663d22..700543839 100644
--- a/src/runtime/pythonengine.cs
+++ b/src/runtime/pythonengine.cs
@@ -130,6 +130,16 @@ public static string Compiler
get { return Marshal.PtrToStringAnsi(Runtime.Py_GetCompiler()); }
}
+ ///
+ /// Set the NoSiteFlag to disable loading the site module.
+ /// Must be called before Initialize.
+ /// https://docs.python.org/3/c-api/init.html#c.Py_NoSiteFlag
+ ///
+ public static void SetNoSiteFlag()
+ {
+ Runtime.SetNoSiteFlag();
+ }
+
public static int RunSimpleString(string code)
{
return Runtime.PyRun_SimpleString(code);
diff --git a/src/runtime/runtime.cs b/src/runtime/runtime.cs
index 4985a57f5..a1f9a38aa 100644
--- a/src/runtime/runtime.cs
+++ b/src/runtime/runtime.cs
@@ -414,6 +414,8 @@ internal static int AtExit()
internal static IntPtr PyNoneType;
internal static IntPtr PyTypeType;
+ internal static IntPtr Py_NoSiteFlag;
+
#if PYTHON3
internal static IntPtr PyBytesType;
#endif
@@ -1884,5 +1886,29 @@ internal static IntPtr PyMem_Realloc(IntPtr ptr, long size)
[DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)]
internal static extern int Py_MakePendingCalls();
+
+ internal static void SetNoSiteFlag()
+ {
+ var loader = LibraryLoader.Get(OperatingSystem);
+
+ IntPtr dllLocal;
+ if (_PythonDll != "__Internal")
+ {
+ dllLocal = loader.Load(_PythonDll);
+ }
+
+ try
+ {
+ Py_NoSiteFlag = loader.GetFunction(dllLocal, "Py_NoSiteFlag");
+ Marshal.WriteInt32(Py_NoSiteFlag, 1);
+ }
+ finally
+ {
+ if (dllLocal != IntPtr.Zero)
+ {
+ loader.Free(dllLocal);
+ }
+ }
+ }
}
}
From 146353855b6ed2414f3c5fd1b4bf231c2a985da8 Mon Sep 17 00:00:00 2001
From: Victor
Date: Wed, 23 Oct 2019 06:05:17 -0700
Subject: [PATCH 035/998] Adds performance tests, that compare to published
NuGet (#975)
Add performance tests that compare to published NuGet
---
.editorconfig | 11 +
pythonnet.15.sln | 191 +++++++++++++++++-
.../BaselineComparisonBenchmarkBase.cs | 69 +++++++
src/perf_tests/BaselineComparisonConfig.cs | 47 +++++
src/perf_tests/BenchmarkTests.cs | 63 ++++++
src/perf_tests/Python.PerformanceTests.csproj | 34 ++++
src/perf_tests/PythonCallingNetBenchmark.cs | 46 +++++
7 files changed, 454 insertions(+), 7 deletions(-)
create mode 100644 src/perf_tests/BaselineComparisonBenchmarkBase.cs
create mode 100644 src/perf_tests/BaselineComparisonConfig.cs
create mode 100644 src/perf_tests/BenchmarkTests.cs
create mode 100644 src/perf_tests/Python.PerformanceTests.csproj
create mode 100644 src/perf_tests/PythonCallingNetBenchmark.cs
diff --git a/.editorconfig b/.editorconfig
index 2e7c58ffe..9e10931d0 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -19,6 +19,17 @@ indent_size = 2
[*.{csproj,pyproj,config}]
indent_size = 2
+# .NET formatting settings
+[*.{cs,vb}]
+dotnet_sort_system_directives_first = true
+dotnet_separate_import_directive_groups = true
+
+[*.cs]
+csharp_new_line_before_open_brace = true
+csharp_new_line_before_else = true
+csharp_new_line_before_catch = true
+csharp_new_line_before_finally = true
+
# Solution
[*.sln]
indent_style = tab
diff --git a/pythonnet.15.sln b/pythonnet.15.sln
index f2015e480..096dfbe9a 100644
--- a/pythonnet.15.sln
+++ b/pythonnet.15.sln
@@ -1,189 +1,366 @@
Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio 15
-VisualStudioVersion = 15.0.26730.3
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.29102.190
MinimumVisualStudioVersion = 10.0.40219.1
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Python.Runtime.15", "src/runtime/Python.Runtime.15.csproj", "{2759F4FF-716B-4828-916F-50FA86613DFC}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Python.Runtime.15", "src\runtime\Python.Runtime.15.csproj", "{2759F4FF-716B-4828-916F-50FA86613DFC}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Python.EmbeddingTest.15", "src/embed_tests/Python.EmbeddingTest.15.csproj", "{66B8D01A-9906-452A-B09E-BF75EA76468F}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Python.EmbeddingTest.15", "src\embed_tests\Python.EmbeddingTest.15.csproj", "{66B8D01A-9906-452A-B09E-BF75EA76468F}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "clrmodule.15", "src/clrmodule/clrmodule.15.csproj", "{E08678D4-9A52-4AD5-B63D-8EBC7399981B}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "clrmodule.15", "src\clrmodule\clrmodule.15.csproj", "{E08678D4-9A52-4AD5-B63D-8EBC7399981B}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Console.15", "src/console/Console.15.csproj", "{CDAD305F-8E72-492C-A314-64CF58D472A0}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Console.15", "src\console\Console.15.csproj", "{CDAD305F-8E72-492C-A314-64CF58D472A0}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Python.Test.15", "src/testing/Python.Test.15.csproj", "{F94B547A-E97E-4500-8D53-B4D64D076E5F}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Python.Test.15", "src\testing\Python.Test.15.csproj", "{F94B547A-E97E-4500-8D53-B4D64D076E5F}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Python.PerformanceTests", "src\perf_tests\Python.PerformanceTests.csproj", "{6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Repo", "Repo", "{441A0123-F4C6-4EE4-9AEE-315FD79BE2D5}"
+ ProjectSection(SolutionItems) = preProject
+ .editorconfig = .editorconfig
+ EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Debug|x64 = Debug|x64
+ Debug|x86 = Debug|x86
+ DebugMono|Any CPU = DebugMono|Any CPU
DebugMono|x64 = DebugMono|x64
DebugMono|x86 = DebugMono|x86
+ DebugMonoPY3|Any CPU = DebugMonoPY3|Any CPU
DebugMonoPY3|x64 = DebugMonoPY3|x64
DebugMonoPY3|x86 = DebugMonoPY3|x86
+ DebugWin|Any CPU = DebugWin|Any CPU
DebugWin|x64 = DebugWin|x64
DebugWin|x86 = DebugWin|x86
+ DebugWinPY3|Any CPU = DebugWinPY3|Any CPU
DebugWinPY3|x64 = DebugWinPY3|x64
DebugWinPY3|x86 = DebugWinPY3|x86
+ Release|Any CPU = Release|Any CPU
+ Release|x64 = Release|x64
+ Release|x86 = Release|x86
+ ReleaseMono|Any CPU = ReleaseMono|Any CPU
ReleaseMono|x64 = ReleaseMono|x64
ReleaseMono|x86 = ReleaseMono|x86
+ ReleaseMonoPY3|Any CPU = ReleaseMonoPY3|Any CPU
ReleaseMonoPY3|x64 = ReleaseMonoPY3|x64
ReleaseMonoPY3|x86 = ReleaseMonoPY3|x86
+ ReleaseWin|Any CPU = ReleaseWin|Any CPU
ReleaseWin|x64 = ReleaseWin|x64
ReleaseWin|x86 = ReleaseWin|x86
+ ReleaseWinPY3|Any CPU = ReleaseWinPY3|Any CPU
ReleaseWinPY3|x64 = ReleaseWinPY3|x64
ReleaseWinPY3|x86 = ReleaseWinPY3|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {2759F4FF-716B-4828-916F-50FA86613DFC}.Debug|Any CPU.ActiveCfg = DebugWinPY3|Any CPU
+ {2759F4FF-716B-4828-916F-50FA86613DFC}.Debug|Any CPU.Build.0 = DebugWinPY3|Any CPU
+ {2759F4FF-716B-4828-916F-50FA86613DFC}.Debug|x64.ActiveCfg = DebugWinPY3|Any CPU
+ {2759F4FF-716B-4828-916F-50FA86613DFC}.Debug|x64.Build.0 = DebugWinPY3|Any CPU
+ {2759F4FF-716B-4828-916F-50FA86613DFC}.Debug|x86.ActiveCfg = DebugWinPY3|Any CPU
+ {2759F4FF-716B-4828-916F-50FA86613DFC}.Debug|x86.Build.0 = DebugWinPY3|Any CPU
+ {2759F4FF-716B-4828-916F-50FA86613DFC}.DebugMono|Any CPU.ActiveCfg = DebugMono|Any CPU
+ {2759F4FF-716B-4828-916F-50FA86613DFC}.DebugMono|Any CPU.Build.0 = DebugMono|Any CPU
{2759F4FF-716B-4828-916F-50FA86613DFC}.DebugMono|x64.ActiveCfg = DebugMono|Any CPU
{2759F4FF-716B-4828-916F-50FA86613DFC}.DebugMono|x64.Build.0 = DebugMono|Any CPU
{2759F4FF-716B-4828-916F-50FA86613DFC}.DebugMono|x86.ActiveCfg = DebugMono|Any CPU
{2759F4FF-716B-4828-916F-50FA86613DFC}.DebugMono|x86.Build.0 = DebugMono|Any CPU
+ {2759F4FF-716B-4828-916F-50FA86613DFC}.DebugMonoPY3|Any CPU.ActiveCfg = DebugMonoPY3|Any CPU
+ {2759F4FF-716B-4828-916F-50FA86613DFC}.DebugMonoPY3|Any CPU.Build.0 = DebugMonoPY3|Any CPU
{2759F4FF-716B-4828-916F-50FA86613DFC}.DebugMonoPY3|x64.ActiveCfg = DebugMonoPY3|Any CPU
{2759F4FF-716B-4828-916F-50FA86613DFC}.DebugMonoPY3|x64.Build.0 = DebugMonoPY3|Any CPU
{2759F4FF-716B-4828-916F-50FA86613DFC}.DebugMonoPY3|x86.ActiveCfg = DebugMonoPY3|Any CPU
{2759F4FF-716B-4828-916F-50FA86613DFC}.DebugMonoPY3|x86.Build.0 = DebugMonoPY3|Any CPU
+ {2759F4FF-716B-4828-916F-50FA86613DFC}.DebugWin|Any CPU.ActiveCfg = DebugWin|Any CPU
+ {2759F4FF-716B-4828-916F-50FA86613DFC}.DebugWin|Any CPU.Build.0 = DebugWin|Any CPU
{2759F4FF-716B-4828-916F-50FA86613DFC}.DebugWin|x64.ActiveCfg = DebugWin|Any CPU
{2759F4FF-716B-4828-916F-50FA86613DFC}.DebugWin|x64.Build.0 = DebugWin|Any CPU
{2759F4FF-716B-4828-916F-50FA86613DFC}.DebugWin|x86.ActiveCfg = DebugWin|Any CPU
{2759F4FF-716B-4828-916F-50FA86613DFC}.DebugWin|x86.Build.0 = DebugWin|Any CPU
+ {2759F4FF-716B-4828-916F-50FA86613DFC}.DebugWinPY3|Any CPU.ActiveCfg = DebugWinPY3|Any CPU
+ {2759F4FF-716B-4828-916F-50FA86613DFC}.DebugWinPY3|Any CPU.Build.0 = DebugWinPY3|Any CPU
{2759F4FF-716B-4828-916F-50FA86613DFC}.DebugWinPY3|x64.ActiveCfg = DebugWinPY3|Any CPU
{2759F4FF-716B-4828-916F-50FA86613DFC}.DebugWinPY3|x64.Build.0 = DebugWinPY3|Any CPU
{2759F4FF-716B-4828-916F-50FA86613DFC}.DebugWinPY3|x86.ActiveCfg = DebugWinPY3|Any CPU
{2759F4FF-716B-4828-916F-50FA86613DFC}.DebugWinPY3|x86.Build.0 = DebugWinPY3|Any CPU
+ {2759F4FF-716B-4828-916F-50FA86613DFC}.Release|Any CPU.ActiveCfg = ReleaseWinPY3|Any CPU
+ {2759F4FF-716B-4828-916F-50FA86613DFC}.Release|Any CPU.Build.0 = ReleaseWinPY3|Any CPU
+ {2759F4FF-716B-4828-916F-50FA86613DFC}.Release|x64.ActiveCfg = ReleaseWinPY3|Any CPU
+ {2759F4FF-716B-4828-916F-50FA86613DFC}.Release|x64.Build.0 = ReleaseWinPY3|Any CPU
+ {2759F4FF-716B-4828-916F-50FA86613DFC}.Release|x86.ActiveCfg = ReleaseWinPY3|Any CPU
+ {2759F4FF-716B-4828-916F-50FA86613DFC}.Release|x86.Build.0 = ReleaseWinPY3|Any CPU
+ {2759F4FF-716B-4828-916F-50FA86613DFC}.ReleaseMono|Any CPU.ActiveCfg = ReleaseMono|Any CPU
+ {2759F4FF-716B-4828-916F-50FA86613DFC}.ReleaseMono|Any CPU.Build.0 = ReleaseMono|Any CPU
{2759F4FF-716B-4828-916F-50FA86613DFC}.ReleaseMono|x64.ActiveCfg = ReleaseMono|Any CPU
{2759F4FF-716B-4828-916F-50FA86613DFC}.ReleaseMono|x64.Build.0 = ReleaseMono|Any CPU
{2759F4FF-716B-4828-916F-50FA86613DFC}.ReleaseMono|x86.ActiveCfg = ReleaseMono|Any CPU
{2759F4FF-716B-4828-916F-50FA86613DFC}.ReleaseMono|x86.Build.0 = ReleaseMono|Any CPU
+ {2759F4FF-716B-4828-916F-50FA86613DFC}.ReleaseMonoPY3|Any CPU.ActiveCfg = ReleaseMonoPY3|Any CPU
+ {2759F4FF-716B-4828-916F-50FA86613DFC}.ReleaseMonoPY3|Any CPU.Build.0 = ReleaseMonoPY3|Any CPU
{2759F4FF-716B-4828-916F-50FA86613DFC}.ReleaseMonoPY3|x64.ActiveCfg = ReleaseMonoPY3|Any CPU
{2759F4FF-716B-4828-916F-50FA86613DFC}.ReleaseMonoPY3|x64.Build.0 = ReleaseMonoPY3|Any CPU
{2759F4FF-716B-4828-916F-50FA86613DFC}.ReleaseMonoPY3|x86.ActiveCfg = ReleaseMonoPY3|Any CPU
{2759F4FF-716B-4828-916F-50FA86613DFC}.ReleaseMonoPY3|x86.Build.0 = ReleaseMonoPY3|Any CPU
+ {2759F4FF-716B-4828-916F-50FA86613DFC}.ReleaseWin|Any CPU.ActiveCfg = ReleaseWin|Any CPU
+ {2759F4FF-716B-4828-916F-50FA86613DFC}.ReleaseWin|Any CPU.Build.0 = ReleaseWin|Any CPU
{2759F4FF-716B-4828-916F-50FA86613DFC}.ReleaseWin|x64.ActiveCfg = ReleaseWin|Any CPU
{2759F4FF-716B-4828-916F-50FA86613DFC}.ReleaseWin|x64.Build.0 = ReleaseWin|Any CPU
{2759F4FF-716B-4828-916F-50FA86613DFC}.ReleaseWin|x86.ActiveCfg = ReleaseWin|Any CPU
{2759F4FF-716B-4828-916F-50FA86613DFC}.ReleaseWin|x86.Build.0 = ReleaseWin|Any CPU
+ {2759F4FF-716B-4828-916F-50FA86613DFC}.ReleaseWinPY3|Any CPU.ActiveCfg = ReleaseWinPY3|Any CPU
+ {2759F4FF-716B-4828-916F-50FA86613DFC}.ReleaseWinPY3|Any CPU.Build.0 = ReleaseWinPY3|Any CPU
{2759F4FF-716B-4828-916F-50FA86613DFC}.ReleaseWinPY3|x64.ActiveCfg = ReleaseWinPY3|Any CPU
{2759F4FF-716B-4828-916F-50FA86613DFC}.ReleaseWinPY3|x64.Build.0 = ReleaseWinPY3|Any CPU
{2759F4FF-716B-4828-916F-50FA86613DFC}.ReleaseWinPY3|x86.ActiveCfg = ReleaseWinPY3|Any CPU
{2759F4FF-716B-4828-916F-50FA86613DFC}.ReleaseWinPY3|x86.Build.0 = ReleaseWinPY3|Any CPU
+ {66B8D01A-9906-452A-B09E-BF75EA76468F}.Debug|Any CPU.ActiveCfg = ReleaseWinPY3|x86
+ {66B8D01A-9906-452A-B09E-BF75EA76468F}.Debug|Any CPU.Build.0 = ReleaseWinPY3|x86
+ {66B8D01A-9906-452A-B09E-BF75EA76468F}.Debug|x64.ActiveCfg = DebugWinPY3|x64
+ {66B8D01A-9906-452A-B09E-BF75EA76468F}.Debug|x64.Build.0 = DebugWinPY3|x64
+ {66B8D01A-9906-452A-B09E-BF75EA76468F}.Debug|x86.ActiveCfg = DebugWinPY3|x86
+ {66B8D01A-9906-452A-B09E-BF75EA76468F}.Debug|x86.Build.0 = DebugWinPY3|x86
+ {66B8D01A-9906-452A-B09E-BF75EA76468F}.DebugMono|Any CPU.ActiveCfg = DebugMono|x86
{66B8D01A-9906-452A-B09E-BF75EA76468F}.DebugMono|x64.ActiveCfg = DebugMono|x64
{66B8D01A-9906-452A-B09E-BF75EA76468F}.DebugMono|x64.Build.0 = DebugMono|x64
{66B8D01A-9906-452A-B09E-BF75EA76468F}.DebugMono|x86.ActiveCfg = DebugMono|x86
{66B8D01A-9906-452A-B09E-BF75EA76468F}.DebugMono|x86.Build.0 = DebugMono|x86
+ {66B8D01A-9906-452A-B09E-BF75EA76468F}.DebugMonoPY3|Any CPU.ActiveCfg = DebugMonoPY3|x86
{66B8D01A-9906-452A-B09E-BF75EA76468F}.DebugMonoPY3|x64.ActiveCfg = DebugMonoPY3|x64
{66B8D01A-9906-452A-B09E-BF75EA76468F}.DebugMonoPY3|x64.Build.0 = DebugMonoPY3|x64
{66B8D01A-9906-452A-B09E-BF75EA76468F}.DebugMonoPY3|x86.ActiveCfg = DebugMonoPY3|x86
{66B8D01A-9906-452A-B09E-BF75EA76468F}.DebugMonoPY3|x86.Build.0 = DebugMonoPY3|x86
+ {66B8D01A-9906-452A-B09E-BF75EA76468F}.DebugWin|Any CPU.ActiveCfg = DebugWin|x86
{66B8D01A-9906-452A-B09E-BF75EA76468F}.DebugWin|x64.ActiveCfg = DebugWin|x64
{66B8D01A-9906-452A-B09E-BF75EA76468F}.DebugWin|x64.Build.0 = DebugWin|x64
{66B8D01A-9906-452A-B09E-BF75EA76468F}.DebugWin|x86.ActiveCfg = DebugWin|x86
{66B8D01A-9906-452A-B09E-BF75EA76468F}.DebugWin|x86.Build.0 = DebugWin|x86
+ {66B8D01A-9906-452A-B09E-BF75EA76468F}.DebugWinPY3|Any CPU.ActiveCfg = DebugWinPY3|x86
{66B8D01A-9906-452A-B09E-BF75EA76468F}.DebugWinPY3|x64.ActiveCfg = DebugWinPY3|x64
{66B8D01A-9906-452A-B09E-BF75EA76468F}.DebugWinPY3|x64.Build.0 = DebugWinPY3|x64
{66B8D01A-9906-452A-B09E-BF75EA76468F}.DebugWinPY3|x86.ActiveCfg = DebugWinPY3|x86
{66B8D01A-9906-452A-B09E-BF75EA76468F}.DebugWinPY3|x86.Build.0 = DebugWinPY3|x86
+ {66B8D01A-9906-452A-B09E-BF75EA76468F}.Release|Any CPU.ActiveCfg = ReleaseWinPY3|x86
+ {66B8D01A-9906-452A-B09E-BF75EA76468F}.Release|Any CPU.Build.0 = ReleaseWinPY3|x86
+ {66B8D01A-9906-452A-B09E-BF75EA76468F}.Release|x64.ActiveCfg = ReleaseWinPY3|x64
+ {66B8D01A-9906-452A-B09E-BF75EA76468F}.Release|x64.Build.0 = ReleaseWinPY3|x64
+ {66B8D01A-9906-452A-B09E-BF75EA76468F}.Release|x86.ActiveCfg = ReleaseWinPY3|x86
+ {66B8D01A-9906-452A-B09E-BF75EA76468F}.Release|x86.Build.0 = ReleaseWinPY3|x86
+ {66B8D01A-9906-452A-B09E-BF75EA76468F}.ReleaseMono|Any CPU.ActiveCfg = ReleaseMono|x86
{66B8D01A-9906-452A-B09E-BF75EA76468F}.ReleaseMono|x64.ActiveCfg = ReleaseMono|x64
{66B8D01A-9906-452A-B09E-BF75EA76468F}.ReleaseMono|x64.Build.0 = ReleaseMono|x64
{66B8D01A-9906-452A-B09E-BF75EA76468F}.ReleaseMono|x86.ActiveCfg = ReleaseMono|x86
{66B8D01A-9906-452A-B09E-BF75EA76468F}.ReleaseMono|x86.Build.0 = ReleaseMono|x86
+ {66B8D01A-9906-452A-B09E-BF75EA76468F}.ReleaseMonoPY3|Any CPU.ActiveCfg = ReleaseMonoPY3|x86
{66B8D01A-9906-452A-B09E-BF75EA76468F}.ReleaseMonoPY3|x64.ActiveCfg = ReleaseMonoPY3|x64
{66B8D01A-9906-452A-B09E-BF75EA76468F}.ReleaseMonoPY3|x64.Build.0 = ReleaseMonoPY3|x64
{66B8D01A-9906-452A-B09E-BF75EA76468F}.ReleaseMonoPY3|x86.ActiveCfg = ReleaseMonoPY3|x86
{66B8D01A-9906-452A-B09E-BF75EA76468F}.ReleaseMonoPY3|x86.Build.0 = ReleaseMonoPY3|x86
+ {66B8D01A-9906-452A-B09E-BF75EA76468F}.ReleaseWin|Any CPU.ActiveCfg = ReleaseWin|x86
{66B8D01A-9906-452A-B09E-BF75EA76468F}.ReleaseWin|x64.ActiveCfg = ReleaseWin|x64
{66B8D01A-9906-452A-B09E-BF75EA76468F}.ReleaseWin|x64.Build.0 = ReleaseWin|x64
{66B8D01A-9906-452A-B09E-BF75EA76468F}.ReleaseWin|x86.ActiveCfg = ReleaseWin|x86
{66B8D01A-9906-452A-B09E-BF75EA76468F}.ReleaseWin|x86.Build.0 = ReleaseWin|x86
+ {66B8D01A-9906-452A-B09E-BF75EA76468F}.ReleaseWinPY3|Any CPU.ActiveCfg = ReleaseWinPY3|x86
{66B8D01A-9906-452A-B09E-BF75EA76468F}.ReleaseWinPY3|x64.ActiveCfg = ReleaseWinPY3|x64
{66B8D01A-9906-452A-B09E-BF75EA76468F}.ReleaseWinPY3|x64.Build.0 = ReleaseWinPY3|x64
{66B8D01A-9906-452A-B09E-BF75EA76468F}.ReleaseWinPY3|x86.ActiveCfg = ReleaseWinPY3|x86
{66B8D01A-9906-452A-B09E-BF75EA76468F}.ReleaseWinPY3|x86.Build.0 = ReleaseWinPY3|x86
+ {E08678D4-9A52-4AD5-B63D-8EBC7399981B}.Debug|Any CPU.ActiveCfg = ReleaseWinPY3|x86
+ {E08678D4-9A52-4AD5-B63D-8EBC7399981B}.Debug|Any CPU.Build.0 = ReleaseWinPY3|x86
+ {E08678D4-9A52-4AD5-B63D-8EBC7399981B}.Debug|x64.ActiveCfg = DebugWinPY3|x64
+ {E08678D4-9A52-4AD5-B63D-8EBC7399981B}.Debug|x64.Build.0 = DebugWinPY3|x64
+ {E08678D4-9A52-4AD5-B63D-8EBC7399981B}.Debug|x86.ActiveCfg = DebugWinPY3|x86
+ {E08678D4-9A52-4AD5-B63D-8EBC7399981B}.Debug|x86.Build.0 = DebugWinPY3|x86
+ {E08678D4-9A52-4AD5-B63D-8EBC7399981B}.DebugMono|Any CPU.ActiveCfg = DebugMono|x86
{E08678D4-9A52-4AD5-B63D-8EBC7399981B}.DebugMono|x64.ActiveCfg = DebugMono|x64
{E08678D4-9A52-4AD5-B63D-8EBC7399981B}.DebugMono|x86.ActiveCfg = DebugMono|x86
+ {E08678D4-9A52-4AD5-B63D-8EBC7399981B}.DebugMonoPY3|Any CPU.ActiveCfg = DebugMonoPY3|x86
{E08678D4-9A52-4AD5-B63D-8EBC7399981B}.DebugMonoPY3|x64.ActiveCfg = DebugMonoPY3|x64
{E08678D4-9A52-4AD5-B63D-8EBC7399981B}.DebugMonoPY3|x86.ActiveCfg = DebugMonoPY3|x86
+ {E08678D4-9A52-4AD5-B63D-8EBC7399981B}.DebugWin|Any CPU.ActiveCfg = DebugWin|x86
{E08678D4-9A52-4AD5-B63D-8EBC7399981B}.DebugWin|x64.ActiveCfg = DebugWin|x64
{E08678D4-9A52-4AD5-B63D-8EBC7399981B}.DebugWin|x64.Build.0 = DebugWin|x64
{E08678D4-9A52-4AD5-B63D-8EBC7399981B}.DebugWin|x86.ActiveCfg = DebugWin|x86
{E08678D4-9A52-4AD5-B63D-8EBC7399981B}.DebugWin|x86.Build.0 = DebugWin|x86
+ {E08678D4-9A52-4AD5-B63D-8EBC7399981B}.DebugWinPY3|Any CPU.ActiveCfg = DebugWinPY3|x86
{E08678D4-9A52-4AD5-B63D-8EBC7399981B}.DebugWinPY3|x64.ActiveCfg = DebugWinPY3|x64
{E08678D4-9A52-4AD5-B63D-8EBC7399981B}.DebugWinPY3|x64.Build.0 = DebugWinPY3|x64
{E08678D4-9A52-4AD5-B63D-8EBC7399981B}.DebugWinPY3|x86.ActiveCfg = DebugWinPY3|x86
{E08678D4-9A52-4AD5-B63D-8EBC7399981B}.DebugWinPY3|x86.Build.0 = DebugWinPY3|x86
+ {E08678D4-9A52-4AD5-B63D-8EBC7399981B}.Release|Any CPU.ActiveCfg = ReleaseWinPY3|x86
+ {E08678D4-9A52-4AD5-B63D-8EBC7399981B}.Release|Any CPU.Build.0 = ReleaseWinPY3|x86
+ {E08678D4-9A52-4AD5-B63D-8EBC7399981B}.Release|x64.ActiveCfg = ReleaseWinPY3|x64
+ {E08678D4-9A52-4AD5-B63D-8EBC7399981B}.Release|x64.Build.0 = ReleaseWinPY3|x64
+ {E08678D4-9A52-4AD5-B63D-8EBC7399981B}.Release|x86.ActiveCfg = ReleaseWinPY3|x86
+ {E08678D4-9A52-4AD5-B63D-8EBC7399981B}.Release|x86.Build.0 = ReleaseWinPY3|x86
+ {E08678D4-9A52-4AD5-B63D-8EBC7399981B}.ReleaseMono|Any CPU.ActiveCfg = ReleaseMono|x86
{E08678D4-9A52-4AD5-B63D-8EBC7399981B}.ReleaseMono|x64.ActiveCfg = ReleaseMono|x64
{E08678D4-9A52-4AD5-B63D-8EBC7399981B}.ReleaseMono|x86.ActiveCfg = ReleaseMono|x86
+ {E08678D4-9A52-4AD5-B63D-8EBC7399981B}.ReleaseMonoPY3|Any CPU.ActiveCfg = ReleaseMonoPY3|x86
{E08678D4-9A52-4AD5-B63D-8EBC7399981B}.ReleaseMonoPY3|x64.ActiveCfg = ReleaseMonoPY3|x64
{E08678D4-9A52-4AD5-B63D-8EBC7399981B}.ReleaseMonoPY3|x86.ActiveCfg = ReleaseMonoPY3|x86
+ {E08678D4-9A52-4AD5-B63D-8EBC7399981B}.ReleaseWin|Any CPU.ActiveCfg = ReleaseWin|x86
{E08678D4-9A52-4AD5-B63D-8EBC7399981B}.ReleaseWin|x64.ActiveCfg = ReleaseWin|x64
{E08678D4-9A52-4AD5-B63D-8EBC7399981B}.ReleaseWin|x64.Build.0 = ReleaseWin|x64
{E08678D4-9A52-4AD5-B63D-8EBC7399981B}.ReleaseWin|x86.ActiveCfg = ReleaseWin|x86
{E08678D4-9A52-4AD5-B63D-8EBC7399981B}.ReleaseWin|x86.Build.0 = ReleaseWin|x86
+ {E08678D4-9A52-4AD5-B63D-8EBC7399981B}.ReleaseWinPY3|Any CPU.ActiveCfg = ReleaseWinPY3|x86
{E08678D4-9A52-4AD5-B63D-8EBC7399981B}.ReleaseWinPY3|x64.ActiveCfg = ReleaseWinPY3|x64
{E08678D4-9A52-4AD5-B63D-8EBC7399981B}.ReleaseWinPY3|x64.Build.0 = ReleaseWinPY3|x64
{E08678D4-9A52-4AD5-B63D-8EBC7399981B}.ReleaseWinPY3|x86.ActiveCfg = ReleaseWinPY3|x86
{E08678D4-9A52-4AD5-B63D-8EBC7399981B}.ReleaseWinPY3|x86.Build.0 = ReleaseWinPY3|x86
+ {CDAD305F-8E72-492C-A314-64CF58D472A0}.Debug|Any CPU.ActiveCfg = ReleaseWinPY3|x86
+ {CDAD305F-8E72-492C-A314-64CF58D472A0}.Debug|Any CPU.Build.0 = ReleaseWinPY3|x86
+ {CDAD305F-8E72-492C-A314-64CF58D472A0}.Debug|x64.ActiveCfg = DebugWinPY3|x64
+ {CDAD305F-8E72-492C-A314-64CF58D472A0}.Debug|x64.Build.0 = DebugWinPY3|x64
+ {CDAD305F-8E72-492C-A314-64CF58D472A0}.Debug|x86.ActiveCfg = DebugWinPY3|x86
+ {CDAD305F-8E72-492C-A314-64CF58D472A0}.Debug|x86.Build.0 = DebugWinPY3|x86
+ {CDAD305F-8E72-492C-A314-64CF58D472A0}.DebugMono|Any CPU.ActiveCfg = DebugMono|x86
{CDAD305F-8E72-492C-A314-64CF58D472A0}.DebugMono|x64.ActiveCfg = DebugMono|x64
{CDAD305F-8E72-492C-A314-64CF58D472A0}.DebugMono|x64.Build.0 = DebugMono|x64
{CDAD305F-8E72-492C-A314-64CF58D472A0}.DebugMono|x86.ActiveCfg = DebugMono|x86
{CDAD305F-8E72-492C-A314-64CF58D472A0}.DebugMono|x86.Build.0 = DebugMono|x86
+ {CDAD305F-8E72-492C-A314-64CF58D472A0}.DebugMonoPY3|Any CPU.ActiveCfg = DebugMonoPY3|x86
{CDAD305F-8E72-492C-A314-64CF58D472A0}.DebugMonoPY3|x64.ActiveCfg = DebugMonoPY3|x64
{CDAD305F-8E72-492C-A314-64CF58D472A0}.DebugMonoPY3|x64.Build.0 = DebugMonoPY3|x64
{CDAD305F-8E72-492C-A314-64CF58D472A0}.DebugMonoPY3|x86.ActiveCfg = DebugMonoPY3|x86
{CDAD305F-8E72-492C-A314-64CF58D472A0}.DebugMonoPY3|x86.Build.0 = DebugMonoPY3|x86
+ {CDAD305F-8E72-492C-A314-64CF58D472A0}.DebugWin|Any CPU.ActiveCfg = DebugWin|x86
{CDAD305F-8E72-492C-A314-64CF58D472A0}.DebugWin|x64.ActiveCfg = DebugWin|x64
{CDAD305F-8E72-492C-A314-64CF58D472A0}.DebugWin|x64.Build.0 = DebugWin|x64
{CDAD305F-8E72-492C-A314-64CF58D472A0}.DebugWin|x86.ActiveCfg = DebugWin|x86
{CDAD305F-8E72-492C-A314-64CF58D472A0}.DebugWin|x86.Build.0 = DebugWin|x86
+ {CDAD305F-8E72-492C-A314-64CF58D472A0}.DebugWinPY3|Any CPU.ActiveCfg = DebugWinPY3|x86
{CDAD305F-8E72-492C-A314-64CF58D472A0}.DebugWinPY3|x64.ActiveCfg = DebugWinPY3|x64
{CDAD305F-8E72-492C-A314-64CF58D472A0}.DebugWinPY3|x64.Build.0 = DebugWinPY3|x64
{CDAD305F-8E72-492C-A314-64CF58D472A0}.DebugWinPY3|x86.ActiveCfg = DebugWinPY3|x86
{CDAD305F-8E72-492C-A314-64CF58D472A0}.DebugWinPY3|x86.Build.0 = DebugWinPY3|x86
+ {CDAD305F-8E72-492C-A314-64CF58D472A0}.Release|Any CPU.ActiveCfg = ReleaseWinPY3|x86
+ {CDAD305F-8E72-492C-A314-64CF58D472A0}.Release|Any CPU.Build.0 = ReleaseWinPY3|x86
+ {CDAD305F-8E72-492C-A314-64CF58D472A0}.Release|x64.ActiveCfg = ReleaseWinPY3|x64
+ {CDAD305F-8E72-492C-A314-64CF58D472A0}.Release|x64.Build.0 = ReleaseWinPY3|x64
+ {CDAD305F-8E72-492C-A314-64CF58D472A0}.Release|x86.ActiveCfg = ReleaseWinPY3|x86
+ {CDAD305F-8E72-492C-A314-64CF58D472A0}.Release|x86.Build.0 = ReleaseWinPY3|x86
+ {CDAD305F-8E72-492C-A314-64CF58D472A0}.ReleaseMono|Any CPU.ActiveCfg = ReleaseMono|x86
{CDAD305F-8E72-492C-A314-64CF58D472A0}.ReleaseMono|x64.ActiveCfg = ReleaseMono|x64
{CDAD305F-8E72-492C-A314-64CF58D472A0}.ReleaseMono|x64.Build.0 = ReleaseMono|x64
{CDAD305F-8E72-492C-A314-64CF58D472A0}.ReleaseMono|x86.ActiveCfg = ReleaseMono|x86
{CDAD305F-8E72-492C-A314-64CF58D472A0}.ReleaseMono|x86.Build.0 = ReleaseMono|x86
+ {CDAD305F-8E72-492C-A314-64CF58D472A0}.ReleaseMonoPY3|Any CPU.ActiveCfg = ReleaseMonoPY3|x86
{CDAD305F-8E72-492C-A314-64CF58D472A0}.ReleaseMonoPY3|x64.ActiveCfg = ReleaseMonoPY3|x64
{CDAD305F-8E72-492C-A314-64CF58D472A0}.ReleaseMonoPY3|x64.Build.0 = ReleaseMonoPY3|x64
{CDAD305F-8E72-492C-A314-64CF58D472A0}.ReleaseMonoPY3|x86.ActiveCfg = ReleaseMonoPY3|x86
{CDAD305F-8E72-492C-A314-64CF58D472A0}.ReleaseMonoPY3|x86.Build.0 = ReleaseMonoPY3|x86
+ {CDAD305F-8E72-492C-A314-64CF58D472A0}.ReleaseWin|Any CPU.ActiveCfg = ReleaseWin|x86
{CDAD305F-8E72-492C-A314-64CF58D472A0}.ReleaseWin|x64.ActiveCfg = ReleaseWin|x64
{CDAD305F-8E72-492C-A314-64CF58D472A0}.ReleaseWin|x64.Build.0 = ReleaseWin|x64
{CDAD305F-8E72-492C-A314-64CF58D472A0}.ReleaseWin|x86.ActiveCfg = ReleaseWin|x86
{CDAD305F-8E72-492C-A314-64CF58D472A0}.ReleaseWin|x86.Build.0 = ReleaseWin|x86
+ {CDAD305F-8E72-492C-A314-64CF58D472A0}.ReleaseWinPY3|Any CPU.ActiveCfg = ReleaseWinPY3|x86
{CDAD305F-8E72-492C-A314-64CF58D472A0}.ReleaseWinPY3|x64.ActiveCfg = ReleaseWinPY3|x64
{CDAD305F-8E72-492C-A314-64CF58D472A0}.ReleaseWinPY3|x64.Build.0 = ReleaseWinPY3|x64
{CDAD305F-8E72-492C-A314-64CF58D472A0}.ReleaseWinPY3|x86.ActiveCfg = ReleaseWinPY3|x86
{CDAD305F-8E72-492C-A314-64CF58D472A0}.ReleaseWinPY3|x86.Build.0 = ReleaseWinPY3|x86
+ {F94B547A-E97E-4500-8D53-B4D64D076E5F}.Debug|Any CPU.ActiveCfg = ReleaseWinPY3|x86
+ {F94B547A-E97E-4500-8D53-B4D64D076E5F}.Debug|Any CPU.Build.0 = ReleaseWinPY3|x86
+ {F94B547A-E97E-4500-8D53-B4D64D076E5F}.Debug|x64.ActiveCfg = DebugWinPY3|x64
+ {F94B547A-E97E-4500-8D53-B4D64D076E5F}.Debug|x64.Build.0 = DebugWinPY3|x64
+ {F94B547A-E97E-4500-8D53-B4D64D076E5F}.Debug|x86.ActiveCfg = DebugWinPY3|x86
+ {F94B547A-E97E-4500-8D53-B4D64D076E5F}.Debug|x86.Build.0 = DebugWinPY3|x86
+ {F94B547A-E97E-4500-8D53-B4D64D076E5F}.DebugMono|Any CPU.ActiveCfg = DebugMono|x86
{F94B547A-E97E-4500-8D53-B4D64D076E5F}.DebugMono|x64.ActiveCfg = DebugMono|x64
{F94B547A-E97E-4500-8D53-B4D64D076E5F}.DebugMono|x64.Build.0 = DebugMono|x64
{F94B547A-E97E-4500-8D53-B4D64D076E5F}.DebugMono|x86.ActiveCfg = DebugMono|x86
{F94B547A-E97E-4500-8D53-B4D64D076E5F}.DebugMono|x86.Build.0 = DebugMono|x86
+ {F94B547A-E97E-4500-8D53-B4D64D076E5F}.DebugMonoPY3|Any CPU.ActiveCfg = DebugMonoPY3|x86
{F94B547A-E97E-4500-8D53-B4D64D076E5F}.DebugMonoPY3|x64.ActiveCfg = DebugMonoPY3|x64
{F94B547A-E97E-4500-8D53-B4D64D076E5F}.DebugMonoPY3|x64.Build.0 = DebugMonoPY3|x64
{F94B547A-E97E-4500-8D53-B4D64D076E5F}.DebugMonoPY3|x86.ActiveCfg = DebugMonoPY3|x86
{F94B547A-E97E-4500-8D53-B4D64D076E5F}.DebugMonoPY3|x86.Build.0 = DebugMonoPY3|x86
+ {F94B547A-E97E-4500-8D53-B4D64D076E5F}.DebugWin|Any CPU.ActiveCfg = DebugWin|x86
{F94B547A-E97E-4500-8D53-B4D64D076E5F}.DebugWin|x64.ActiveCfg = DebugWin|x64
{F94B547A-E97E-4500-8D53-B4D64D076E5F}.DebugWin|x64.Build.0 = DebugWin|x64
{F94B547A-E97E-4500-8D53-B4D64D076E5F}.DebugWin|x86.ActiveCfg = DebugWin|x86
{F94B547A-E97E-4500-8D53-B4D64D076E5F}.DebugWin|x86.Build.0 = DebugWin|x86
+ {F94B547A-E97E-4500-8D53-B4D64D076E5F}.DebugWinPY3|Any CPU.ActiveCfg = DebugWinPY3|x86
{F94B547A-E97E-4500-8D53-B4D64D076E5F}.DebugWinPY3|x64.ActiveCfg = DebugWinPY3|x64
{F94B547A-E97E-4500-8D53-B4D64D076E5F}.DebugWinPY3|x64.Build.0 = DebugWinPY3|x64
{F94B547A-E97E-4500-8D53-B4D64D076E5F}.DebugWinPY3|x86.ActiveCfg = DebugWinPY3|x86
{F94B547A-E97E-4500-8D53-B4D64D076E5F}.DebugWinPY3|x86.Build.0 = DebugWinPY3|x86
+ {F94B547A-E97E-4500-8D53-B4D64D076E5F}.Release|Any CPU.ActiveCfg = ReleaseWinPY3|x86
+ {F94B547A-E97E-4500-8D53-B4D64D076E5F}.Release|Any CPU.Build.0 = ReleaseWinPY3|x86
+ {F94B547A-E97E-4500-8D53-B4D64D076E5F}.Release|x64.ActiveCfg = ReleaseWinPY3|x64
+ {F94B547A-E97E-4500-8D53-B4D64D076E5F}.Release|x64.Build.0 = ReleaseWinPY3|x64
+ {F94B547A-E97E-4500-8D53-B4D64D076E5F}.Release|x86.ActiveCfg = ReleaseWinPY3|x86
+ {F94B547A-E97E-4500-8D53-B4D64D076E5F}.Release|x86.Build.0 = ReleaseWinPY3|x86
+ {F94B547A-E97E-4500-8D53-B4D64D076E5F}.ReleaseMono|Any CPU.ActiveCfg = ReleaseMono|x86
{F94B547A-E97E-4500-8D53-B4D64D076E5F}.ReleaseMono|x64.ActiveCfg = ReleaseMono|x64
{F94B547A-E97E-4500-8D53-B4D64D076E5F}.ReleaseMono|x64.Build.0 = ReleaseMono|x64
{F94B547A-E97E-4500-8D53-B4D64D076E5F}.ReleaseMono|x86.ActiveCfg = ReleaseMono|x86
{F94B547A-E97E-4500-8D53-B4D64D076E5F}.ReleaseMono|x86.Build.0 = ReleaseMono|x86
+ {F94B547A-E97E-4500-8D53-B4D64D076E5F}.ReleaseMonoPY3|Any CPU.ActiveCfg = ReleaseMonoPY3|x86
{F94B547A-E97E-4500-8D53-B4D64D076E5F}.ReleaseMonoPY3|x64.ActiveCfg = ReleaseMonoPY3|x64
{F94B547A-E97E-4500-8D53-B4D64D076E5F}.ReleaseMonoPY3|x64.Build.0 = ReleaseMonoPY3|x64
{F94B547A-E97E-4500-8D53-B4D64D076E5F}.ReleaseMonoPY3|x86.ActiveCfg = ReleaseMonoPY3|x86
{F94B547A-E97E-4500-8D53-B4D64D076E5F}.ReleaseMonoPY3|x86.Build.0 = ReleaseMonoPY3|x86
+ {F94B547A-E97E-4500-8D53-B4D64D076E5F}.ReleaseWin|Any CPU.ActiveCfg = ReleaseWin|x86
{F94B547A-E97E-4500-8D53-B4D64D076E5F}.ReleaseWin|x64.ActiveCfg = ReleaseWin|x64
{F94B547A-E97E-4500-8D53-B4D64D076E5F}.ReleaseWin|x64.Build.0 = ReleaseWin|x64
{F94B547A-E97E-4500-8D53-B4D64D076E5F}.ReleaseWin|x86.ActiveCfg = ReleaseWin|x86
{F94B547A-E97E-4500-8D53-B4D64D076E5F}.ReleaseWin|x86.Build.0 = ReleaseWin|x86
+ {F94B547A-E97E-4500-8D53-B4D64D076E5F}.ReleaseWinPY3|Any CPU.ActiveCfg = ReleaseWinPY3|x86
{F94B547A-E97E-4500-8D53-B4D64D076E5F}.ReleaseWinPY3|x64.ActiveCfg = ReleaseWinPY3|x64
{F94B547A-E97E-4500-8D53-B4D64D076E5F}.ReleaseWinPY3|x64.Build.0 = ReleaseWinPY3|x64
{F94B547A-E97E-4500-8D53-B4D64D076E5F}.ReleaseWinPY3|x86.ActiveCfg = ReleaseWinPY3|x86
{F94B547A-E97E-4500-8D53-B4D64D076E5F}.ReleaseWinPY3|x86.Build.0 = ReleaseWinPY3|x86
+ {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.Debug|Any CPU.ActiveCfg = DebugMono|Any CPU
+ {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.Debug|Any CPU.Build.0 = DebugMono|Any CPU
+ {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.Debug|x64.ActiveCfg = DebugMono|Any CPU
+ {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.Debug|x64.Build.0 = DebugMono|Any CPU
+ {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.Debug|x86.ActiveCfg = DebugMono|Any CPU
+ {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.Debug|x86.Build.0 = DebugMono|Any CPU
+ {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.DebugMono|Any CPU.ActiveCfg = DebugMono|Any CPU
+ {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.DebugMono|x64.ActiveCfg = DebugMono|Any CPU
+ {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.DebugMono|x86.ActiveCfg = DebugMono|Any CPU
+ {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.DebugMonoPY3|Any CPU.ActiveCfg = DebugMonoPY3|Any CPU
+ {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.DebugMonoPY3|x64.ActiveCfg = DebugMonoPY3|Any CPU
+ {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.DebugMonoPY3|x86.ActiveCfg = DebugMonoPY3|Any CPU
+ {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.DebugWin|Any CPU.ActiveCfg = DebugWin|Any CPU
+ {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.DebugWin|Any CPU.Build.0 = DebugWin|Any CPU
+ {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.DebugWin|x64.ActiveCfg = DebugWin|Any CPU
+ {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.DebugWin|x64.Build.0 = DebugWin|Any CPU
+ {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.DebugWin|x86.ActiveCfg = DebugWin|Any CPU
+ {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.DebugWin|x86.Build.0 = DebugWin|Any CPU
+ {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.DebugWinPY3|Any CPU.ActiveCfg = DebugWinPY3|Any CPU
+ {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.DebugWinPY3|Any CPU.Build.0 = DebugWinPY3|Any CPU
+ {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.DebugWinPY3|x64.ActiveCfg = DebugWinPY3|Any CPU
+ {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.DebugWinPY3|x64.Build.0 = DebugWinPY3|Any CPU
+ {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.DebugWinPY3|x86.ActiveCfg = DebugWinPY3|Any CPU
+ {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.DebugWinPY3|x86.Build.0 = DebugWinPY3|Any CPU
+ {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.Release|Any CPU.ActiveCfg = ReleaseMono|Any CPU
+ {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.Release|Any CPU.Build.0 = ReleaseMono|Any CPU
+ {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.Release|x64.ActiveCfg = ReleaseMono|Any CPU
+ {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.Release|x64.Build.0 = ReleaseMono|Any CPU
+ {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.Release|x86.ActiveCfg = ReleaseMono|Any CPU
+ {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.Release|x86.Build.0 = ReleaseMono|Any CPU
+ {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.ReleaseMono|Any CPU.ActiveCfg = ReleaseMono|Any CPU
+ {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.ReleaseMono|x64.ActiveCfg = ReleaseMono|Any CPU
+ {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.ReleaseMono|x86.ActiveCfg = ReleaseMono|Any CPU
+ {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.ReleaseMonoPY3|Any CPU.ActiveCfg = ReleaseMonoPY3|Any CPU
+ {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.ReleaseMonoPY3|x64.ActiveCfg = ReleaseMonoPY3|Any CPU
+ {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.ReleaseMonoPY3|x86.ActiveCfg = ReleaseMonoPY3|Any CPU
+ {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.ReleaseWin|Any CPU.ActiveCfg = ReleaseWin|Any CPU
+ {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.ReleaseWin|Any CPU.Build.0 = ReleaseWin|Any CPU
+ {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.ReleaseWin|x64.ActiveCfg = ReleaseWin|Any CPU
+ {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.ReleaseWin|x64.Build.0 = ReleaseWin|Any CPU
+ {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.ReleaseWin|x86.ActiveCfg = ReleaseWin|Any CPU
+ {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.ReleaseWin|x86.Build.0 = ReleaseWin|Any CPU
+ {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.ReleaseWinPY3|Any CPU.ActiveCfg = ReleaseWinPY3|Any CPU
+ {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.ReleaseWinPY3|Any CPU.Build.0 = ReleaseWinPY3|Any CPU
+ {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.ReleaseWinPY3|x64.ActiveCfg = ReleaseWinPY3|Any CPU
+ {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.ReleaseWinPY3|x64.Build.0 = ReleaseWinPY3|Any CPU
+ {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.ReleaseWinPY3|x86.ActiveCfg = ReleaseWinPY3|Any CPU
+ {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.ReleaseWinPY3|x86.Build.0 = ReleaseWinPY3|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/src/perf_tests/BaselineComparisonBenchmarkBase.cs b/src/perf_tests/BaselineComparisonBenchmarkBase.cs
new file mode 100644
index 000000000..2388e3982
--- /dev/null
+++ b/src/perf_tests/BaselineComparisonBenchmarkBase.cs
@@ -0,0 +1,69 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+
+using Python.Runtime;
+
+namespace Python.PerformanceTests
+{
+ public class BaselineComparisonBenchmarkBase
+ {
+ public BaselineComparisonBenchmarkBase()
+ {
+ Console.WriteLine($"CWD: {Environment.CurrentDirectory}");
+ Console.WriteLine($"Using Python.Runtime from {typeof(PythonEngine).Assembly.Location} {typeof(PythonEngine).Assembly.GetName()}");
+
+ try {
+ PythonEngine.Initialize();
+ Console.WriteLine("Python Initialized");
+ if (PythonEngine.BeginAllowThreads() == IntPtr.Zero)
+ throw new PythonException();
+ Console.WriteLine("Threading enabled");
+ }
+ catch (Exception e) {
+ Console.WriteLine(e);
+ throw;
+ }
+ }
+
+ static BaselineComparisonBenchmarkBase()
+ {
+ string pythonRuntimeDll = Environment.GetEnvironmentVariable(BaselineComparisonConfig.EnvironmentVariableName);
+ if (string.IsNullOrEmpty(pythonRuntimeDll))
+ {
+ throw new ArgumentException(
+ "Required environment variable is missing",
+ BaselineComparisonConfig.EnvironmentVariableName);
+ }
+
+ Console.WriteLine("Preloading " + pythonRuntimeDll);
+ Assembly.LoadFrom(pythonRuntimeDll);
+ foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies()) {
+ if (assembly.FullName.StartsWith("Python.Runtime"))
+ Console.WriteLine(assembly.Location);
+ foreach(var dependency in assembly.GetReferencedAssemblies())
+ if (dependency.FullName.Contains("Python.Runtime")) {
+ Console.WriteLine($"{assembly} -> {dependency}");
+ }
+ }
+
+ AppDomain.CurrentDomain.AssemblyResolve += CurrentDomainOnAssemblyResolve;
+ }
+
+ static Assembly CurrentDomainOnAssemblyResolve(object sender, ResolveEventArgs args) {
+ if (!args.Name.StartsWith("Python.Runtime"))
+ return null;
+
+ var preloaded = AppDomain.CurrentDomain.GetAssemblies()
+ .FirstOrDefault(a => a.GetName().Name == "Python.Runtime");
+ if (preloaded != null) return preloaded;
+
+ string pythonRuntimeDll = Environment.GetEnvironmentVariable(BaselineComparisonConfig.EnvironmentVariableName);
+ if (string.IsNullOrEmpty(pythonRuntimeDll))
+ return null;
+
+ return Assembly.LoadFrom(pythonRuntimeDll);
+ }
+ }
+}
diff --git a/src/perf_tests/BaselineComparisonConfig.cs b/src/perf_tests/BaselineComparisonConfig.cs
new file mode 100644
index 000000000..06d529ff9
--- /dev/null
+++ b/src/perf_tests/BaselineComparisonConfig.cs
@@ -0,0 +1,47 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Reflection;
+
+using BenchmarkDotNet.Configs;
+using BenchmarkDotNet.Jobs;
+
+namespace Python.PerformanceTests
+{
+ public class BaselineComparisonConfig : ManualConfig
+ {
+ public const string EnvironmentVariableName = "PythonRuntimeDLL";
+
+ public BaselineComparisonConfig()
+ {
+ this.Options |= ConfigOptions.DisableOptimizationsValidator;
+
+ string deploymentRoot = BenchmarkTests.DeploymentRoot;
+
+ var baseJob = Job.Default;
+ this.Add(baseJob
+ .WithId("baseline")
+ .WithEnvironmentVariable(EnvironmentVariableName,
+ Path.Combine(deploymentRoot, "baseline", "Python.Runtime.dll"))
+ .WithBaseline(true));
+ this.Add(baseJob
+ .WithId("new")
+ .WithEnvironmentVariable(EnvironmentVariableName,
+ Path.Combine(deploymentRoot, "new", "Python.Runtime.dll")));
+ }
+
+ static BaselineComparisonConfig() {
+ AppDomain.CurrentDomain.AssemblyResolve += CurrentDomainOnAssemblyResolve;
+ }
+
+ static Assembly CurrentDomainOnAssemblyResolve(object sender, ResolveEventArgs args) {
+ Console.WriteLine(args.Name);
+ if (!args.Name.StartsWith("Python.Runtime"))
+ return null;
+ string pythonRuntimeDll = Environment.GetEnvironmentVariable(EnvironmentVariableName);
+ if (string.IsNullOrEmpty(pythonRuntimeDll))
+ pythonRuntimeDll = Path.Combine(BenchmarkTests.DeploymentRoot, "baseline", "Python.Runtime.dll");
+ return Assembly.LoadFrom(pythonRuntimeDll);
+ }
+ }
+}
diff --git a/src/perf_tests/BenchmarkTests.cs b/src/perf_tests/BenchmarkTests.cs
new file mode 100644
index 000000000..12ba6c900
--- /dev/null
+++ b/src/perf_tests/BenchmarkTests.cs
@@ -0,0 +1,63 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Runtime.CompilerServices;
+using System.Reflection;
+
+using BenchmarkDotNet.Reports;
+using BenchmarkDotNet.Running;
+using NUnit.Framework;
+
+namespace Python.PerformanceTests
+{
+ public class BenchmarkTests
+ {
+ Summary summary;
+
+ [OneTimeSetUp]
+ public void SetUp()
+ {
+ Environment.CurrentDirectory = Path.Combine(DeploymentRoot, "new");
+ this.summary = BenchmarkRunner.Run();
+ Assert.IsNotEmpty(this.summary.Reports);
+ Assert.IsTrue(this.summary.Reports.All(r => r.Success));
+ }
+
+ [Test]
+ public void ReadInt64Property()
+ {
+ double optimisticPerfRatio = GetOptimisticPerfRatio(this.summary.Reports);
+ Assert.LessOrEqual(optimisticPerfRatio, 0.68);
+ }
+
+ [Test]
+ public void WriteInt64Property()
+ {
+ double optimisticPerfRatio = GetOptimisticPerfRatio(this.summary.Reports);
+ Assert.LessOrEqual(optimisticPerfRatio, 0.66);
+ }
+
+ static double GetOptimisticPerfRatio(
+ IReadOnlyList reports,
+ [CallerMemberName] string methodName = null)
+ {
+ reports = reports.Where(r => r.BenchmarkCase.Descriptor.WorkloadMethod.Name == methodName).ToArray();
+ if (reports.Count == 0)
+ throw new ArgumentException(
+ message: $"No reports found for {methodName}. "
+ + "You have to match test method name to benchmark method name or "
+ + "pass benchmark method name explicitly",
+ paramName: nameof(methodName));
+
+ var baseline = reports.Single(r => r.BenchmarkCase.Job.ResolvedId == "baseline").ResultStatistics;
+ var @new = reports.Single(r => r.BenchmarkCase.Job.ResolvedId != "baseline").ResultStatistics;
+
+ double newTimeOptimistic = @new.Mean - (@new.StandardDeviation + baseline.StandardDeviation) * 0.5;
+
+ return newTimeOptimistic / baseline.Mean;
+ }
+
+ public static string DeploymentRoot => Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
+ }
+}
diff --git a/src/perf_tests/Python.PerformanceTests.csproj b/src/perf_tests/Python.PerformanceTests.csproj
new file mode 100644
index 000000000..33949fdc1
--- /dev/null
+++ b/src/perf_tests/Python.PerformanceTests.csproj
@@ -0,0 +1,34 @@
+
+
+
+ net461
+ DebugMono;DebugMonoPY3;ReleaseMono;ReleaseMonoPY3;DebugWin;DebugWinPY3;ReleaseWin;ReleaseWinPY3
+
+ false
+
+
+
+
+
+
+
+
+ compile
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/perf_tests/PythonCallingNetBenchmark.cs b/src/perf_tests/PythonCallingNetBenchmark.cs
new file mode 100644
index 000000000..4e9461d2e
--- /dev/null
+++ b/src/perf_tests/PythonCallingNetBenchmark.cs
@@ -0,0 +1,46 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+using BenchmarkDotNet.Attributes;
+using Python.Runtime;
+
+namespace Python.PerformanceTests
+{
+ [Config(typeof(BaselineComparisonConfig))]
+ public class PythonCallingNetBenchmark: BaselineComparisonBenchmarkBase
+ {
+ [Benchmark]
+ public void ReadInt64Property()
+ {
+ using (Py.GIL())
+ {
+ var locals = new PyDict();
+ locals.SetItem("a", new NetObject().ToPython());
+ PythonEngine.Exec($@"
+s = 0
+for i in range(300000):
+ s += a.{nameof(NetObject.LongProperty)}
+", locals: locals.Handle);
+ }
+ }
+
+ [Benchmark]
+ public void WriteInt64Property() {
+ using (Py.GIL()) {
+ var locals = new PyDict();
+ locals.SetItem("a", new NetObject().ToPython());
+ PythonEngine.Exec($@"
+s = 0
+for i in range(300000):
+ a.{nameof(NetObject.LongProperty)} += i
+", locals: locals.Handle);
+ }
+ }
+ }
+
+ class NetObject
+ {
+ public long LongProperty { get; set; } = 42;
+ }
+}
From e1931262c8d0f18e6bd55cb122510be417733794 Mon Sep 17 00:00:00 2001
From: Ivan Cronyn
Date: Wed, 13 Nov 2019 13:46:22 +0000
Subject: [PATCH 036/998] Adds support for the Jetson Nano (#986)
---
CHANGELOG.md | 1 +
src/runtime/platform/Types.cs | 1 +
src/runtime/runtime.cs | 1 +
3 files changed, 3 insertions(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c7cad9567..5c999d668 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -11,6 +11,7 @@ This document follows the conventions laid out in [Keep a CHANGELOG][].
- Added automatic NuGet package generation in appveyor and local builds
- Added function that sets Py_NoSiteFlag to 1.
+- Added support for Jetson Nano.
### Changed
diff --git a/src/runtime/platform/Types.cs b/src/runtime/platform/Types.cs
index bdc51af39..62be0e421 100644
--- a/src/runtime/platform/Types.cs
+++ b/src/runtime/platform/Types.cs
@@ -6,6 +6,7 @@ public enum MachineType
x86_64,
armv7l,
armv8,
+ aarch64,
Other
};
diff --git a/src/runtime/runtime.cs b/src/runtime/runtime.cs
index a1f9a38aa..f97821d13 100644
--- a/src/runtime/runtime.cs
+++ b/src/runtime/runtime.cs
@@ -143,6 +143,7 @@ public class Runtime
["em64t"] = MachineType.x86_64,
["armv7l"] = MachineType.armv7l,
["armv8"] = MachineType.armv8,
+ ["aarch64"] = MachineType.aarch64,
};
///
From 2736094ab87dcde26e63e1fddbb30a7a2453c961 Mon Sep 17 00:00:00 2001
From: matham
Date: Tue, 19 Nov 2019 01:19:29 -0500
Subject: [PATCH 037/998] Add CI support for py3.8. (#988)
* Add CI support for py3.8
* Add interop38.cs
* Add PYTHON38
* Add support for 3.8
* Bump 3.7 to 3.8
* Allow failures for py3.8 because it's a Python 3.8.0 bug
* Add note about py3.8.0 to readme
---
README.rst | 8 ++
appveyor.yml | 6 ++
src/runtime/Python.Runtime.15.csproj | 2 +-
src/runtime/Python.Runtime.csproj | 27 ++---
src/runtime/interop38.cs | 152 +++++++++++++++++++++++++++
src/runtime/runtime.cs | 5 +-
6 files changed, 185 insertions(+), 15 deletions(-)
create mode 100644 src/runtime/interop38.cs
diff --git a/README.rst b/README.rst
index 84bf93d84..ee6573d84 100644
--- a/README.rst
+++ b/README.rst
@@ -95,6 +95,14 @@ projects using pythonnet can be found in the Wiki:
https://github.com/pythonnet/pythonnet/wiki
+Python 3.8.0 support
+--------------------
+
+Some features are disabled in Python 3.8.0 because of
+`this bug in Python `_. The error is
+``System.EntryPointNotFoundException : Unable to find an entry point named
+'Py_CompileString' in DLL 'python38'``. This will be fixed in Python 3.8.1.
+
.. |Join the chat at https://gitter.im/pythonnet/pythonnet| image:: https://badges.gitter.im/pythonnet/pythonnet.svg
:target: https://gitter.im/pythonnet/pythonnet?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge
.. |appveyor shield| image:: https://img.shields.io/appveyor/ci/pythonnet/pythonnet/master.svg?label=AppVeyor
diff --git a/appveyor.yml b/appveyor.yml
index 445f9bb5a..20d8ed991 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -23,16 +23,22 @@ environment:
BUILD_OPTS: --xplat
- PYTHON_VERSION: 3.7
BUILD_OPTS: --xplat
+ - PYTHON_VERSION: 3.8
+ BUILD_OPTS: --xplat
- PYTHON_VERSION: 2.7
- PYTHON_VERSION: 3.5
- PYTHON_VERSION: 3.6
- PYTHON_VERSION: 3.7
+ - PYTHON_VERSION: 3.8
matrix:
allow_failures:
- PYTHON_VERSION: 3.4
BUILD_OPTS: --xplat
- PYTHON_VERSION: 3.4
+ - PYTHON_VERSION: 3.8
+ BUILD_OPTS: --xplat
+ - PYTHON_VERSION: 3.8
init:
# Update Environment Variables based on matrix/platform
diff --git a/src/runtime/Python.Runtime.15.csproj b/src/runtime/Python.Runtime.15.csproj
index 122132513..c31d4bf91 100644
--- a/src/runtime/Python.Runtime.15.csproj
+++ b/src/runtime/Python.Runtime.15.csproj
@@ -42,7 +42,7 @@
$(PYTHONNET_PY2_VERSION)
PYTHON27
$(PYTHONNET_PY3_VERSION)
- PYTHON37
+ PYTHON38
$(PYTHONNET_WIN_DEFINE_CONSTANTS)
UCS2
$(PYTHONNET_MONO_DEFINE_CONSTANTS)
diff --git a/src/runtime/Python.Runtime.csproj b/src/runtime/Python.Runtime.csproj
index ac6b59150..02656e51e 100644
--- a/src/runtime/Python.Runtime.csproj
+++ b/src/runtime/Python.Runtime.csproj
@@ -22,11 +22,11 @@
-
PYTHON2;PYTHON27;UCS4
@@ -34,7 +34,7 @@
pdbonly
- PYTHON3;PYTHON37;UCS4
+ PYTHON3;PYTHON38;UCS4
true
pdbonly
@@ -46,7 +46,7 @@
true
- PYTHON3;PYTHON37;UCS4;TRACE;DEBUG
+ PYTHON3;PYTHON38;UCS4;TRACE;DEBUG
false
full
@@ -56,7 +56,7 @@
pdbonly
- PYTHON3;PYTHON37;UCS2
+ PYTHON3;PYTHON38;UCS2
true
pdbonly
@@ -68,7 +68,7 @@
true
- PYTHON3;PYTHON37;UCS2;TRACE;DEBUG
+ PYTHON3;PYTHON38;UCS2;TRACE;DEBUG
false
full
@@ -140,8 +140,8 @@
-
-
+
+
@@ -151,7 +151,8 @@
-
+
+
@@ -170,4 +171,4 @@
-
+
diff --git a/src/runtime/interop38.cs b/src/runtime/interop38.cs
new file mode 100644
index 000000000..8f2e32afe
--- /dev/null
+++ b/src/runtime/interop38.cs
@@ -0,0 +1,152 @@
+
+// Auto-generated by geninterop.py.
+// DO NOT MODIFIY BY HAND.
+
+
+#if PYTHON38
+using System;
+using System.Collections;
+using System.Collections.Specialized;
+using System.Runtime.InteropServices;
+using System.Reflection;
+using System.Text;
+
+namespace Python.Runtime
+{
+ [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
+ internal class TypeOffset
+ {
+ static TypeOffset()
+ {
+ Type type = typeof(TypeOffset);
+ FieldInfo[] fi = type.GetFields();
+ int size = IntPtr.Size;
+ for (int i = 0; i < fi.Length; i++)
+ {
+ fi[i].SetValue(null, i * size);
+ }
+ }
+
+ public static int magic()
+ {
+ return ob_size;
+ }
+
+ // Auto-generated from PyHeapTypeObject in Python.h
+ public static int ob_refcnt = 0;
+ public static int ob_type = 0;
+ public static int ob_size = 0;
+ public static int tp_name = 0;
+ public static int tp_basicsize = 0;
+ public static int tp_itemsize = 0;
+ public static int tp_dealloc = 0;
+ public static int tp_vectorcall_offset = 0;
+ public static int tp_getattr = 0;
+ public static int tp_setattr = 0;
+ public static int tp_as_async = 0;
+ public static int tp_repr = 0;
+ public static int tp_as_number = 0;
+ public static int tp_as_sequence = 0;
+ public static int tp_as_mapping = 0;
+ public static int tp_hash = 0;
+ public static int tp_call = 0;
+ public static int tp_str = 0;
+ public static int tp_getattro = 0;
+ public static int tp_setattro = 0;
+ public static int tp_as_buffer = 0;
+ public static int tp_flags = 0;
+ public static int tp_doc = 0;
+ public static int tp_traverse = 0;
+ public static int tp_clear = 0;
+ public static int tp_richcompare = 0;
+ public static int tp_weaklistoffset = 0;
+ public static int tp_iter = 0;
+ public static int tp_iternext = 0;
+ public static int tp_methods = 0;
+ public static int tp_members = 0;
+ public static int tp_getset = 0;
+ public static int tp_base = 0;
+ public static int tp_dict = 0;
+ public static int tp_descr_get = 0;
+ public static int tp_descr_set = 0;
+ public static int tp_dictoffset = 0;
+ public static int tp_init = 0;
+ public static int tp_alloc = 0;
+ public static int tp_new = 0;
+ public static int tp_free = 0;
+ public static int tp_is_gc = 0;
+ public static int tp_bases = 0;
+ public static int tp_mro = 0;
+ public static int tp_cache = 0;
+ public static int tp_subclasses = 0;
+ public static int tp_weaklist = 0;
+ public static int tp_del = 0;
+ public static int tp_version_tag = 0;
+ public static int tp_finalize = 0;
+ public static int tp_vectorcall = 0;
+ public static int am_await = 0;
+ public static int am_aiter = 0;
+ public static int am_anext = 0;
+ public static int nb_add = 0;
+ public static int nb_subtract = 0;
+ public static int nb_multiply = 0;
+ public static int nb_remainder = 0;
+ public static int nb_divmod = 0;
+ public static int nb_power = 0;
+ public static int nb_negative = 0;
+ public static int nb_positive = 0;
+ public static int nb_absolute = 0;
+ public static int nb_bool = 0;
+ public static int nb_invert = 0;
+ public static int nb_lshift = 0;
+ public static int nb_rshift = 0;
+ public static int nb_and = 0;
+ public static int nb_xor = 0;
+ public static int nb_or = 0;
+ public static int nb_int = 0;
+ public static int nb_reserved = 0;
+ public static int nb_float = 0;
+ public static int nb_inplace_add = 0;
+ public static int nb_inplace_subtract = 0;
+ public static int nb_inplace_multiply = 0;
+ public static int nb_inplace_remainder = 0;
+ public static int nb_inplace_power = 0;
+ public static int nb_inplace_lshift = 0;
+ public static int nb_inplace_rshift = 0;
+ public static int nb_inplace_and = 0;
+ public static int nb_inplace_xor = 0;
+ public static int nb_inplace_or = 0;
+ public static int nb_floor_divide = 0;
+ public static int nb_true_divide = 0;
+ public static int nb_inplace_floor_divide = 0;
+ public static int nb_inplace_true_divide = 0;
+ public static int nb_index = 0;
+ public static int nb_matrix_multiply = 0;
+ public static int nb_inplace_matrix_multiply = 0;
+ public static int mp_length = 0;
+ public static int mp_subscript = 0;
+ public static int mp_ass_subscript = 0;
+ public static int sq_length = 0;
+ public static int sq_concat = 0;
+ public static int sq_repeat = 0;
+ public static int sq_item = 0;
+ public static int was_sq_slice = 0;
+ public static int sq_ass_item = 0;
+ public static int was_sq_ass_slice = 0;
+ public static int sq_contains = 0;
+ public static int sq_inplace_concat = 0;
+ public static int sq_inplace_repeat = 0;
+ public static int bf_getbuffer = 0;
+ public static int bf_releasebuffer = 0;
+ public static int name = 0;
+ public static int ht_slots = 0;
+ public static int qualname = 0;
+ public static int ht_cached_keys = 0;
+
+ /* here are optional user slots, followed by the members. */
+ public static int members = 0;
+ }
+}
+
+#endif
+
diff --git a/src/runtime/runtime.cs b/src/runtime/runtime.cs
index f97821d13..7a78cd6e1 100644
--- a/src/runtime/runtime.cs
+++ b/src/runtime/runtime.cs
@@ -64,8 +64,11 @@ public class Runtime
#elif PYTHON37
internal const string _pyversion = "3.7";
internal const string _pyver = "37";
+#elif PYTHON38
+ internal const string _pyversion = "3.8";
+ internal const string _pyver = "38";
#else
-#error You must define one of PYTHON34 to PYTHON37 or PYTHON27
+#error You must define one of PYTHON34 to PYTHON38 or PYTHON27
#endif
#if MONO_LINUX || MONO_OSX // Linux/macOS use dotted version string
From 5f2e2e2f81d0d4439f04d6ee322bae304d382f21 Mon Sep 17 00:00:00 2001
From: Benoit Hudson
Date: Thu, 21 Nov 2019 04:10:58 -0500
Subject: [PATCH 038/998] Split from PR 958: restoring the __import__ after
shutdown. (#993)
When C# shuts down we should restore Python to its original state.
---
src/runtime/importhook.cs | 67 +++++++++++++++++++++++++++++++--------
1 file changed, 54 insertions(+), 13 deletions(-)
diff --git a/src/runtime/importhook.cs b/src/runtime/importhook.cs
index 7e4a208f5..06ba7a56d 100644
--- a/src/runtime/importhook.cs
+++ b/src/runtime/importhook.cs
@@ -26,25 +26,64 @@ internal static void InitializeModuleDef()
#endif
///
- /// Initialization performed on startup of the Python runtime.
+ /// Get a New reference to the builtins module.
///
- internal static void Initialize()
+ static IntPtr GetNewRefToBuiltins()
{
- // Initialize the Python <--> CLR module hook. We replace the
- // built-in Python __import__ with our own. This isn't ideal,
- // but it provides the most "Pythonic" way of dealing with CLR
- // modules (Python doesn't provide a way to emulate packages).
- IntPtr dict = Runtime.PyImport_GetModuleDict();
+ if (Runtime.IsPython3)
+ {
+ return Runtime.PyImport_ImportModule("builtins");
+ }
+ else
+ {
+ // dict is a borrowed ref, no need to decref
+ IntPtr dict = Runtime.PyImport_GetModuleDict();
- IntPtr mod = Runtime.IsPython3
- ? Runtime.PyImport_ImportModule("builtins")
- : Runtime.PyDict_GetItemString(dict, "__builtin__");
+ // GetItemString is a borrowed ref; incref to get a new ref
+ IntPtr builtins = Runtime.PyDict_GetItemString(dict, "__builtin__");
+ Runtime.XIncref(builtins);
+ return builtins;
+ }
+ }
- py_import = Runtime.PyObject_GetAttrString(mod, "__import__");
+ ///
+ /// Initialize just the __import__ hook itself.
+ ///
+ static void InitImport()
+ {
+ // We replace the built-in Python __import__ with our own: first
+ // look in CLR modules, then if we don't find any call the default
+ // Python __import__.
+ IntPtr builtins = GetNewRefToBuiltins();
+ py_import = Runtime.PyObject_GetAttrString(builtins, "__import__");
hook = new MethodWrapper(typeof(ImportHook), "__import__", "TernaryFunc");
- Runtime.PyObject_SetAttrString(mod, "__import__", hook.ptr);
+ Runtime.PyObject_SetAttrString(builtins, "__import__", hook.ptr);
Runtime.XDecref(hook.ptr);
+ Runtime.XDecref(builtins);
+ }
+
+ ///
+ /// Restore the __import__ hook.
+ ///
+ static void RestoreImport()
+ {
+ IntPtr builtins = GetNewRefToBuiltins();
+
+ Runtime.PyObject_SetAttrString(builtins, "__import__", py_import);
+ Runtime.XDecref(py_import);
+ py_import = IntPtr.Zero;
+
+ Runtime.XDecref(builtins);
+ }
+
+ ///
+ /// Initialization performed on startup of the Python runtime.
+ ///
+ internal static void Initialize()
+ {
+ InitImport();
+ // Initialize the clr module and tell Python about it.
root = new CLRModule();
#if PYTHON3
@@ -62,6 +101,7 @@ internal static void Initialize()
Runtime.XIncref(root.pyHandle); // we are using the module two times
py_clr_module = root.pyHandle; // Alias handle for PY2/PY3
#endif
+ IntPtr dict = Runtime.PyImport_GetModuleDict();
Runtime.PyDict_SetItemString(dict, "CLR", py_clr_module);
Runtime.PyDict_SetItemString(dict, "clr", py_clr_module);
}
@@ -74,9 +114,10 @@ internal static void Shutdown()
{
if (Runtime.Py_IsInitialized() != 0)
{
+ RestoreImport();
+
Runtime.XDecref(py_clr_module);
Runtime.XDecref(root.pyHandle);
- Runtime.XDecref(py_import);
}
}
From 627cac0cb91eea4bfa6a36b25e523500a792c88c Mon Sep 17 00:00:00 2001
From: Jeff17Robbins
Date: Wed, 27 Nov 2019 20:57:19 -0500
Subject: [PATCH 039/998] Capture function pointer declarations in structs
- Update geninterop.py to visit function pointers
- Support running on Windows 10
- Update interop38.cs
---
AUTHORS.md | 1 +
src/runtime/interop38.cs | 4 ++--
tools/geninterop/geninterop.py | 14 +++++++++++++-
3 files changed, 16 insertions(+), 3 deletions(-)
diff --git a/AUTHORS.md b/AUTHORS.md
index efa04c8f0..e42a456ae 100644
--- a/AUTHORS.md
+++ b/AUTHORS.md
@@ -34,6 +34,7 @@
- Ivan Cronyn ([@cronan](https://github.com/cronan))
- Jan Krivanek ([@jakrivan](https://github.com/jakrivan))
- Jeff Reback ([@jreback](https://github.com/jreback))
+- Jeff Robbins ([@jeff17robbins](https://github.com/jeff17robbins))
- Joe Frayne ([@jfrayne](https://github.com/jfrayne))
- Joe Lidbetter ([@jmlidbetter](https://github.com/jmlidbetter))
- Joe Savage ([@s4v4g3](https://github.com/s4v4g3))
diff --git a/src/runtime/interop38.cs b/src/runtime/interop38.cs
index 8f2e32afe..9126bca6a 100644
--- a/src/runtime/interop38.cs
+++ b/src/runtime/interop38.cs
@@ -1,6 +1,6 @@
// Auto-generated by geninterop.py.
-// DO NOT MODIFIY BY HAND.
+// DO NOT MODIFY BY HAND.
#if PYTHON38
@@ -84,6 +84,7 @@ public static int magic()
public static int tp_version_tag = 0;
public static int tp_finalize = 0;
public static int tp_vectorcall = 0;
+ public static int tp_print = 0;
public static int am_await = 0;
public static int am_aiter = 0;
public static int am_anext = 0;
@@ -149,4 +150,3 @@ public static int magic()
}
#endif
-
diff --git a/tools/geninterop/geninterop.py b/tools/geninterop/geninterop.py
index f8ef8e561..1f4751939 100644
--- a/tools/geninterop/geninterop.py
+++ b/tools/geninterop/geninterop.py
@@ -76,6 +76,8 @@ def visit(self, node):
self.visit_struct(node)
elif isinstance(node, c_ast.Decl):
self.visit_decl(node)
+ elif isinstance(node, c_ast.FuncDecl):
+ self.visit_funcdecl(node)
elif isinstance(node, c_ast.PtrDecl):
self.visit_ptrdecl(node)
elif isinstance(node, c_ast.IdentifierType):
@@ -110,6 +112,9 @@ def visit_struct(self, struct):
def visit_decl(self, decl):
self.visit(decl.type)
+ def visit_funcdecl(self, funcdecl):
+ self.visit(funcdecl.type)
+
def visit_ptrdecl(self, ptrdecl):
self.__ptr_decl_depth += 1
self.visit(ptrdecl.type)
@@ -177,6 +182,13 @@ def preprocess_python_headers():
"-D", "_POSIX_THREADS"
]
+ if os.name == 'nt':
+ defines.extend([
+ "-D", "__inline=inline",
+ "-D", "__ptr32=",
+ "-D", "__declspec(x)=",
+ ])
+
if hasattr(sys, "abiflags"):
if "d" in sys.abiflags:
defines.extend(("-D", "PYTHON_WITH_PYDEBUG"))
@@ -216,7 +228,7 @@ def gen_interop_code(members):
defines_str = " && ".join(defines)
class_definition = """
// Auto-generated by %s.
-// DO NOT MODIFIY BY HAND.
+// DO NOT MODIFY BY HAND.
#if %s
From b3e889b9f6f320f7a40f67dfe6cbe315e03215bb Mon Sep 17 00:00:00 2001
From: amos402
Date: Sun, 1 Dec 2019 21:35:05 +0800
Subject: [PATCH 040/998] * Load PyModuleType without LibraryLoader * Drop C
module dependency when getting _PyObject_NextNotImplemented * Exception
details for SetNoSiteFlag
---
src/runtime/runtime.cs | 62 ++++++++++++++++++++++++++----------------
1 file changed, 38 insertions(+), 24 deletions(-)
diff --git a/src/runtime/runtime.cs b/src/runtime/runtime.cs
index 945c8f809..8b4ef6cbe 100644
--- a/src/runtime/runtime.cs
+++ b/src/runtime/runtime.cs
@@ -292,26 +292,18 @@ internal static void Initialize(bool initSigs = false)
Error = new IntPtr(-1);
+ _PyObject_NextNotImplemented = Get_PyObject_NextNotImplemented();
+ {
+ IntPtr sys = PyImport_ImportModule("sys");
+ PyModuleType = PyObject_Type(sys);
+ XDecref(sys);
+ }
+
// Initialize data about the platform we're running on. We need
// this for the type manager and potentially other details. Must
// happen after caching the python types, above.
InitializePlatformData();
- IntPtr dllLocal = IntPtr.Zero;
- var loader = LibraryLoader.Get(OperatingSystem);
-
- // Since `_PyObject_NextNotImplemented` would set to a heap class
- // for tp_iternext which doesn't implement __next__.
- // Thus we need a heap class to get it, the ZipImportError is a
- // heap class and it's in builtins, so we can use it as a trick.
- var zipimport = PyImport_ImportModule("zipimport");
- var ZipImportError = PyObject_GetAttrString(zipimport, "ZipImportError");
- _PyObject_NextNotImplemented = Marshal.ReadIntPtr(ZipImportError, TypeOffset.tp_iternext);
- XDecref(ZipImportError);
- XDecref(zipimport);
- PyModuleType = loader.GetFunction(dllLocal, "PyModule_Type");
-
-
// Initialize modules that depend on the runtime class.
AssemblyManager.Initialize();
PyCLRMetaType = MetaType.Initialize();
@@ -328,6 +320,29 @@ internal static void Initialize(bool initSigs = false)
AssemblyManager.UpdatePath();
}
+ private static IntPtr Get_PyObject_NextNotImplemented()
+ {
+ IntPtr globals = PyDict_New();
+ if (PyDict_SetItemString(globals, "__builtins__", PyEval_GetBuiltins()) != 0)
+ {
+ XDecref(globals);
+ throw new PythonException();
+ }
+ const string code = "class A(object): pass";
+ IntPtr res = PyRun_String(code, (IntPtr)RunFlagType.File, globals, globals);
+ if (res == IntPtr.Zero)
+ {
+ XDecref(globals);
+ throw new PythonException();
+ }
+ XDecref(res);
+ IntPtr A = PyDict_GetItemString(globals, "A");
+ IntPtr iternext = Marshal.ReadIntPtr(A, TypeOffset.tp_iternext);
+ XDecref(globals);
+ XDecref(A);
+ return iternext;
+ }
+
///
/// Initializes the data about platforms.
///
@@ -1893,14 +1908,16 @@ internal static IntPtr PyMem_Realloc(IntPtr ptr, long size)
internal static void SetNoSiteFlag()
{
+ if (_PythonDll == "__Internal")
+ {
+ throw new NotSupportedException("SetNoSiteFlag didn't support on static compile");
+ }
var loader = LibraryLoader.Get(OperatingSystem);
-
- IntPtr dllLocal;
- if (_PythonDll != "__Internal")
+ IntPtr dllLocal = loader.Load(_PythonDll);
+ if (dllLocal == IntPtr.Zero)
{
- dllLocal = loader.Load(_PythonDll);
+ throw new Exception($"Cannot load {_PythonDll}");
}
-
try
{
Py_NoSiteFlag = loader.GetFunction(dllLocal, "Py_NoSiteFlag");
@@ -1908,10 +1925,7 @@ internal static void SetNoSiteFlag()
}
finally
{
- if (dllLocal != IntPtr.Zero)
- {
- loader.Free(dllLocal);
- }
+ loader.Free(dllLocal);
}
}
}
From abbe870db593ba63efc5e9999b2474169d87e141 Mon Sep 17 00:00:00 2001
From: amos402
Date: Mon, 2 Dec 2019 19:53:15 +0800
Subject: [PATCH 041/998] Internal implement Py_CompileString for compat with
CPython 3.8.0 (#1000)
---
src/runtime/pythonengine.cs | 6 +++---
src/runtime/runtime.cs | 30 +++++++++++++++++++++++++++++-
2 files changed, 32 insertions(+), 4 deletions(-)
diff --git a/src/runtime/pythonengine.cs b/src/runtime/pythonengine.cs
index 700543839..5073067d3 100644
--- a/src/runtime/pythonengine.cs
+++ b/src/runtime/pythonengine.cs
@@ -503,7 +503,7 @@ public static PyObject ReloadModule(PyObject module)
///
public static PyObject ModuleFromString(string name, string code)
{
- IntPtr c = Runtime.Py_CompileString(code, "none", (IntPtr)257);
+ IntPtr c = Runtime.Py_CompileString(code, "none", (int)RunFlagType.File);
Runtime.CheckExceptionOccurred();
IntPtr m = Runtime.PyImport_ExecCodeModule(name, c);
Runtime.CheckExceptionOccurred();
@@ -512,7 +512,7 @@ public static PyObject ModuleFromString(string name, string code)
public static PyObject Compile(string code, string filename = "", RunFlagType mode = RunFlagType.File)
{
- var flag = (IntPtr)mode;
+ var flag = (int)mode;
IntPtr ptr = Runtime.Py_CompileString(code, filename, flag);
Runtime.CheckExceptionOccurred();
return new PyObject(ptr);
@@ -610,7 +610,7 @@ internal static PyObject RunString(string code, IntPtr? globals, IntPtr? locals,
}
}
- public enum RunFlagType
+ public enum RunFlagType : int
{
Single = 256,
File = 257, /* Py_file_input */
diff --git a/src/runtime/runtime.cs b/src/runtime/runtime.cs
index 7a78cd6e1..66f5ed123 100644
--- a/src/runtime/runtime.cs
+++ b/src/runtime/runtime.cs
@@ -763,8 +763,36 @@ public static extern int Py_Main(
[DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)]
internal static extern IntPtr PyEval_EvalCode(IntPtr co, IntPtr globals, IntPtr locals);
+#if PYTHON2
+ [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern IntPtr Py_CompileString(string code, string file, int start);
+#else
+ ///
+ /// Return value: New reference.
+ /// This is a simplified interface to Py_CompileStringFlags() below, leaving flags set to NULL.
+ ///
+ internal static IntPtr Py_CompileString(string str, string file, int start)
+ {
+ return Py_CompileStringFlags(str, file, start, IntPtr.Zero);
+ }
+
+ ///
+ /// Return value: New reference.
+ /// This is a simplified interface to Py_CompileStringExFlags() below, with optimize set to -1.
+ ///
+ internal static IntPtr Py_CompileStringFlags(string str, string file, int start, IntPtr flags)
+ {
+ return Py_CompileStringExFlags(str, file, start, flags, -1);
+ }
+
+ ///
+ /// Return value: New reference.
+ /// Like Py_CompileStringObject(), but filename is a byte string decoded from the filesystem encoding(os.fsdecode()).
+ ///
+ ///
[DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)]
- internal static extern IntPtr Py_CompileString(string code, string file, IntPtr tok);
+ internal static extern IntPtr Py_CompileStringExFlags(string str, string file, int start, IntPtr flags, int optimize);
+#endif
[DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)]
internal static extern IntPtr PyImport_ExecCodeModule(string name, IntPtr code);
From d1044c3a54f1a359785251836868d835dbf97c6b Mon Sep 17 00:00:00 2001
From: amos402
Date: Mon, 2 Dec 2019 20:23:47 +0800
Subject: [PATCH 042/998] Remove unnecessary `CopySlot` calls (#1004)
---
src/runtime/typemanager.cs | 16 +++++-----------
1 file changed, 5 insertions(+), 11 deletions(-)
diff --git a/src/runtime/typemanager.cs b/src/runtime/typemanager.cs
index 9a98e9ebb..e9a8818f0 100644
--- a/src/runtime/typemanager.cs
+++ b/src/runtime/typemanager.cs
@@ -309,17 +309,11 @@ internal static IntPtr CreateMetaType(Type impl)
Marshal.WriteIntPtr(type, TypeOffset.tp_base, py_type);
Runtime.XIncref(py_type);
- // Copy gc and other type slots from the base Python metatype.
-
- CopySlot(py_type, type, TypeOffset.tp_basicsize);
- CopySlot(py_type, type, TypeOffset.tp_itemsize);
-
- CopySlot(py_type, type, TypeOffset.tp_dictoffset);
- CopySlot(py_type, type, TypeOffset.tp_weaklistoffset);
-
- CopySlot(py_type, type, TypeOffset.tp_traverse);
- CopySlot(py_type, type, TypeOffset.tp_clear);
- CopySlot(py_type, type, TypeOffset.tp_is_gc);
+ // Slots will inherit from TypeType, it's not neccesary for setting them.
+ // Inheried slots:
+ // tp_basicsize, tp_itemsize,
+ // tp_dictoffset, tp_weaklistoffset,
+ // tp_traverse, tp_clear, tp_is_gc, etc.
// Override type slots with those of the managed implementation.
From 5f56ebc8f0785f6581c10d5beb9c1a8fffe6c650 Mon Sep 17 00:00:00 2001
From: amos402
Date: Mon, 2 Dec 2019 20:31:05 +0800
Subject: [PATCH 043/998] Fix refcnt errors (split from #958) (#1001)
* Add exception helper
* Fixed refcnt error in ExtensionType.FinalizeObject
* Fixed typename leaking
* Fix refcnt error by using `using`
---
.editorconfig | 2 +-
src/runtime/extensiontype.cs | 3 ++-
src/runtime/metatype.cs | 1 +
src/runtime/pythonexception.cs | 17 +++++++++++++++++
src/runtime/runtime.cs | 4 ++++
src/runtime/typemanager.cs | 8 ++------
6 files changed, 27 insertions(+), 8 deletions(-)
diff --git a/.editorconfig b/.editorconfig
index 9e10931d0..d64f74bc1 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -25,7 +25,7 @@ dotnet_sort_system_directives_first = true
dotnet_separate_import_directive_groups = true
[*.cs]
-csharp_new_line_before_open_brace = true
+csharp_new_line_before_open_brace = all
csharp_new_line_before_else = true
csharp_new_line_before_catch = true
csharp_new_line_before_finally = true
diff --git a/src/runtime/extensiontype.cs b/src/runtime/extensiontype.cs
index 693a46f42..6585180c1 100644
--- a/src/runtime/extensiontype.cs
+++ b/src/runtime/extensiontype.cs
@@ -38,6 +38,7 @@ public ExtensionType()
Runtime.PyObject_GC_UnTrack(py);
+ // Steals a ref to tpHandle.
tpHandle = tp;
pyHandle = py;
gcHandle = gc;
@@ -50,7 +51,7 @@ public ExtensionType()
public static void FinalizeObject(ManagedType self)
{
Runtime.PyObject_GC_Del(self.pyHandle);
- Runtime.XDecref(self.tpHandle);
+ // Not necessary for decref of `tpHandle`.
self.gcHandle.Free();
}
diff --git a/src/runtime/metatype.cs b/src/runtime/metatype.cs
index 8853c2d5e..5af2e1a7e 100644
--- a/src/runtime/metatype.cs
+++ b/src/runtime/metatype.cs
@@ -266,6 +266,7 @@ private static IntPtr DoInstanceCheck(IntPtr tp, IntPtr args, bool checkType)
return Runtime.PyFalse;
}
+ Runtime.XIncref(args);
using (var argsObj = new PyList(args))
{
if (argsObj.Length() != 1)
diff --git a/src/runtime/pythonexception.cs b/src/runtime/pythonexception.cs
index 295a63b3d..8a6a24799 100644
--- a/src/runtime/pythonexception.cs
+++ b/src/runtime/pythonexception.cs
@@ -1,4 +1,5 @@
using System;
+using System.Runtime.CompilerServices;
namespace Python.Runtime
{
@@ -190,5 +191,21 @@ public static bool Matches(IntPtr ob)
{
return Runtime.PyErr_ExceptionMatches(ob) != 0;
}
+
+ public static void ThrowIfIsNull(IntPtr ob)
+ {
+ if (ob == IntPtr.Zero)
+ {
+ throw new PythonException();
+ }
+ }
+
+ public static void ThrowIfIsNotZero(int value)
+ {
+ if (value != 0)
+ {
+ throw new PythonException();
+ }
+ }
}
}
diff --git a/src/runtime/runtime.cs b/src/runtime/runtime.cs
index 66f5ed123..449d22435 100644
--- a/src/runtime/runtime.cs
+++ b/src/runtime/runtime.cs
@@ -1368,6 +1368,10 @@ internal static IntPtr PyUnicode_FromStringAndSize(IntPtr value, long size)
[DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr PyUnicode_FromStringAndSize(IntPtr value, IntPtr size);
+
+ [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern IntPtr PyUnicode_AsUTF8(IntPtr unicode);
+
#elif PYTHON2
internal static IntPtr PyString_FromStringAndSize(string value, long size)
{
diff --git a/src/runtime/typemanager.cs b/src/runtime/typemanager.cs
index e9a8818f0..4427305e6 100644
--- a/src/runtime/typemanager.cs
+++ b/src/runtime/typemanager.cs
@@ -409,12 +409,8 @@ internal static IntPtr AllocateTypeObject(string name)
// the Python version of the type name - otherwise we'd have to
// allocate the tp_name and would have no way to free it.
#if PYTHON3
- // For python3 we leak two objects. One for the ASCII representation
- // required for tp_name, and another for the Unicode representation
- // for ht_name.
- IntPtr temp = Runtime.PyBytes_FromString(name);
- IntPtr raw = Runtime.PyBytes_AS_STRING(temp);
- temp = Runtime.PyUnicode_FromString(name);
+ IntPtr temp = Runtime.PyUnicode_FromString(name);
+ IntPtr raw = Runtime.PyUnicode_AsUTF8(temp);
#elif PYTHON2
IntPtr temp = Runtime.PyString_FromString(name);
IntPtr raw = Runtime.PyString_AsString(temp);
From 5150e618612a299af3a5c970f5ec299b3ca974f0 Mon Sep 17 00:00:00 2001
From: amos402
Date: Sun, 15 Dec 2019 02:34:28 +0800
Subject: [PATCH 044/998] Prevent exception override
---
src/runtime/runtime.cs | 10 ++++++++--
1 file changed, 8 insertions(+), 2 deletions(-)
diff --git a/src/runtime/runtime.cs b/src/runtime/runtime.cs
index 153738930..358a3946b 100644
--- a/src/runtime/runtime.cs
+++ b/src/runtime/runtime.cs
@@ -332,8 +332,14 @@ private static IntPtr Get_PyObject_NextNotImplemented()
IntPtr res = PyRun_String(code, (IntPtr)RunFlagType.File, globals, globals);
if (res == IntPtr.Zero)
{
- XDecref(globals);
- throw new PythonException();
+ try
+ {
+ throw new PythonException();
+ }
+ finally
+ {
+ XDecref(globals);
+ }
}
XDecref(res);
IntPtr A = PyDict_GetItemString(globals, "A");
From 65e209e00d76a39007b86b4a3c825ab1e38a1d2c Mon Sep 17 00:00:00 2001
From: amos402
Date: Mon, 16 Dec 2019 01:33:42 +0800
Subject: [PATCH 045/998] Rollback symbol loading for __Internal
---
src/runtime/runtime.cs | 19 +++++++++++--------
1 file changed, 11 insertions(+), 8 deletions(-)
diff --git a/src/runtime/runtime.cs b/src/runtime/runtime.cs
index 358a3946b..495dd7bb7 100644
--- a/src/runtime/runtime.cs
+++ b/src/runtime/runtime.cs
@@ -1946,15 +1946,15 @@ internal static IntPtr PyMem_Realloc(IntPtr ptr, long size)
internal static void SetNoSiteFlag()
{
- if (_PythonDll == "__Internal")
- {
- throw new NotSupportedException("SetNoSiteFlag didn't support on static compile");
- }
var loader = LibraryLoader.Get(OperatingSystem);
- IntPtr dllLocal = loader.Load(_PythonDll);
- if (dllLocal == IntPtr.Zero)
+ IntPtr dllLocal;
+ if (_PythonDll != "__Internal")
{
- throw new Exception($"Cannot load {_PythonDll}");
+ dllLocal = loader.Load(_PythonDll);
+ if (dllLocal == IntPtr.Zero)
+ {
+ throw new Exception($"Cannot load {_PythonDll}");
+ }
}
try
{
@@ -1963,7 +1963,10 @@ internal static void SetNoSiteFlag()
}
finally
{
- loader.Free(dllLocal);
+ if (dllLocal != IntPtr.Zero)
+ {
+ loader.Free(dllLocal);
+ }
}
}
}
From ba5127aaf687d8bb816423cd31a4bc7698b1ebc7 Mon Sep 17 00:00:00 2001
From: Alex Earl
Date: Mon, 16 Dec 2019 14:33:00 -0700
Subject: [PATCH 046/998] Add mp_length slot for .NET classes implementing
ICollection/ICollection (#994)
- Add mp_length slot implementation for .NET types
- Check if the object implement ICollection or ICollection
- Add tests for explicit and non-explicit interface implementation
---
AUTHORS.md | 1 +
CHANGELOG.md | 1 +
src/runtime/Python.Runtime.csproj | 1 +
src/runtime/arrayobject.cs | 11 --
src/runtime/slots/mp_length.cs | 50 +++++++++
src/runtime/typemanager.cs | 15 +++
src/testing/Python.Test.csproj | 1 +
src/testing/mp_lengthtest.cs | 171 ++++++++++++++++++++++++++++++
src/tests/test_mp_length.py | 49 +++++++++
9 files changed, 289 insertions(+), 11 deletions(-)
create mode 100644 src/runtime/slots/mp_length.cs
create mode 100644 src/testing/mp_lengthtest.cs
create mode 100644 src/tests/test_mp_length.py
diff --git a/AUTHORS.md b/AUTHORS.md
index e42a456ae..26285bf6a 100644
--- a/AUTHORS.md
+++ b/AUTHORS.md
@@ -12,6 +12,7 @@
## Contributors
+- Alex Earl ([@slide](https://github.com/slide))
- Alex Helms ([@alexhelms](https://github.com/alexhelms))
- Alexandre Catarino([@AlexCatarino](https://github.com/AlexCatarino))
- Arvid JB ([@ArvidJB](https://github.com/ArvidJB))
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5c999d668..1fd2b1dcf 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -12,6 +12,7 @@ This document follows the conventions laid out in [Keep a CHANGELOG][].
- Added automatic NuGet package generation in appveyor and local builds
- Added function that sets Py_NoSiteFlag to 1.
- Added support for Jetson Nano.
+- Added support for __len__ for .NET classes that implement ICollection
### Changed
diff --git a/src/runtime/Python.Runtime.csproj b/src/runtime/Python.Runtime.csproj
index 02656e51e..0c2f912de 100644
--- a/src/runtime/Python.Runtime.csproj
+++ b/src/runtime/Python.Runtime.csproj
@@ -142,6 +142,7 @@
+
diff --git a/src/runtime/arrayobject.cs b/src/runtime/arrayobject.cs
index c37295704..1ef318473 100644
--- a/src/runtime/arrayobject.cs
+++ b/src/runtime/arrayobject.cs
@@ -244,16 +244,5 @@ public static int sq_contains(IntPtr ob, IntPtr v)
return 0;
}
-
-
- ///
- /// Implements __len__ for array types.
- ///
- public static int mp_length(IntPtr ob)
- {
- var self = (CLRObject)GetManagedObject(ob);
- var items = self.inst as Array;
- return items.Length;
- }
}
}
diff --git a/src/runtime/slots/mp_length.cs b/src/runtime/slots/mp_length.cs
new file mode 100644
index 000000000..b0a2e8d79
--- /dev/null
+++ b/src/runtime/slots/mp_length.cs
@@ -0,0 +1,50 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+
+namespace Python.Runtime.Slots
+{
+ internal static class mp_length_slot
+ {
+ ///
+ /// Implements __len__ for classes that implement ICollection
+ /// (this includes any IList implementer or Array subclass)
+ ///
+ public static int mp_length(IntPtr ob)
+ {
+ var co = ManagedType.GetManagedObject(ob) as CLRObject;
+ if (co == null)
+ {
+ Exceptions.RaiseTypeError("invalid object");
+ }
+
+ // first look for ICollection implementation directly
+ if (co.inst is ICollection c)
+ {
+ return c.Count;
+ }
+
+ Type clrType = co.inst.GetType();
+
+ // now look for things that implement ICollection directly (non-explicitly)
+ PropertyInfo p = clrType.GetProperty("Count");
+ if (p != null && clrType.GetInterfaces().Any(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof(ICollection<>)))
+ {
+ return (int)p.GetValue(co.inst, null);
+ }
+
+ // finally look for things that implement the interface explicitly
+ var iface = clrType.GetInterfaces().FirstOrDefault(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof(ICollection<>));
+ if (iface != null)
+ {
+ p = iface.GetProperty(nameof(ICollection.Count));
+ return (int)p.GetValue(co.inst, null);
+ }
+
+ Exceptions.SetError(Exceptions.TypeError, $"object of type '{clrType.Name}' has no len()");
+ return -1;
+ }
+ }
+}
diff --git a/src/runtime/typemanager.cs b/src/runtime/typemanager.cs
index 4427305e6..97e6032cd 100644
--- a/src/runtime/typemanager.cs
+++ b/src/runtime/typemanager.cs
@@ -1,9 +1,11 @@
using System;
using System.Collections;
using System.Collections.Generic;
+using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using Python.Runtime.Platform;
+using Python.Runtime.Slots;
namespace Python.Runtime
{
@@ -153,6 +155,13 @@ internal static IntPtr CreateType(ManagedType impl, Type clrType)
Marshal.WriteIntPtr(type, TypeOffset.tp_itemsize, IntPtr.Zero);
Marshal.WriteIntPtr(type, TypeOffset.tp_dictoffset, (IntPtr)tp_dictoffset);
+ // add a __len__ slot for inheritors of ICollection and ICollection<>
+ if (typeof(ICollection).IsAssignableFrom(clrType) || clrType.GetInterfaces().Any(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof(ICollection<>)))
+ {
+ InitializeSlot(type, TypeOffset.mp_length, typeof(mp_length_slot).GetMethod(nameof(mp_length_slot.mp_length)));
+ }
+
+ // we want to do this after the slot stuff above in case the class itself implements a slot method
InitializeSlots(type, impl.GetType());
if (base_ != IntPtr.Zero)
@@ -193,6 +202,12 @@ internal static IntPtr CreateType(ManagedType impl, Type clrType)
return type;
}
+ static void InitializeSlot(IntPtr type, int slotOffset, MethodInfo method)
+ {
+ IntPtr thunk = Interop.GetThunk(method);
+ Marshal.WriteIntPtr(type, slotOffset, thunk);
+ }
+
internal static IntPtr CreateSubType(IntPtr py_name, IntPtr py_base_type, IntPtr py_dict)
{
// Utility to create a subtype of a managed type with the ability for the
diff --git a/src/testing/Python.Test.csproj b/src/testing/Python.Test.csproj
index 6bf5c2d22..515fd928c 100644
--- a/src/testing/Python.Test.csproj
+++ b/src/testing/Python.Test.csproj
@@ -92,6 +92,7 @@
+
diff --git a/src/testing/mp_lengthtest.cs b/src/testing/mp_lengthtest.cs
new file mode 100644
index 000000000..a4f3e8c25
--- /dev/null
+++ b/src/testing/mp_lengthtest.cs
@@ -0,0 +1,171 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+
+namespace Python.Test
+{
+ public class MpLengthCollectionTest : ICollection
+ {
+ private readonly List items;
+
+ public MpLengthCollectionTest()
+ {
+ SyncRoot = new object();
+ items = new List
+ {
+ 1,
+ 2,
+ 3
+ };
+ }
+
+ public int Count => items.Count;
+
+ public object SyncRoot { get; private set; }
+
+ public bool IsSynchronized => false;
+
+ public void CopyTo(Array array, int index)
+ {
+ throw new NotImplementedException();
+ }
+
+ public IEnumerator GetEnumerator()
+ {
+ throw new NotImplementedException();
+ }
+ }
+
+ public class MpLengthExplicitCollectionTest : ICollection
+ {
+ private readonly List items;
+ private readonly object syncRoot;
+
+ public MpLengthExplicitCollectionTest()
+ {
+ syncRoot = new object();
+ items = new List
+ {
+ 9,
+ 10
+ };
+ }
+ int ICollection.Count => items.Count;
+
+ object ICollection.SyncRoot => syncRoot;
+
+ bool ICollection.IsSynchronized => false;
+
+ void ICollection.CopyTo(Array array, int index)
+ {
+ throw new NotImplementedException();
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ throw new NotImplementedException();
+ }
+ }
+
+ public class MpLengthGenericCollectionTest : ICollection
+ {
+ private readonly List items;
+
+ public MpLengthGenericCollectionTest() {
+ SyncRoot = new object();
+ items = new List();
+ }
+
+ public int Count => items.Count;
+
+ public object SyncRoot { get; private set; }
+
+ public bool IsSynchronized => false;
+
+ public bool IsReadOnly => false;
+
+ public void Add(T item)
+ {
+ items.Add(item);
+ }
+
+ public void Clear()
+ {
+ items.Clear();
+ }
+
+ public bool Contains(T item)
+ {
+ return items.Contains(item);
+ }
+
+ public void CopyTo(T[] array, int arrayIndex)
+ {
+ items.CopyTo(array, arrayIndex);
+ }
+
+ public IEnumerator GetEnumerator()
+ {
+ return ((IEnumerable)items).GetEnumerator();
+ }
+
+ public bool Remove(T item)
+ {
+ return items.Remove(item);
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return items.GetEnumerator();
+ }
+ }
+
+ public class MpLengthExplicitGenericCollectionTest : ICollection
+ {
+ private readonly List items;
+
+ public MpLengthExplicitGenericCollectionTest()
+ {
+ items = new List();
+ }
+
+ int ICollection.Count => items.Count;
+
+ bool ICollection.IsReadOnly => false;
+
+ public void Add(T item)
+ {
+ items.Add(item);
+ }
+
+ void ICollection.Clear()
+ {
+ items.Clear();
+ }
+
+ bool ICollection.Contains(T item)
+ {
+ return items.Contains(item);
+ }
+
+ void ICollection.CopyTo(T[] array, int arrayIndex)
+ {
+ items.CopyTo(array, arrayIndex);
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return items.GetEnumerator();
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return ((IEnumerable)items).GetEnumerator();
+ }
+
+ bool ICollection.Remove(T item)
+ {
+ return items.Remove(item);
+ }
+ }
+}
diff --git a/src/tests/test_mp_length.py b/src/tests/test_mp_length.py
new file mode 100644
index 000000000..c96ac77d1
--- /dev/null
+++ b/src/tests/test_mp_length.py
@@ -0,0 +1,49 @@
+# -*- coding: utf-8 -*-
+
+"""Test __len__ for .NET classes implementing ICollection/ICollection."""
+
+import System
+import pytest
+from Python.Test import MpLengthCollectionTest, MpLengthExplicitCollectionTest, MpLengthGenericCollectionTest, MpLengthExplicitGenericCollectionTest
+
+def test_simple___len__():
+ """Test __len__ for simple ICollection implementers"""
+ import System
+ import System.Collections.Generic
+ l = System.Collections.Generic.List[int]()
+ assert len(l) == 0
+ l.Add(5)
+ l.Add(6)
+ assert len(l) == 2
+
+ d = System.Collections.Generic.Dictionary[int, int]()
+ assert len(d) == 0
+ d.Add(4, 5)
+ assert len(d) == 1
+
+ a = System.Array[int]([0,1,2,3])
+ assert len(a) == 4
+
+def test_custom_collection___len__():
+ """Test __len__ for custom collection class"""
+ s = MpLengthCollectionTest()
+ assert len(s) == 3
+
+def test_custom_collection_explicit___len__():
+ """Test __len__ for custom collection class that explicitly implements ICollection"""
+ s = MpLengthExplicitCollectionTest()
+ assert len(s) == 2
+
+def test_custom_generic_collection___len__():
+ """Test __len__ for custom generic collection class"""
+ s = MpLengthGenericCollectionTest[int]()
+ s.Add(1)
+ s.Add(2)
+ assert len(s) == 2
+
+def test_custom_generic_collection_explicit___len__():
+ """Test __len__ for custom generic collection that explicity implements ICollection"""
+ s = MpLengthExplicitGenericCollectionTest[int]()
+ s.Add(1)
+ s.Add(10)
+ assert len(s) == 2
From c1190f4397f365667d8246503ac936d3945bdc2b Mon Sep 17 00:00:00 2001
From: amos402
Date: Wed, 18 Dec 2019 16:03:28 +0800
Subject: [PATCH 047/998] Release method wrapper(split from #958) (#1002)
* Add exception helper
* Release memory on ImportHook.Shutdown
* Release ModuleDef when py_clr_module released
* Completely ModuleDef initialization
---
src/runtime/importhook.cs | 44 ++++++++++++++++++++++++++++++------
src/runtime/interop.cs | 2 +-
src/runtime/methodwrapper.cs | 17 ++++++++++++++
src/runtime/moduleobject.cs | 5 ++++
src/runtime/runtime.cs | 1 -
5 files changed, 60 insertions(+), 9 deletions(-)
diff --git a/src/runtime/importhook.cs b/src/runtime/importhook.cs
index 06ba7a56d..94f0f7c94 100644
--- a/src/runtime/importhook.cs
+++ b/src/runtime/importhook.cs
@@ -23,6 +23,16 @@ internal static void InitializeModuleDef()
module_def = ModuleDefOffset.AllocModuleDef("clr");
}
}
+
+ internal static void ReleaseModuleDef()
+ {
+ if (module_def == IntPtr.Zero)
+ {
+ return;
+ }
+ ModuleDefOffset.FreeModuleDef(module_def);
+ module_def = IntPtr.Zero;
+ }
#endif
///
@@ -56,9 +66,12 @@ static void InitImport()
// Python __import__.
IntPtr builtins = GetNewRefToBuiltins();
py_import = Runtime.PyObject_GetAttrString(builtins, "__import__");
+ PythonException.ThrowIfIsNull(py_import);
+
hook = new MethodWrapper(typeof(ImportHook), "__import__", "TernaryFunc");
- Runtime.PyObject_SetAttrString(builtins, "__import__", hook.ptr);
- Runtime.XDecref(hook.ptr);
+ int res = Runtime.PyObject_SetAttrString(builtins, "__import__", hook.ptr);
+ PythonException.ThrowIfIsNotZero(res);
+
Runtime.XDecref(builtins);
}
@@ -69,10 +82,14 @@ static void RestoreImport()
{
IntPtr builtins = GetNewRefToBuiltins();
- Runtime.PyObject_SetAttrString(builtins, "__import__", py_import);
+ int res = Runtime.PyObject_SetAttrString(builtins, "__import__", py_import);
+ PythonException.ThrowIfIsNotZero(res);
Runtime.XDecref(py_import);
py_import = IntPtr.Zero;
+ hook.Release();
+ hook = null;
+
Runtime.XDecref(builtins);
}
@@ -112,13 +129,26 @@ internal static void Initialize()
///
internal static void Shutdown()
{
- if (Runtime.Py_IsInitialized() != 0)
+ if (Runtime.Py_IsInitialized() == 0)
{
- RestoreImport();
+ return;
+ }
- Runtime.XDecref(py_clr_module);
- Runtime.XDecref(root.pyHandle);
+ RestoreImport();
+
+ bool shouldFreeDef = Runtime.Refcount(py_clr_module) == 1;
+ Runtime.XDecref(py_clr_module);
+ py_clr_module = IntPtr.Zero;
+#if PYTHON3
+ if (shouldFreeDef)
+ {
+ ReleaseModuleDef();
}
+#endif
+
+ Runtime.XDecref(root.pyHandle);
+ root = null;
+ CLRModule.Reset();
}
///
diff --git a/src/runtime/interop.cs b/src/runtime/interop.cs
index 4ae4b61e0..2e29601fd 100644
--- a/src/runtime/interop.cs
+++ b/src/runtime/interop.cs
@@ -227,7 +227,7 @@ public static IntPtr AllocModuleDef(string modulename)
byte[] ascii = Encoding.ASCII.GetBytes(modulename);
int size = name + ascii.Length + 1;
IntPtr ptr = Marshal.AllocHGlobal(size);
- for (int i = 0; i < m_free; i += IntPtr.Size)
+ for (int i = 0; i <= m_free; i += IntPtr.Size)
Marshal.WriteIntPtr(ptr, i, IntPtr.Zero);
Marshal.Copy(ascii, 0, (IntPtr)(ptr + name), ascii.Length);
Marshal.WriteIntPtr(ptr, m_name, (IntPtr)(ptr + name));
diff --git a/src/runtime/methodwrapper.cs b/src/runtime/methodwrapper.cs
index 2f3ce3ef2..ba92e99d4 100644
--- a/src/runtime/methodwrapper.cs
+++ b/src/runtime/methodwrapper.cs
@@ -12,6 +12,7 @@ internal class MethodWrapper
{
public IntPtr mdef;
public IntPtr ptr;
+ private bool _disposed = false;
public MethodWrapper(Type type, string name, string funcType = null)
{
@@ -31,5 +32,21 @@ public IntPtr Call(IntPtr args, IntPtr kw)
{
return Runtime.PyCFunction_Call(ptr, args, kw);
}
+
+ public void Release()
+ {
+ if (_disposed)
+ {
+ return;
+ }
+ _disposed = true;
+ bool freeDef = Runtime.Refcount(ptr) == 1;
+ Runtime.XDecref(ptr);
+ if (freeDef && mdef != IntPtr.Zero)
+ {
+ Runtime.PyMem_Free(mdef);
+ mdef = IntPtr.Zero;
+ }
+ }
}
}
diff --git a/src/runtime/moduleobject.cs b/src/runtime/moduleobject.cs
index 7a45c6c81..544f69c81 100644
--- a/src/runtime/moduleobject.cs
+++ b/src/runtime/moduleobject.cs
@@ -316,6 +316,11 @@ internal class CLRModule : ModuleObject
internal static bool _SuppressDocs = false;
internal static bool _SuppressOverloads = false;
+ static CLRModule()
+ {
+ Reset();
+ }
+
public CLRModule() : base("clr")
{
_namespace = string.Empty;
diff --git a/src/runtime/runtime.cs b/src/runtime/runtime.cs
index 449d22435..130d90c0a 100644
--- a/src/runtime/runtime.cs
+++ b/src/runtime/runtime.cs
@@ -187,7 +187,6 @@ internal static void Initialize(bool initSigs = false)
IsFinalizing = false;
- CLRModule.Reset();
GenericUtil.Reset();
PyScopeManager.Reset();
ClassManager.Reset();
From e896aa6714b9d736bea58fae7c9cbf0fd01337a2 Mon Sep 17 00:00:00 2001
From: amos402
Date: Thu, 19 Dec 2019 02:49:46 +0800
Subject: [PATCH 048/998] * Decref the members of Runtime * Unified GetBuiltins
method
---
src/runtime/importhook.cs | 25 +-----
src/runtime/runtime.cs | 170 ++++++++++++++++++++++++++------------
2 files changed, 120 insertions(+), 75 deletions(-)
diff --git a/src/runtime/importhook.cs b/src/runtime/importhook.cs
index 94f0f7c94..aa3bbab6d 100644
--- a/src/runtime/importhook.cs
+++ b/src/runtime/importhook.cs
@@ -35,27 +35,6 @@ internal static void ReleaseModuleDef()
}
#endif
- ///
- /// Get a New reference to the builtins module.
- ///
- static IntPtr GetNewRefToBuiltins()
- {
- if (Runtime.IsPython3)
- {
- return Runtime.PyImport_ImportModule("builtins");
- }
- else
- {
- // dict is a borrowed ref, no need to decref
- IntPtr dict = Runtime.PyImport_GetModuleDict();
-
- // GetItemString is a borrowed ref; incref to get a new ref
- IntPtr builtins = Runtime.PyDict_GetItemString(dict, "__builtin__");
- Runtime.XIncref(builtins);
- return builtins;
- }
- }
-
///
/// Initialize just the __import__ hook itself.
///
@@ -64,7 +43,7 @@ static void InitImport()
// We replace the built-in Python __import__ with our own: first
// look in CLR modules, then if we don't find any call the default
// Python __import__.
- IntPtr builtins = GetNewRefToBuiltins();
+ IntPtr builtins = Runtime.GetBuiltins();
py_import = Runtime.PyObject_GetAttrString(builtins, "__import__");
PythonException.ThrowIfIsNull(py_import);
@@ -80,7 +59,7 @@ static void InitImport()
///
static void RestoreImport()
{
- IntPtr builtins = GetNewRefToBuiltins();
+ IntPtr builtins = Runtime.GetBuiltins();
int res = Runtime.PyObject_SetAttrString(builtins, "__import__", py_import);
PythonException.ThrowIfIsNotZero(res);
diff --git a/src/runtime/runtime.cs b/src/runtime/runtime.cs
index 130d90c0a..2443e3edc 100644
--- a/src/runtime/runtime.cs
+++ b/src/runtime/runtime.cs
@@ -169,6 +169,8 @@ public class Runtime
///
internal static readonly Encoding PyEncoding = _UCS == 2 ? Encoding.Unicode : Encoding.UTF32;
+ private static PyReferenceCollection _pyRefs = new PyReferenceCollection();
+
///
/// Initialize the runtime...
///
@@ -194,99 +196,94 @@ internal static void Initialize(bool initSigs = false)
TypeManager.Reset();
IntPtr op;
- IntPtr dict;
- if (IsPython3)
- {
- op = PyImport_ImportModule("builtins");
- dict = PyObject_GetAttrString(op, "__dict__");
- }
- else // Python2
{
- dict = PyImport_GetModuleDict();
- op = PyDict_GetItemString(dict, "__builtin__");
- }
- PyNotImplemented = PyObject_GetAttrString(op, "NotImplemented");
- PyBaseObjectType = PyObject_GetAttrString(op, "object");
+ var builtins = GetBuiltins();
+ SetPyMember(ref PyNotImplemented, PyObject_GetAttrString(builtins, "NotImplemented"));
- PyNone = PyObject_GetAttrString(op, "None");
- PyTrue = PyObject_GetAttrString(op, "True");
- PyFalse = PyObject_GetAttrString(op, "False");
+ SetPyMember(ref PyBaseObjectType, PyObject_GetAttrString(builtins, "object"));
- PyBoolType = PyObject_Type(PyTrue);
- PyNoneType = PyObject_Type(PyNone);
- PyTypeType = PyObject_Type(PyNoneType);
+ SetPyMember(ref PyNone, PyObject_GetAttrString(builtins, "None"));
+ SetPyMember(ref PyTrue, PyObject_GetAttrString(builtins, "True"));
+ SetPyMember(ref PyFalse, PyObject_GetAttrString(builtins, "False"));
- op = PyObject_GetAttrString(dict, "keys");
- PyMethodType = PyObject_Type(op);
- XDecref(op);
+ SetPyMember(ref PyBoolType, PyObject_Type(PyTrue));
+ SetPyMember(ref PyNoneType, PyObject_Type(PyNone));
+ SetPyMember(ref PyTypeType, PyObject_Type(PyNoneType));
- // For some arcane reason, builtins.__dict__.__setitem__ is *not*
- // a wrapper_descriptor, even though dict.__setitem__ is.
- //
- // object.__init__ seems safe, though.
- op = PyObject_GetAttrString(PyBaseObjectType, "__init__");
- PyWrapperDescriptorType = PyObject_Type(op);
- XDecref(op);
+ op = PyObject_GetAttrString(builtins, "len");
+ SetPyMember(ref PyMethodType, PyObject_Type(op));
+ XDecref(op);
-#if PYTHON3
- XDecref(dict);
-#endif
+ // For some arcane reason, builtins.__dict__.__setitem__ is *not*
+ // a wrapper_descriptor, even though dict.__setitem__ is.
+ //
+ // object.__init__ seems safe, though.
+ op = PyObject_GetAttrString(PyBaseObjectType, "__init__");
+ SetPyMember(ref PyWrapperDescriptorType, PyObject_Type(op));
+ XDecref(op);
+
+ SetPyMember(ref PySuper_Type, PyObject_GetAttrString(builtins, "super"));
+
+ XDecref(builtins);
+ }
op = PyString_FromString("string");
- PyStringType = PyObject_Type(op);
+ SetPyMember(ref PyStringType, PyObject_Type(op));
XDecref(op);
op = PyUnicode_FromString("unicode");
- PyUnicodeType = PyObject_Type(op);
+ SetPyMember(ref PyUnicodeType, PyObject_Type(op));
XDecref(op);
#if PYTHON3
op = PyBytes_FromString("bytes");
- PyBytesType = PyObject_Type(op);
+ SetPyMember(ref PyBytesType, PyObject_Type(op));
XDecref(op);
#endif
op = PyTuple_New(0);
- PyTupleType = PyObject_Type(op);
+ SetPyMember(ref PyTupleType, PyObject_Type(op));
XDecref(op);
op = PyList_New(0);
- PyListType = PyObject_Type(op);
+ SetPyMember(ref PyListType, PyObject_Type(op));
XDecref(op);
op = PyDict_New();
- PyDictType = PyObject_Type(op);
+ SetPyMember(ref PyDictType, PyObject_Type(op));
XDecref(op);
op = PyInt_FromInt32(0);
- PyIntType = PyObject_Type(op);
+ SetPyMember(ref PyIntType, PyObject_Type(op));
XDecref(op);
op = PyLong_FromLong(0);
- PyLongType = PyObject_Type(op);
+ SetPyMember(ref PyLongType, PyObject_Type(op));
XDecref(op);
op = PyFloat_FromDouble(0);
- PyFloatType = PyObject_Type(op);
+ SetPyMember(ref PyFloatType, PyObject_Type(op));
XDecref(op);
-#if PYTHON3
+#if !PYTHON2
PyClassType = IntPtr.Zero;
PyInstanceType = IntPtr.Zero;
-#elif PYTHON2
- IntPtr s = PyString_FromString("_temp");
- IntPtr d = PyDict_New();
+#else
+ {
+ IntPtr s = PyString_FromString("_temp");
+ IntPtr d = PyDict_New();
- IntPtr c = PyClass_New(IntPtr.Zero, d, s);
- PyClassType = PyObject_Type(c);
+ IntPtr c = PyClass_New(IntPtr.Zero, d, s);
+ SetPyMember(ref PyClassType, PyObject_Type(c));
- IntPtr i = PyInstance_New(c, IntPtr.Zero, IntPtr.Zero);
- PyInstanceType = PyObject_Type(i);
+ IntPtr i = PyInstance_New(c, IntPtr.Zero, IntPtr.Zero);
+ SetPyMember(ref PyInstanceType, PyObject_Type(i));
- XDecref(s);
- XDecref(i);
- XDecref(c);
- XDecref(d);
+ XDecref(s);
+ XDecref(i);
+ XDecref(c);
+ XDecref(d);
+ }
#endif
Error = new IntPtr(-1);
@@ -380,6 +377,9 @@ internal static void Shutdown()
Exceptions.Shutdown();
ImportHook.Shutdown();
Finalizer.Shutdown();
+ // TOOD: PyCLRMetaType's release operation still in #958
+ PyCLRMetaType = IntPtr.Zero;
+ ResetPyMembers();
Py_Finalize();
}
@@ -393,6 +393,19 @@ internal static int AtExit()
return 0;
}
+ private static void SetPyMember(ref IntPtr obj, IntPtr value)
+ {
+ // XXX: For current usages, value should not be null.
+ PythonException.ThrowIfIsNull(value);
+ obj = value;
+ _pyRefs.Add(ref obj);
+ }
+
+ private static void ResetPyMembers()
+ {
+ _pyRefs.Release();
+ }
+
internal static IntPtr Py_single_input = (IntPtr)256;
internal static IntPtr Py_file_input = (IntPtr)257;
internal static IntPtr Py_eval_input = (IntPtr)258;
@@ -401,6 +414,7 @@ internal static int AtExit()
internal static IntPtr PyModuleType;
internal static IntPtr PyClassType;
internal static IntPtr PyInstanceType;
+ internal static IntPtr PySuper_Type;
internal static IntPtr PyCLRMetaType;
internal static IntPtr PyMethodType;
internal static IntPtr PyWrapperDescriptorType;
@@ -1746,6 +1760,9 @@ internal static bool PyIter_Check(IntPtr pointer)
[DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)]
internal static extern IntPtr PyImport_Import(IntPtr name);
+ ///
+ /// Return value: New reference.
+ ///
[DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)]
internal static extern IntPtr PyImport_ImportModule(string name);
@@ -1945,5 +1962,54 @@ internal static void SetNoSiteFlag()
}
}
}
+
+ ///
+ /// Return value: New reference.
+ ///
+ internal static IntPtr GetBuiltins()
+ {
+ return IsPython3 ? PyImport_ImportModule("builtins")
+ : PyImport_ImportModule("__builtin__");
+ }
+ }
+
+
+ class PyReferenceCollection
+ {
+ public List _objects { get; private set; }
+
+ public PyReferenceCollection()
+ {
+ _objects = new List();
+ }
+
+ ///
+ /// Record obj's address to release the obj in the future,
+ /// obj must alive before calling Release.
+ ///
+ public void Add(ref IntPtr obj)
+ {
+ unsafe
+ {
+ fixed (void* p = &obj)
+ {
+ _objects.Add((IntPtr)p);
+ }
+ }
+ }
+
+ public void Release()
+ {
+ foreach (var objRef in _objects)
+ {
+ unsafe
+ {
+ var p = (void**)objRef;
+ Runtime.XDecref((IntPtr)(*p));
+ *p = null;
+ }
+ }
+ _objects.Clear();
+ }
}
}
From f0e9c380687663cbc9fa5dbd52f0e461416cce0f Mon Sep 17 00:00:00 2001
From: amos402
Date: Thu, 19 Dec 2019 07:00:18 +0800
Subject: [PATCH 049/998] Transfer the ownership of method's thunk to
caller(split from #958) (#1003)
* Add exception helper
* Make the caller of `Interop.GetThunk` handle thunk's lifecycle(unfinished)
* Use Marshal.GetFunctionPointerForDelegate instead of Marshal + Thunk
---
src/runtime/interop.cs | 43 ++++++++++++++++++++++++------------
src/runtime/methodwrapper.cs | 6 +++--
src/runtime/typemanager.cs | 17 ++++++++------
3 files changed, 43 insertions(+), 23 deletions(-)
diff --git a/src/runtime/interop.cs b/src/runtime/interop.cs
index 2e29601fd..ca3c35bfd 100644
--- a/src/runtime/interop.cs
+++ b/src/runtime/interop.cs
@@ -4,6 +4,7 @@
using System.Runtime.InteropServices;
using System.Reflection;
using System.Text;
+using System.Collections.Generic;
namespace Python.Runtime
{
@@ -334,7 +335,7 @@ internal class TypeFlags
internal class Interop
{
- private static ArrayList keepAlive;
+ private static List keepAlive;
private static Hashtable pmap;
static Interop()
@@ -351,8 +352,7 @@ static Interop()
p[item.Name] = item;
}
- keepAlive = new ArrayList();
- Marshal.AllocHGlobal(IntPtr.Size);
+ keepAlive = new List();
pmap = new Hashtable();
pmap["tp_dealloc"] = p["DestructorFunc"];
@@ -449,7 +449,7 @@ internal static Type GetPrototype(string name)
return pmap[name] as Type;
}
- internal static IntPtr GetThunk(MethodInfo method, string funcType = null)
+ internal static ThunkInfo GetThunk(MethodInfo method, string funcType = null)
{
Type dt;
if (funcType != null)
@@ -457,18 +457,15 @@ internal static IntPtr GetThunk(MethodInfo method, string funcType = null)
else
dt = GetPrototype(method.Name);
- if (dt != null)
+ if (dt == null)
{
- IntPtr tmp = Marshal.AllocHGlobal(IntPtr.Size);
- Delegate d = Delegate.CreateDelegate(dt, method);
- Thunk cb = new Thunk(d);
- Marshal.StructureToPtr(cb, tmp, false);
- IntPtr fp = Marshal.ReadIntPtr(tmp, 0);
- Marshal.FreeHGlobal(tmp);
- keepAlive.Add(d);
- return fp;
+ return ThunkInfo.Empty;
}
- return IntPtr.Zero;
+ Delegate d = Delegate.CreateDelegate(dt, method);
+ var info = new ThunkInfo(d);
+ // TODO: remove keepAlive when #958 merged, let the lifecycle of ThunkInfo transfer to caller.
+ keepAlive.Add(info);
+ return info;
}
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
@@ -522,4 +519,22 @@ public Thunk(Delegate d)
fn = d;
}
}
+
+ internal class ThunkInfo
+ {
+ public readonly Delegate Target;
+ public readonly IntPtr Address;
+
+ public static readonly ThunkInfo Empty = new ThunkInfo(null);
+
+ public ThunkInfo(Delegate target)
+ {
+ if (target == null)
+ {
+ return;
+ }
+ Target = target;
+ Address = Marshal.GetFunctionPointerForDelegate(target);
+ }
+ }
}
diff --git a/src/runtime/methodwrapper.cs b/src/runtime/methodwrapper.cs
index ba92e99d4..bc7500dab 100644
--- a/src/runtime/methodwrapper.cs
+++ b/src/runtime/methodwrapper.cs
@@ -14,17 +14,19 @@ internal class MethodWrapper
public IntPtr ptr;
private bool _disposed = false;
+ private ThunkInfo _thunk;
+
public MethodWrapper(Type type, string name, string funcType = null)
{
// Turn the managed method into a function pointer
- IntPtr fp = Interop.GetThunk(type.GetMethod(name), funcType);
+ _thunk = Interop.GetThunk(type.GetMethod(name), funcType);
// Allocate and initialize a PyMethodDef structure to represent
// the managed method, then create a PyCFunction.
mdef = Runtime.PyMem_Malloc(4 * IntPtr.Size);
- TypeManager.WriteMethodDef(mdef, name, fp, 0x0003);
+ TypeManager.WriteMethodDef(mdef, name, _thunk.Address, 0x0003);
ptr = Runtime.PyCFunction_NewEx(mdef, IntPtr.Zero, IntPtr.Zero);
}
diff --git a/src/runtime/typemanager.cs b/src/runtime/typemanager.cs
index 97e6032cd..bb920b74f 100644
--- a/src/runtime/typemanager.cs
+++ b/src/runtime/typemanager.cs
@@ -204,8 +204,8 @@ internal static IntPtr CreateType(ManagedType impl, Type clrType)
static void InitializeSlot(IntPtr type, int slotOffset, MethodInfo method)
{
- IntPtr thunk = Interop.GetThunk(method);
- Marshal.WriteIntPtr(type, slotOffset, thunk);
+ var thunk = Interop.GetThunk(method);
+ Marshal.WriteIntPtr(type, slotOffset, thunk.Address);
}
internal static IntPtr CreateSubType(IntPtr py_name, IntPtr py_base_type, IntPtr py_dict)
@@ -344,16 +344,18 @@ internal static IntPtr CreateMetaType(Type impl)
// 4 int-ptrs in size.
IntPtr mdef = Runtime.PyMem_Malloc(3 * 4 * IntPtr.Size);
IntPtr mdefStart = mdef;
+ ThunkInfo thunkInfo = Interop.GetThunk(typeof(MetaType).GetMethod("__instancecheck__"), "BinaryFunc");
mdef = WriteMethodDef(
mdef,
"__instancecheck__",
- Interop.GetThunk(typeof(MetaType).GetMethod("__instancecheck__"), "BinaryFunc")
+ thunkInfo.Address
);
+ thunkInfo = Interop.GetThunk(typeof(MetaType).GetMethod("__subclasscheck__"), "BinaryFunc");
mdef = WriteMethodDef(
mdef,
"__subclasscheck__",
- Interop.GetThunk(typeof(MetaType).GetMethod("__subclasscheck__"), "BinaryFunc")
+ thunkInfo.Address
);
// FIXME: mdef is not used
@@ -710,7 +712,8 @@ internal static void InitializeSlots(IntPtr type, Type impl)
continue;
}
- InitializeSlot(type, Interop.GetThunk(method), name);
+ var thunkInfo = Interop.GetThunk(method);
+ InitializeSlot(type, thunkInfo.Address, name);
seen.Add(name);
}
@@ -728,8 +731,8 @@ internal static void InitializeSlots(IntPtr type, Type impl)
// These have to be defined, though, so by default we fill these with
// static C# functions from this class.
- var ret0 = Interop.GetThunk(((Func)Return0).Method);
- var ret1 = Interop.GetThunk(((Func)Return1).Method);
+ var ret0 = Interop.GetThunk(((Func)Return0).Method).Address;
+ var ret1 = Interop.GetThunk(((Func)Return1).Method).Address;
if (native != null)
{
From 0dee5dad765e6c2482c59d02b3111ca25b4f7723 Mon Sep 17 00:00:00 2001
From: amos402
Date: Fri, 20 Dec 2019 17:51:45 +0800
Subject: [PATCH 050/998] Free GC handle for all subclass of ExtensionType
---
src/runtime/constructorbinding.cs | 4 ++--
src/runtime/eventbinding.cs | 2 +-
src/runtime/eventobject.cs | 2 +-
src/runtime/extensiontype.cs | 9 +++++++--
src/runtime/methodbinding.cs | 2 +-
src/runtime/methodobject.cs | 2 +-
src/runtime/moduleobject.cs | 2 +-
src/runtime/overload.cs | 2 +-
8 files changed, 15 insertions(+), 10 deletions(-)
diff --git a/src/runtime/constructorbinding.cs b/src/runtime/constructorbinding.cs
index a9f6c961b..2ec698f90 100644
--- a/src/runtime/constructorbinding.cs
+++ b/src/runtime/constructorbinding.cs
@@ -145,7 +145,7 @@ public static IntPtr tp_repr(IntPtr ob)
var self = (ConstructorBinding)GetManagedObject(ob);
Runtime.XDecref(self.repr);
Runtime.XDecref(self.pyTypeHndl);
- ExtensionType.FinalizeObject(self);
+ self.Dealloc();
}
public static int tp_traverse(IntPtr ob, IntPtr visit, IntPtr arg)
@@ -242,7 +242,7 @@ public static IntPtr tp_repr(IntPtr ob)
var self = (BoundContructor)GetManagedObject(ob);
Runtime.XDecref(self.repr);
Runtime.XDecref(self.pyTypeHndl);
- FinalizeObject(self);
+ self.Dealloc();
}
public static int tp_traverse(IntPtr ob, IntPtr visit, IntPtr arg)
diff --git a/src/runtime/eventbinding.cs b/src/runtime/eventbinding.cs
index b8b4c82ad..c844f5fd4 100644
--- a/src/runtime/eventbinding.cs
+++ b/src/runtime/eventbinding.cs
@@ -118,7 +118,7 @@ public static IntPtr tp_repr(IntPtr ob)
{
var self = (EventBinding)GetManagedObject(ob);
Runtime.XDecref(self.target);
- ExtensionType.FinalizeObject(self);
+ self.Dealloc();
}
}
}
diff --git a/src/runtime/eventobject.cs b/src/runtime/eventobject.cs
index 5f18c4609..2a98896fe 100644
--- a/src/runtime/eventobject.cs
+++ b/src/runtime/eventobject.cs
@@ -202,7 +202,7 @@ public static IntPtr tp_repr(IntPtr ob)
{
Runtime.XDecref(self.unbound.pyHandle);
}
- FinalizeObject(self);
+ self.Dealloc();
}
}
diff --git a/src/runtime/extensiontype.cs b/src/runtime/extensiontype.cs
index bc6872412..f7380b22c 100644
--- a/src/runtime/extensiontype.cs
+++ b/src/runtime/extensiontype.cs
@@ -54,6 +54,11 @@ public static void FinalizeObject(ManagedType self)
self.gcHandle.Free();
}
+ protected void Dealloc()
+ {
+ FinalizeObject(this);
+ FreeGCHandle();
+ }
///
/// Type __setattr__ implementation.
@@ -88,8 +93,8 @@ public static void tp_dealloc(IntPtr ob)
{
// Clean up a Python instance of this extension type. This
// frees the allocated Python object and decrefs the type.
- ManagedType self = GetManagedObject(ob);
- FinalizeObject(self);
+ var self = (ExtensionType)GetManagedObject(ob);
+ self.Dealloc();
}
}
}
diff --git a/src/runtime/methodbinding.cs b/src/runtime/methodbinding.cs
index 5c4164067..1caec322c 100644
--- a/src/runtime/methodbinding.cs
+++ b/src/runtime/methodbinding.cs
@@ -244,7 +244,7 @@ public static IntPtr tp_repr(IntPtr ob)
{
var self = (MethodBinding)GetManagedObject(ob);
self.ClearMembers();
- FinalizeObject(self);
+ self.Dealloc();
}
public static int tp_clear(IntPtr ob)
diff --git a/src/runtime/methodobject.cs b/src/runtime/methodobject.cs
index 847183c73..4b0987c13 100644
--- a/src/runtime/methodobject.cs
+++ b/src/runtime/methodobject.cs
@@ -208,7 +208,7 @@ public static IntPtr tp_repr(IntPtr ob)
{
var self = (MethodObject)GetManagedObject(ob);
self.ClearMembers();
- ExtensionType.FinalizeObject(self);
+ self.Dealloc();
}
public static int tp_clear(IntPtr ob)
diff --git a/src/runtime/moduleobject.cs b/src/runtime/moduleobject.cs
index 4b4f09a41..420bce85a 100644
--- a/src/runtime/moduleobject.cs
+++ b/src/runtime/moduleobject.cs
@@ -305,7 +305,7 @@ public static IntPtr tp_repr(IntPtr ob)
{
var self = (ModuleObject)GetManagedObject(ob);
tp_clear(ob);
- ExtensionType.tp_dealloc(ob);
+ self.Dealloc();
}
public static int tp_traverse(IntPtr ob, IntPtr visit, IntPtr arg)
diff --git a/src/runtime/overload.cs b/src/runtime/overload.cs
index 67868a1b1..e9fa91d3b 100644
--- a/src/runtime/overload.cs
+++ b/src/runtime/overload.cs
@@ -65,7 +65,7 @@ public static IntPtr tp_repr(IntPtr op)
{
var self = (OverloadMapper)GetManagedObject(ob);
Runtime.XDecref(self.target);
- FinalizeObject(self);
+ self.Dealloc();
}
}
}
From 00a0b320af3e30d75f001f873d0f5471d690e991 Mon Sep 17 00:00:00 2001
From: amos402
Date: Fri, 20 Dec 2019 21:20:34 +0800
Subject: [PATCH 051/998] Specific exception types
---
src/embed_tests/TestDomainReload.cs | 2 +-
src/runtime/runtime_state.cs | 8 ++++----
2 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/src/embed_tests/TestDomainReload.cs b/src/embed_tests/TestDomainReload.cs
index 6084c4062..2953a60e1 100644
--- a/src/embed_tests/TestDomainReload.cs
+++ b/src/embed_tests/TestDomainReload.cs
@@ -212,7 +212,7 @@ static void RunAssemblyAndUnload(Assembly assembly, string assemblyName)
{
Console.WriteLine($"[Program.Main] The Proxy object is valid ({theProxy}). Unexpected domain unload behavior");
}
- catch (Exception)
+ catch (AppDomainUnloadedException)
{
Console.WriteLine("[Program.Main] The Proxy object is not valid anymore, domain unload complete.");
}
diff --git a/src/runtime/runtime_state.cs b/src/runtime/runtime_state.cs
index 372a6d25b..bb91b125c 100644
--- a/src/runtime/runtime_state.cs
+++ b/src/runtime/runtime_state.cs
@@ -82,7 +82,7 @@ public static void Restore()
var dummyGCAddr = PySys_GetObject("dummy_gc");
if (dummyGCAddr == IntPtr.Zero)
{
- throw new Exception("Runtime state have not set");
+ throw new InvalidOperationException("Runtime state have not set");
}
var dummyGC = PyLong_AsVoidPtr(dummyGCAddr);
ResotreModules(dummyGC);
@@ -122,8 +122,8 @@ private static void RestoreObjects(IntPtr dummyGC)
{
throw new Exception("To prevent crash by _PyObject_GC_UNTRACK in Python internal, UseDummyGC should be enabled when using ResotreObjects");
}
- var intialObjs = PySys_GetObject("initial_objs");
- Debug.Assert(intialObjs != null);
+ IntPtr intialObjs = PySys_GetObject("initial_objs");
+ Debug.Assert(intialObjs != IntPtr.Zero);
foreach (var obj in PyGCGetObjects())
{
var p = PyLong_FromVoidPtr(obj);
@@ -192,7 +192,7 @@ private static void ExchangeGCChain(IntPtr obj, IntPtr gc)
var head = _Py_AS_GC(obj);
if ((long)_PyGCHead_REFS(head) == _PyGC_REFS_UNTRACKED)
{
- throw new Exception("GC object untracked");
+ throw new ArgumentException("GC object untracked");
}
unsafe
{
From ab0cb02fb8306f15beb9b734a8fbf56b2708ad26 Mon Sep 17 00:00:00 2001
From: amos402
Date: Sat, 21 Dec 2019 11:56:30 +0800
Subject: [PATCH 052/998] Add explicit release action
---
src/runtime/runtime.cs | 121 ++++++++++++++++++++++-------------------
1 file changed, 64 insertions(+), 57 deletions(-)
diff --git a/src/runtime/runtime.cs b/src/runtime/runtime.cs
index 2443e3edc..7748bafa9 100644
--- a/src/runtime/runtime.cs
+++ b/src/runtime/runtime.cs
@@ -198,20 +198,29 @@ internal static void Initialize(bool initSigs = false)
IntPtr op;
{
var builtins = GetBuiltins();
- SetPyMember(ref PyNotImplemented, PyObject_GetAttrString(builtins, "NotImplemented"));
-
- SetPyMember(ref PyBaseObjectType, PyObject_GetAttrString(builtins, "object"));
-
- SetPyMember(ref PyNone, PyObject_GetAttrString(builtins, "None"));
- SetPyMember(ref PyTrue, PyObject_GetAttrString(builtins, "True"));
- SetPyMember(ref PyFalse, PyObject_GetAttrString(builtins, "False"));
-
- SetPyMember(ref PyBoolType, PyObject_Type(PyTrue));
- SetPyMember(ref PyNoneType, PyObject_Type(PyNone));
- SetPyMember(ref PyTypeType, PyObject_Type(PyNoneType));
+ SetPyMember(ref PyNotImplemented, PyObject_GetAttrString(builtins, "NotImplemented"),
+ () => PyNotImplemented = IntPtr.Zero);
+
+ SetPyMember(ref PyBaseObjectType, PyObject_GetAttrString(builtins, "object"),
+ () => PyBaseObjectType = IntPtr.Zero);
+
+ SetPyMember(ref PyNone, PyObject_GetAttrString(builtins, "None"),
+ () => PyNone = IntPtr.Zero);
+ SetPyMember(ref PyTrue, PyObject_GetAttrString(builtins, "True"),
+ () => PyTrue = IntPtr.Zero);
+ SetPyMember(ref PyFalse, PyObject_GetAttrString(builtins, "False"),
+ () => PyFalse = IntPtr.Zero);
+
+ SetPyMember(ref PyBoolType, PyObject_Type(PyTrue),
+ () => PyBoolType = IntPtr.Zero);
+ SetPyMember(ref PyNoneType, PyObject_Type(PyNone),
+ () => PyNoneType = IntPtr.Zero);
+ SetPyMember(ref PyTypeType, PyObject_Type(PyNoneType),
+ () => PyTypeType = IntPtr.Zero);
op = PyObject_GetAttrString(builtins, "len");
- SetPyMember(ref PyMethodType, PyObject_Type(op));
+ SetPyMember(ref PyMethodType, PyObject_Type(op),
+ () => PyMethodType = IntPtr.Zero);
XDecref(op);
// For some arcane reason, builtins.__dict__.__setitem__ is *not*
@@ -219,50 +228,61 @@ internal static void Initialize(bool initSigs = false)
//
// object.__init__ seems safe, though.
op = PyObject_GetAttrString(PyBaseObjectType, "__init__");
- SetPyMember(ref PyWrapperDescriptorType, PyObject_Type(op));
+ SetPyMember(ref PyWrapperDescriptorType, PyObject_Type(op),
+ () => PyWrapperDescriptorType = IntPtr.Zero);
XDecref(op);
- SetPyMember(ref PySuper_Type, PyObject_GetAttrString(builtins, "super"));
+ SetPyMember(ref PySuper_Type, PyObject_GetAttrString(builtins, "super"),
+ () => PySuper_Type = IntPtr.Zero);
XDecref(builtins);
}
op = PyString_FromString("string");
- SetPyMember(ref PyStringType, PyObject_Type(op));
+ SetPyMember(ref PyStringType, PyObject_Type(op),
+ () => PyStringType = IntPtr.Zero);
XDecref(op);
op = PyUnicode_FromString("unicode");
- SetPyMember(ref PyUnicodeType, PyObject_Type(op));
+ SetPyMember(ref PyUnicodeType, PyObject_Type(op),
+ () => PyUnicodeType = IntPtr.Zero);
XDecref(op);
#if PYTHON3
op = PyBytes_FromString("bytes");
- SetPyMember(ref PyBytesType, PyObject_Type(op));
+ SetPyMember(ref PyBytesType, PyObject_Type(op),
+ () => PyBytesType = IntPtr.Zero);
XDecref(op);
#endif
op = PyTuple_New(0);
- SetPyMember(ref PyTupleType, PyObject_Type(op));
+ SetPyMember(ref PyTupleType, PyObject_Type(op),
+ () => PyTupleType = IntPtr.Zero);
XDecref(op);
op = PyList_New(0);
- SetPyMember(ref PyListType, PyObject_Type(op));
+ SetPyMember(ref PyListType, PyObject_Type(op),
+ () => PyListType = IntPtr.Zero);
XDecref(op);
op = PyDict_New();
- SetPyMember(ref PyDictType, PyObject_Type(op));
+ SetPyMember(ref PyDictType, PyObject_Type(op),
+ () => PyDictType = IntPtr.Zero);
XDecref(op);
op = PyInt_FromInt32(0);
- SetPyMember(ref PyIntType, PyObject_Type(op));
+ SetPyMember(ref PyIntType, PyObject_Type(op),
+ () => PyIntType = IntPtr.Zero);
XDecref(op);
op = PyLong_FromLong(0);
- SetPyMember(ref PyLongType, PyObject_Type(op));
+ SetPyMember(ref PyLongType, PyObject_Type(op),
+ () => PyLongType = IntPtr.Zero);
XDecref(op);
op = PyFloat_FromDouble(0);
- SetPyMember(ref PyFloatType, PyObject_Type(op));
+ SetPyMember(ref PyFloatType, PyObject_Type(op),
+ () => PyFloatType = IntPtr.Zero);
XDecref(op);
#if !PYTHON2
@@ -274,10 +294,12 @@ internal static void Initialize(bool initSigs = false)
IntPtr d = PyDict_New();
IntPtr c = PyClass_New(IntPtr.Zero, d, s);
- SetPyMember(ref PyClassType, PyObject_Type(c));
+ SetPyMember(ref PyClassType, PyObject_Type(c),
+ () => PyClassType = IntPtr.Zero);
IntPtr i = PyInstance_New(c, IntPtr.Zero, IntPtr.Zero);
- SetPyMember(ref PyInstanceType, PyObject_Type(i));
+ SetPyMember(ref PyInstanceType, PyObject_Type(i),
+ () => PyInstanceType = IntPtr.Zero);
XDecref(s);
XDecref(i);
@@ -393,12 +415,12 @@ internal static int AtExit()
return 0;
}
- private static void SetPyMember(ref IntPtr obj, IntPtr value)
+ private static void SetPyMember(ref IntPtr obj, IntPtr value, Action onRelease)
{
// XXX: For current usages, value should not be null.
PythonException.ThrowIfIsNull(value);
obj = value;
- _pyRefs.Add(ref obj);
+ _pyRefs.Add(value, onRelease);
}
private static void ResetPyMembers()
@@ -977,7 +999,7 @@ internal static int PyObject_Compare(IntPtr value1, IntPtr value2)
internal static long PyObject_Size(IntPtr pointer)
{
- return (long) _PyObject_Size(pointer);
+ return (long)_PyObject_Size(pointer);
}
[DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl, EntryPoint = "PyObject_Size")]
@@ -1093,7 +1115,7 @@ internal static bool PyLong_Check(IntPtr ob)
internal static IntPtr PyLong_FromUnsignedLong(object value)
{
- if(Is32Bit || IsWindows)
+ if (Is32Bit || IsWindows)
return PyLong_FromUnsignedLong32(Convert.ToUInt32(value));
else
return PyLong_FromUnsignedLong64(Convert.ToUInt64(value));
@@ -1283,7 +1305,7 @@ internal static int PySequence_DelSlice(IntPtr pointer, long i1, long i2)
internal static long PySequence_Size(IntPtr pointer)
{
- return (long) _PySequence_Size(pointer);
+ return (long)_PySequence_Size(pointer);
}
[DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl, EntryPoint = "PySequence_Size")]
@@ -1308,7 +1330,7 @@ internal static IntPtr PySequence_Repeat(IntPtr pointer, long count)
internal static long PySequence_Count(IntPtr pointer, IntPtr value)
{
- return (long) _PySequence_Count(pointer, value);
+ return (long)_PySequence_Count(pointer, value);
}
[DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl, EntryPoint = "PySequence_Count")]
@@ -1351,7 +1373,7 @@ internal static IntPtr PyString_FromString(string value)
internal static long PyBytes_Size(IntPtr op)
{
- return (long) _PyBytes_Size(op);
+ return (long)_PyBytes_Size(op);
}
[DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl, EntryPoint = "PyBytes_Size")]
@@ -1582,7 +1604,7 @@ internal static bool PyDict_Check(IntPtr ob)
internal static long PyDict_Size(IntPtr pointer)
{
- return (long) _PyDict_Size(pointer);
+ return (long)_PyDict_Size(pointer);
}
[DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl, EntryPoint = "PyDict_Size")]
@@ -1660,7 +1682,7 @@ internal static int PyList_SetSlice(IntPtr pointer, long start, long end, IntPtr
internal static long PyList_Size(IntPtr pointer)
{
- return (long) _PyList_Size(pointer);
+ return (long)_PyList_Size(pointer);
}
[DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl, EntryPoint = "PyList_Size")]
@@ -1709,7 +1731,7 @@ internal static IntPtr PyTuple_GetSlice(IntPtr pointer, long start, long end)
internal static long PyTuple_Size(IntPtr pointer)
{
- return (long) _PyTuple_Size(pointer);
+ return (long)_PyTuple_Size(pointer);
}
[DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl, EntryPoint = "PyTuple_Size")]
@@ -1976,40 +1998,25 @@ internal static IntPtr GetBuiltins()
class PyReferenceCollection
{
- public List _objects { get; private set; }
-
- public PyReferenceCollection()
- {
- _objects = new List();
- }
+ private List> _actions = new List>();
///
/// Record obj's address to release the obj in the future,
/// obj must alive before calling Release.
///
- public void Add(ref IntPtr obj)
+ public void Add(IntPtr ob, Action onRelease)
{
- unsafe
- {
- fixed (void* p = &obj)
- {
- _objects.Add((IntPtr)p);
- }
- }
+ _actions.Add(new KeyValuePair(ob, onRelease));
}
public void Release()
{
- foreach (var objRef in _objects)
+ foreach (var item in _actions)
{
- unsafe
- {
- var p = (void**)objRef;
- Runtime.XDecref((IntPtr)(*p));
- *p = null;
- }
+ Runtime.XDecref(item.Key);
+ item.Value?.Invoke();
}
- _objects.Clear();
+ _actions.Clear();
}
}
}
From 77da6dff83b6315cf328c164367571c2e13acc61 Mon Sep 17 00:00:00 2001
From: amos402
Date: Mon, 23 Dec 2019 15:59:59 +0800
Subject: [PATCH 053/998] Record mp_length slot
---
src/runtime/typemanager.cs | 32 ++++++++++++++++++++------------
1 file changed, 20 insertions(+), 12 deletions(-)
diff --git a/src/runtime/typemanager.cs b/src/runtime/typemanager.cs
index 771aa05ee..e7eb8e0f1 100644
--- a/src/runtime/typemanager.cs
+++ b/src/runtime/typemanager.cs
@@ -184,16 +184,17 @@ internal static IntPtr CreateType(ManagedType impl, Type clrType)
Marshal.WriteIntPtr(type, TypeOffset.tp_itemsize, IntPtr.Zero);
Marshal.WriteIntPtr(type, TypeOffset.tp_dictoffset, (IntPtr)tp_dictoffset);
+ // we want to do this after the slot stuff above in case the class itself implements a slot method
+ SlotsHolder slotsHolder = new SlotsHolder(type);
+ InitializeSlots(type, impl.GetType(), slotsHolder);
+
// add a __len__ slot for inheritors of ICollection and ICollection<>
if (typeof(ICollection).IsAssignableFrom(clrType) || clrType.GetInterfaces().Any(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof(ICollection<>)))
{
- InitializeSlot(type, TypeOffset.mp_length, typeof(mp_length_slot).GetMethod(nameof(mp_length_slot.mp_length)));
+ var method = typeof(mp_length_slot).GetMethod(nameof(mp_length_slot.mp_length));
+ InitializeSlot(type, TypeOffset.mp_length, method, slotsHolder);
}
- // we want to do this after the slot stuff above in case the class itself implements a slot method
- SlotsHolder slotsHolder = new SlotsHolder(type);
- InitializeSlots(type, impl.GetType(), slotsHolder);
-
if (base_ != IntPtr.Zero)
{
Marshal.WriteIntPtr(type, TypeOffset.tp_base, base_);
@@ -239,12 +240,6 @@ internal static IntPtr CreateType(ManagedType impl, Type clrType)
return type;
}
- static void InitializeSlot(IntPtr type, int slotOffset, MethodInfo method)
- {
- var thunk = Interop.GetThunk(method);
- Marshal.WriteIntPtr(type, slotOffset, thunk.Address);
- }
-
internal static IntPtr CreateSubType(IntPtr py_name, IntPtr py_base_type, IntPtr py_dict)
{
// Utility to create a subtype of a managed type with the ability for the
@@ -912,7 +907,20 @@ static void InitializeSlot(IntPtr type, ThunkInfo thunk, string name, SlotsHolde
return;
}
Marshal.WriteIntPtr(type, offset, thunk.Address);
- slotsHolder.Add(offset, thunk);
+ if (slotsHolder != null)
+ {
+ slotsHolder.Add(offset, thunk);
+ }
+ }
+
+ static void InitializeSlot(IntPtr type, int slotOffset, MethodInfo method, SlotsHolder slotsHolder = null)
+ {
+ var thunk = Interop.GetThunk(method);
+ Marshal.WriteIntPtr(type, slotOffset, thunk.Address);
+ if (slotsHolder != null)
+ {
+ slotsHolder.Add(slotOffset, thunk);
+ }
}
static int GetSlotOffset(string name)
From 2039e69dbd97c528ce3628303b23f3689806b571 Mon Sep 17 00:00:00 2001
From: amos402
Date: Mon, 23 Dec 2019 16:04:39 +0800
Subject: [PATCH 054/998] * Fix deadlock on domain unload * Reset
Runtime.Exceptions
---
src/embed_tests/TestDomainReload.cs | 7 ++++---
src/runtime/exceptions.cs | 22 +++++++++++-----------
src/runtime/pythonengine.cs | 19 ++++++++++---------
src/runtime/runtime.cs | 2 ++
4 files changed, 27 insertions(+), 23 deletions(-)
diff --git a/src/embed_tests/TestDomainReload.cs b/src/embed_tests/TestDomainReload.cs
index 2953a60e1..8fddf6b93 100644
--- a/src/embed_tests/TestDomainReload.cs
+++ b/src/embed_tests/TestDomainReload.cs
@@ -56,8 +56,8 @@ public static void DomainReloadAndGC()
Assembly pythonRunner1 = BuildAssembly("test1");
RunAssemblyAndUnload(pythonRunner1, "test1");
- // Verify that python is not initialized even though we ran it.
- //Assert.That(Runtime.Runtime.Py_IsInitialized(), Is.Zero);
+ Assert.That(Runtime.Runtime.Py_IsInitialized() != 0,
+ "On soft-shutdown mode, Python runtime should still running");
// This caused a crash because objects allocated in pythonRunner1
// still existed in memory, but the code to do python GC on those
@@ -83,7 +83,7 @@ public static void RunPython() {
AppDomain.CurrentDomain.DomainUnload += OnDomainUnload;
string name = AppDomain.CurrentDomain.FriendlyName;
Console.WriteLine(string.Format(""[{0} in .NET] In PythonRunner.RunPython"", name));
- //PythonEngine.Initialize(softShutdown: true);
+ PythonEngine.Initialize(softShutdown: true);
using (Py.GIL()) {
try {
var pyScript = string.Format(""import clr\n""
@@ -99,6 +99,7 @@ public static void RunPython() {
Console.WriteLine(string.Format(""[{0} in .NET] Caught exception: {1}"", name, e));
}
}
+ PythonEngine.BeginAllowThreads();
}
static void OnDomainUnload(object sender, EventArgs e) {
System.Console.WriteLine(string.Format(""[{0} in .NET] unloading"", AppDomain.CurrentDomain.FriendlyName));
diff --git a/src/runtime/exceptions.cs b/src/runtime/exceptions.cs
index 31c367eb2..c1edaced2 100644
--- a/src/runtime/exceptions.cs
+++ b/src/runtime/exceptions.cs
@@ -132,21 +132,21 @@ internal static void Initialize()
///
internal static void Shutdown()
{
- if (Runtime.Py_IsInitialized() != 0)
+ if (Runtime.Py_IsInitialized() == 0)
{
- Type type = typeof(Exceptions);
- foreach (FieldInfo fi in type.GetFields(BindingFlags.Public | BindingFlags.Static))
+ return;
+ }
+ Type type = typeof(Exceptions);
+ foreach (FieldInfo fi in type.GetFields(BindingFlags.Public | BindingFlags.Static))
+ {
+ var op = (IntPtr)fi.GetValue(type);
+ if (op != IntPtr.Zero)
{
- var op = (IntPtr)fi.GetValue(type);
- if (op != IntPtr.Zero)
- {
- Runtime.XDecref(op);
- }
+ Runtime.XDecref(op);
}
- Runtime.XDecref(exceptions_module);
- Runtime.PyObject_HasAttrString(warnings_module, "xx");
- Runtime.XDecref(warnings_module);
}
+ Runtime.Py_CLEAR(ref exceptions_module);
+ Runtime.Py_CLEAR(ref warnings_module);
}
///
diff --git a/src/runtime/pythonengine.cs b/src/runtime/pythonengine.cs
index cbf829bdb..d8e733e4d 100644
--- a/src/runtime/pythonengine.cs
+++ b/src/runtime/pythonengine.cs
@@ -324,18 +324,19 @@ public static void InitExt()
///
public static void Shutdown()
{
- if (initialized)
+ if (!initialized)
{
- PyScopeManager.Global.Clear();
-
- // If the shutdown handlers trigger a domain unload,
- // don't call shutdown again.
- AppDomain.CurrentDomain.DomainUnload -= OnDomainUnload;
+ return;
+ }
+ // If the shutdown handlers trigger a domain unload,
+ // don't call shutdown again.
+ AppDomain.CurrentDomain.DomainUnload -= OnDomainUnload;
- ExecuteShutdownHandlers();
+ PyScopeManager.Global.Clear();
+ ExecuteShutdownHandlers();
- initialized = false;
- }
+ initialized = false;
+
}
///
diff --git a/src/runtime/runtime.cs b/src/runtime/runtime.cs
index a94aa00b9..5d5c4a59a 100644
--- a/src/runtime/runtime.cs
+++ b/src/runtime/runtime.cs
@@ -394,6 +394,8 @@ internal static void Shutdown(bool soft)
{
return;
}
+ PyGILState_Ensure();
+
AssemblyManager.Shutdown();
Exceptions.Shutdown();
ImportHook.Shutdown();
From fe5050dfccb466d9824a63ef0e54123a5bf41d1e Mon Sep 17 00:00:00 2001
From: amos402
Date: Thu, 26 Dec 2019 22:26:05 +0800
Subject: [PATCH 055/998] Fix refcnt error
---
src/runtime/classbase.cs | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/runtime/classbase.cs b/src/runtime/classbase.cs
index 73d461fa1..de02e5237 100644
--- a/src/runtime/classbase.cs
+++ b/src/runtime/classbase.cs
@@ -260,6 +260,7 @@ public static IntPtr tp_repr(IntPtr ob)
//otherwise use the standard object.__repr__(inst)
IntPtr args = Runtime.PyTuple_New(1);
+ Runtime.XIncref(ob);
Runtime.PyTuple_SetItem(args, 0, ob);
IntPtr reprFunc = Runtime.PyObject_GetAttrString(Runtime.PyBaseObjectType, "__repr__");
var output = Runtime.PyObject_Call(reprFunc, args, IntPtr.Zero);
From 593fb00fc31066f1c10e104d5ecf5a25b648344a Mon Sep 17 00:00:00 2001
From: amos402
Date: Thu, 26 Dec 2019 22:30:15 +0800
Subject: [PATCH 056/998] Reset a tuple for tp_bases
---
src/runtime/typemanager.cs | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/src/runtime/typemanager.cs b/src/runtime/typemanager.cs
index e7eb8e0f1..1290903d3 100644
--- a/src/runtime/typemanager.cs
+++ b/src/runtime/typemanager.cs
@@ -1103,7 +1103,8 @@ private void ResetSlots()
IntPtr tp_bases = Marshal.ReadIntPtr(_type, TypeOffset.tp_bases);
Runtime.XDecref(tp_bases);
- Marshal.WriteIntPtr(_type, TypeOffset.tp_bases, IntPtr.Zero);
+ tp_bases = Runtime.PyTuple_New(0);
+ Marshal.WriteIntPtr(_type, TypeOffset.tp_bases, tp_bases);
}
private static void OnDestruct(IntPtr ob)
From 4e5fbe040ac5569bd297d5d87a2960304343a043 Mon Sep 17 00:00:00 2001
From: Tim Gates
Date: Tue, 31 Dec 2019 19:31:15 +1100
Subject: [PATCH 057/998] Fix simple typo: heirarchy -> hierarchy (#1026)
Closes #1025
---
src/tests/test_repr.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/tests/test_repr.py b/src/tests/test_repr.py
index d120b0c4c..5131f5d88 100644
--- a/src/tests/test_repr.py
+++ b/src/tests/test_repr.py
@@ -26,7 +26,7 @@ def test_str_only():
assert "
Date: Fri, 17 Jan 2020 22:12:45 +0800
Subject: [PATCH 058/998] Fix refcnt error
---
src/runtime/extensiontype.cs | 3 +--
src/runtime/managedtype.cs | 5 +++++
src/runtime/moduleobject.cs | 6 +++++-
src/runtime/typemanager.cs | 12 +++++-------
4 files changed, 16 insertions(+), 10 deletions(-)
diff --git a/src/runtime/extensiontype.cs b/src/runtime/extensiontype.cs
index f7380b22c..04302743d 100644
--- a/src/runtime/extensiontype.cs
+++ b/src/runtime/extensiontype.cs
@@ -51,13 +51,12 @@ public static void FinalizeObject(ManagedType self)
{
Runtime.PyObject_GC_Del(self.pyHandle);
// Not necessary for decref of `tpHandle`.
- self.gcHandle.Free();
+ self.FreeGCHandle();
}
protected void Dealloc()
{
FinalizeObject(this);
- FreeGCHandle();
}
///
diff --git a/src/runtime/managedtype.cs b/src/runtime/managedtype.cs
index 04e59a115..4b1296d31 100644
--- a/src/runtime/managedtype.cs
+++ b/src/runtime/managedtype.cs
@@ -19,6 +19,11 @@ internal abstract class ManagedType
private static readonly HashSet _managedObjs = new HashSet();
+ internal void IncrRefCount()
+ {
+ Runtime.XIncref(pyHandle);
+ }
+
internal void DecrRefCount()
{
Runtime.XDecref(pyHandle);
diff --git a/src/runtime/moduleobject.cs b/src/runtime/moduleobject.cs
index 420bce85a..bdd1551a6 100644
--- a/src/runtime/moduleobject.cs
+++ b/src/runtime/moduleobject.cs
@@ -174,7 +174,11 @@ public ManagedType GetAttribute(string name, bool guess)
///
private void StoreAttribute(string name, ManagedType ob)
{
- Runtime.PyDict_SetItemString(dict, name, ob.pyHandle);
+ if (Runtime.PyDict_SetItemString(dict, name, ob.pyHandle) != 0)
+ {
+ throw new PythonException();
+ }
+ ob.IncrRefCount();
cache[name] = ob;
}
diff --git a/src/runtime/typemanager.cs b/src/runtime/typemanager.cs
index 1290903d3..5c9004622 100644
--- a/src/runtime/typemanager.cs
+++ b/src/runtime/typemanager.cs
@@ -17,14 +17,9 @@ namespace Python.Runtime
///
internal class TypeManager
{
- private static BindingFlags tbFlags;
+ private const BindingFlags tbFlags = BindingFlags.Public | BindingFlags.Static;
private static Dictionary cache;
- static TypeManager()
- {
- tbFlags = BindingFlags.Public | BindingFlags.Static;
- }
-
public static void Reset()
{
cache = new Dictionary(128);
@@ -74,6 +69,7 @@ internal static IntPtr GetTypeHandle(Type type)
///
+ /// Return value: Borrowed reference.
/// Get the handle of a Python type that reflects the given CLR type.
/// The given ManagedType instance is a managed object that implements
/// the appropriate semantics in Python for the reflected managed type.
@@ -311,7 +307,7 @@ internal static IntPtr CreateSubType(IntPtr py_name, IntPtr py_base_type, IntPtr
// derived class we want the python overrides in there instead if they exist.
IntPtr cls_dict = Marshal.ReadIntPtr(py_type, TypeOffset.tp_dict);
Runtime.PyDict_Update(cls_dict, py_dict);
-
+ Runtime.XIncref(py_type);
return py_type;
}
catch (Exception e)
@@ -1075,7 +1071,9 @@ private void ResetSlots()
foreach (var offset in _slots.Keys)
{
IntPtr ptr = GetDefaultSlot(offset);
+#if DEBUG
//DebugUtil.Print($"Set slot<{TypeOffsetHelper.GetSlotNameByOffset(offset)}> to 0x{ptr.ToString("X")} at {typeName}<0x{_type}>");
+#endif
Marshal.WriteIntPtr(_type, offset, ptr);
}
From bdc0f72daf16bf96e5074bc9951faeee2df42fb6 Mon Sep 17 00:00:00 2001
From: amos402
Date: Sat, 18 Jan 2020 00:05:44 +0800
Subject: [PATCH 059/998] * Use subtype slots instead JIT code * Correct
signature of PyRun_String
---
src/runtime/classbase.cs | 22 +--------
src/runtime/classmanager.cs | 8 ++--
src/runtime/extensiontype.cs | 1 +
src/runtime/managedtype.cs | 30 +++++++++++-
src/runtime/methodobject.cs | 2 +
src/runtime/pyscope.cs | 6 +--
src/runtime/pythonengine.cs | 2 +-
src/runtime/runtime.cs | 13 ++----
src/runtime/typemanager.cs | 89 ++++++++++++++++++++++++++++--------
9 files changed, 112 insertions(+), 61 deletions(-)
diff --git a/src/runtime/classbase.cs b/src/runtime/classbase.cs
index de02e5237..10a451fe3 100644
--- a/src/runtime/classbase.cs
+++ b/src/runtime/classbase.cs
@@ -299,29 +299,9 @@ public static int tp_clear(IntPtr ob)
{
ClearObjectDict(ob);
}
+ self.tpHandle = IntPtr.Zero;
Runtime.Py_CLEAR(ref self.tpHandle);
return 0;
}
-
- private static IntPtr GetObjectDict(IntPtr ob)
- {
- return Marshal.ReadIntPtr(ob, ObjectOffset.DictOffset(ob));
- }
-
- private static void SetObjectDict(IntPtr ob, IntPtr value)
- {
- Marshal.WriteIntPtr(ob, ObjectOffset.DictOffset(ob), value);
- }
-
- private static void ClearObjectDict(IntPtr ob)
- {
- IntPtr dict = GetObjectDict(ob);
- if (dict == IntPtr.Zero)
- {
- return;
- }
- SetObjectDict(ob, IntPtr.Zero);
- Runtime.XDecref(dict);
- }
}
}
diff --git a/src/runtime/classmanager.cs b/src/runtime/classmanager.cs
index 43892aabc..aa6fd6e51 100644
--- a/src/runtime/classmanager.cs
+++ b/src/runtime/classmanager.cs
@@ -53,9 +53,9 @@ internal static void RemoveClasses()
{
foreach (var cls in cache.Values)
{
- cls.TypeTraverse(OnVisit, visitedPtr);
+ cls.CallTypeTraverse(OnVisit, visitedPtr);
// XXX: Force release instance resources but not dealloc itself.
- cls.TypeClear();
+ cls.CallTypeClear();
}
}
finally
@@ -75,8 +75,8 @@ private static int OnVisit(IntPtr ob, IntPtr arg)
var clrObj = ManagedType.GetManagedObject(ob);
if (clrObj != null)
{
- clrObj.TypeTraverse(OnVisit, arg);
- clrObj.TypeClear();
+ clrObj.CallTypeTraverse(OnVisit, arg);
+ clrObj.CallTypeClear();
}
return 0;
}
diff --git a/src/runtime/extensiontype.cs b/src/runtime/extensiontype.cs
index 04302743d..c1aff3ca0 100644
--- a/src/runtime/extensiontype.cs
+++ b/src/runtime/extensiontype.cs
@@ -49,6 +49,7 @@ public ExtensionType()
///
public static void FinalizeObject(ManagedType self)
{
+ ClearObjectDict(self.pyHandle);
Runtime.PyObject_GC_Del(self.pyHandle);
// Not necessary for decref of `tpHandle`.
self.FreeGCHandle();
diff --git a/src/runtime/managedtype.cs b/src/runtime/managedtype.cs
index 4b1296d31..a93628524 100644
--- a/src/runtime/managedtype.cs
+++ b/src/runtime/managedtype.cs
@@ -149,7 +149,7 @@ internal static int PyVisit(IntPtr ob, IntPtr visit, IntPtr arg)
///
/// Wrapper for calling tp_clear
///
- internal void TypeClear()
+ internal void CallTypeClear()
{
if (tpHandle == IntPtr.Zero || pyHandle == IntPtr.Zero)
{
@@ -168,7 +168,7 @@ internal void TypeClear()
///
/// Wrapper for calling tp_traverse
///
- internal void TypeTraverse(Interop.ObjObjFunc visitproc, IntPtr arg)
+ internal void CallTypeTraverse(Interop.ObjObjFunc visitproc, IntPtr arg)
{
if (tpHandle == IntPtr.Zero || pyHandle == IntPtr.Zero)
{
@@ -184,5 +184,31 @@ internal void TypeTraverse(Interop.ObjObjFunc visitproc, IntPtr arg)
// TODO: Handle errors base on return value
traverseFunc(pyHandle, visiPtr, arg);
}
+
+ protected void TypeClear()
+ {
+ ClearObjectDict(pyHandle);
+ }
+
+ protected static void ClearObjectDict(IntPtr ob)
+ {
+ IntPtr dict = GetObjectDict(ob);
+ if (dict == IntPtr.Zero)
+ {
+ return;
+ }
+ SetObjectDict(ob, IntPtr.Zero);
+ Runtime.XDecref(dict);
+ }
+
+ private static IntPtr GetObjectDict(IntPtr ob)
+ {
+ return Marshal.ReadIntPtr(ob, ObjectOffset.DictOffset(ob));
+ }
+
+ private static void SetObjectDict(IntPtr ob, IntPtr value)
+ {
+ Marshal.WriteIntPtr(ob, ObjectOffset.DictOffset(ob), value);
+ }
}
}
diff --git a/src/runtime/methodobject.cs b/src/runtime/methodobject.cs
index 4b0987c13..3942aae52 100644
--- a/src/runtime/methodobject.cs
+++ b/src/runtime/methodobject.cs
@@ -208,6 +208,7 @@ public static IntPtr tp_repr(IntPtr ob)
{
var self = (MethodObject)GetManagedObject(ob);
self.ClearMembers();
+ ClearObjectDict(ob);
self.Dealloc();
}
@@ -215,6 +216,7 @@ public static int tp_clear(IntPtr ob)
{
var self = (MethodObject)GetManagedObject(ob);
self.ClearMembers();
+ ClearObjectDict(ob);
return 0;
}
}
diff --git a/src/runtime/pyscope.cs b/src/runtime/pyscope.cs
index 4008ce29a..2a7652e3c 100644
--- a/src/runtime/pyscope.cs
+++ b/src/runtime/pyscope.cs
@@ -277,9 +277,8 @@ public PyObject Eval(string code, PyDict locals = null)
{
Check();
IntPtr _locals = locals == null ? variables : locals.obj;
- var flag = (IntPtr)Runtime.Py_eval_input;
IntPtr ptr = Runtime.PyRun_String(
- code, flag, variables, _locals
+ code, RunFlagType.Eval, variables, _locals
);
Runtime.CheckExceptionOccurred();
return new PyObject(ptr);
@@ -315,9 +314,8 @@ public void Exec(string code, PyDict locals = null)
private void Exec(string code, IntPtr _globals, IntPtr _locals)
{
- var flag = (IntPtr)Runtime.Py_file_input;
IntPtr ptr = Runtime.PyRun_String(
- code, flag, _globals, _locals
+ code, RunFlagType.File, _globals, _locals
);
Runtime.CheckExceptionOccurred();
if (ptr != Runtime.PyNone)
diff --git a/src/runtime/pythonengine.cs b/src/runtime/pythonengine.cs
index d8e733e4d..e036bab33 100644
--- a/src/runtime/pythonengine.cs
+++ b/src/runtime/pythonengine.cs
@@ -600,7 +600,7 @@ internal static PyObject RunString(string code, IntPtr? globals, IntPtr? locals,
try
{
IntPtr result = Runtime.PyRun_String(
- code, (IntPtr)flag, globals.Value, locals.Value
+ code, flag, globals.Value, locals.Value
);
Runtime.CheckExceptionOccurred();
diff --git a/src/runtime/runtime.cs b/src/runtime/runtime.cs
index 0b89838d8..18dc0c113 100644
--- a/src/runtime/runtime.cs
+++ b/src/runtime/runtime.cs
@@ -209,7 +209,7 @@ internal static void Initialize(bool initSigs = false, bool softShutdown = false
PyScopeManager.Reset();
ClassManager.Reset();
ClassDerivedObject.Reset();
- TypeManager.Reset();
+ TypeManager.Initialize();
IntPtr op;
{
@@ -527,7 +527,7 @@ private static void MoveClrInstancesOnwershipToPython()
{
continue;
}
- obj.TypeClear();
+ obj.CallTypeClear();
// obj's tp_type will degenerate to a pure Python type after TypeManager.RemoveTypes(),
// thus just be safe to give it back to GC chain.
PyObject_GC_Track(obj.pyHandle);
@@ -537,11 +537,6 @@ private static void MoveClrInstancesOnwershipToPython()
ManagedType.ClearTrackedObjects();
}
-
- internal static IntPtr Py_single_input = (IntPtr)256;
- internal static IntPtr Py_file_input = (IntPtr)257;
- internal static IntPtr Py_eval_input = (IntPtr)258;
-
internal static IntPtr PyBaseObjectType;
internal static IntPtr PyModuleType;
internal static IntPtr PyClassType;
@@ -903,7 +898,7 @@ public static extern int Py_Main(
internal static extern int PyRun_SimpleString(string code);
[DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)]
- internal static extern IntPtr PyRun_String(string code, IntPtr st, IntPtr globals, IntPtr locals);
+ internal static extern IntPtr PyRun_String(string code, RunFlagType st, IntPtr globals, IntPtr locals);
[DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)]
internal static extern IntPtr PyEval_EvalCode(IntPtr co, IntPtr globals, IntPtr locals);
@@ -2013,7 +2008,7 @@ internal static IntPtr PyType_GenericAlloc(IntPtr type, long n)
private static extern IntPtr PyType_GenericAlloc(IntPtr type, IntPtr n);
///
- /// Finalize a type object. This should be called on all type objects to finish their initialization. This function is responsible for adding inherited slots from a types base class. Return 0 on success, or return -1 and sets an exception on error.
+ /// Finalize a type object. This should be called on all type objects to finish their initialization. This function is responsible for adding inherited slots from a type’s base class. Return 0 on success, or return -1 and sets an exception on error.
///
[DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)]
internal static extern int PyType_Ready(IntPtr type);
diff --git a/src/runtime/typemanager.cs b/src/runtime/typemanager.cs
index 5c9004622..1e54b3345 100644
--- a/src/runtime/typemanager.cs
+++ b/src/runtime/typemanager.cs
@@ -17,12 +17,20 @@ namespace Python.Runtime
///
internal class TypeManager
{
+ internal static IntPtr subtype_traverse;
+ internal static IntPtr subtype_clear;
+
private const BindingFlags tbFlags = BindingFlags.Public | BindingFlags.Static;
private static Dictionary cache;
- public static void Reset()
+ public static void Initialize()
{
cache = new Dictionary(128);
+
+ IntPtr type = SlotHelper.CreateObjectType();
+ subtype_traverse = Marshal.ReadIntPtr(type, TypeOffset.tp_traverse);
+ subtype_clear = Marshal.ReadIntPtr(type, TypeOffset.tp_clear);
+ Runtime.XDecref(type);
}
public static IList GetManagedTypes()
@@ -440,7 +448,7 @@ internal static IntPtr CreateMetaType(Type impl)
mdef = WriteMethodDefSentinel(mdef);
Marshal.WriteIntPtr(type, TypeOffset.tp_methods, mdefStart);
- slotsHolder.Add(TypeOffset.tp_methods, (t, offset) =>
+ slotsHolder.Set(TypeOffset.tp_methods, (t, offset) =>
{
var p = Marshal.ReadIntPtr(t, offset);
Runtime.PyMem_Free(p);
@@ -851,7 +859,7 @@ internal static void InitializeSlots(IntPtr type, Type impl, SlotsHolder slotsHo
Marshal.WriteIntPtr(type, offset, thunkRet0.Address);
if (slotsHolder != null)
{
- slotsHolder.Add(offset, thunkRet0);
+ slotsHolder.Set(offset, thunkRet0);
}
}
if (!IsSlotSet(type, "tp_clear"))
@@ -861,7 +869,7 @@ internal static void InitializeSlots(IntPtr type, Type impl, SlotsHolder slotsHo
Marshal.WriteIntPtr(type, offset, thunkRet0.Address);
if (slotsHolder != null)
{
- slotsHolder.Add(offset, thunkRet0);
+ slotsHolder.Set(offset, thunkRet0);
}
}
}
@@ -905,7 +913,7 @@ static void InitializeSlot(IntPtr type, ThunkInfo thunk, string name, SlotsHolde
Marshal.WriteIntPtr(type, offset, thunk.Address);
if (slotsHolder != null)
{
- slotsHolder.Add(offset, thunk);
+ slotsHolder.Set(offset, thunk);
}
}
@@ -915,7 +923,7 @@ static void InitializeSlot(IntPtr type, int slotOffset, MethodInfo method, Slots
Marshal.WriteIntPtr(type, slotOffset, thunk.Address);
if (slotsHolder != null)
{
- slotsHolder.Add(slotOffset, thunk);
+ slotsHolder.Set(slotOffset, thunk);
}
}
@@ -992,9 +1000,9 @@ class SlotsHolder
private IntPtr _type;
private Dictionary _slots = new Dictionary();
private List _keepalive = new List();
- private Dictionary _customRestors = new Dictionary();
+ private Dictionary _customResetors = new Dictionary();
private List _deallocators = new List();
- private bool _alredyReset = false;
+ private bool _alreadyReset = false;
///
/// Create slots holder for holding the delegate of slots and be able to reset them.
@@ -1005,14 +1013,14 @@ public SlotsHolder(IntPtr type)
_type = type;
}
- public void Add(int offset, ThunkInfo thunk)
+ public void Set(int offset, ThunkInfo thunk)
{
- _slots.Add(offset, thunk);
+ _slots[offset] = thunk;
}
- public void Add(int offset, Resetor resetor)
+ public void Set(int offset, Resetor resetor)
{
- _customRestors[offset] = resetor;
+ _customResetors[offset] = resetor;
}
public void AddDealloctor(Action deallocate)
@@ -1059,11 +1067,11 @@ public static void ReleaseTypeSlots(IntPtr type)
private void ResetSlots()
{
- if (_alredyReset)
+ if (_alreadyReset)
{
return;
}
- _alredyReset = true;
+ _alreadyReset = true;
#if DEBUG
IntPtr tp_name = Marshal.ReadIntPtr(_type, TypeOffset.tp_name);
string typeName = Marshal.PtrToStringAnsi(tp_name);
@@ -1082,14 +1090,14 @@ private void ResetSlots()
action();
}
- foreach (var pair in _customRestors)
+ foreach (var pair in _customResetors)
{
int offset = pair.Key;
var resetor = pair.Value;
resetor?.Invoke(_type, offset);
}
- _customRestors.Clear();
+ _customResetors.Clear();
_slots.Clear();
_keepalive.Clear();
_deallocators.Clear();
@@ -1097,12 +1105,16 @@ private void ResetSlots()
// Custom reset
IntPtr tp_base = Marshal.ReadIntPtr(_type, TypeOffset.tp_base);
Runtime.XDecref(tp_base);
- Marshal.WriteIntPtr(_type, TypeOffset.tp_base, IntPtr.Zero);
+ Runtime.XIncref(Runtime.PyBaseObjectType);
+ Marshal.WriteIntPtr(_type, TypeOffset.tp_base, Runtime.PyBaseObjectType);
IntPtr tp_bases = Marshal.ReadIntPtr(_type, TypeOffset.tp_bases);
Runtime.XDecref(tp_bases);
tp_bases = Runtime.PyTuple_New(0);
Marshal.WriteIntPtr(_type, TypeOffset.tp_bases, tp_bases);
+
+ // FIXME: release dict;
+ Marshal.WriteIntPtr(_type, TypeOffset.tp_dictoffset, IntPtr.Zero);
}
private static void OnDestruct(IntPtr ob)
@@ -1122,10 +1134,13 @@ private static SlotsHolder RecoverFromCapsule(IntPtr ob)
private static IntPtr GetDefaultSlot(int offset)
{
- if (offset == TypeOffset.tp_clear
- || offset == TypeOffset.tp_traverse)
+ if (offset == TypeOffset.tp_clear)
+ {
+ return TypeManager.subtype_clear;
+ }
+ else if (offset == TypeOffset.tp_traverse)
{
- return TypeManager.NativeCodePage + TypeManager.NativeCode.Active.Return0;
+ return TypeManager.subtype_traverse;
}
else if (offset == TypeOffset.tp_dealloc)
{
@@ -1160,4 +1175,38 @@ private static IntPtr GetDefaultSlot(int offset)
return Marshal.ReadIntPtr(Runtime.PyTypeType, offset);
}
}
+
+
+ static class SlotHelper
+ {
+ public static IntPtr CreateObjectType()
+ {
+ // TODO: extract the prepare actions as a common method
+ IntPtr globals = Runtime.PyDict_New();
+ if (Runtime.PyDict_SetItemString(globals, "__builtins__", Runtime.PyEval_GetBuiltins()) != 0)
+ {
+ Runtime.XDecref(globals);
+ throw new PythonException();
+ }
+ const string code = "class A(object): pass";
+ IntPtr res = Runtime.PyRun_String(code, RunFlagType.File, globals, globals);
+ if (res == IntPtr.Zero)
+ {
+ try
+ {
+ throw new PythonException();
+ }
+ finally
+ {
+ Runtime.XDecref(globals);
+ }
+ }
+ Runtime.XDecref(res);
+ IntPtr A = Runtime.PyDict_GetItemString(globals, "A");
+ Debug.Assert(A != IntPtr.Zero);
+ Runtime.XIncref(A);
+ Runtime.XDecref(globals);
+ return A;
+ }
+ }
}
From 49d98e85f4ae249ba6813e580631cbade3a6b4c6 Mon Sep 17 00:00:00 2001
From: amos402
Date: Sat, 18 Jan 2020 13:35:39 +0800
Subject: [PATCH 060/998] Clear tp_dict of ModuleObject
---
src/runtime/classbase.cs | 1 -
src/runtime/constructorbinding.cs | 8 ++------
src/runtime/interop.cs | 11 -----------
src/runtime/moduleobject.cs | 5 +++--
src/runtime/typemanager.cs | 3 ---
5 files changed, 5 insertions(+), 23 deletions(-)
diff --git a/src/runtime/classbase.cs b/src/runtime/classbase.cs
index 10a451fe3..e98bed638 100644
--- a/src/runtime/classbase.cs
+++ b/src/runtime/classbase.cs
@@ -300,7 +300,6 @@ public static int tp_clear(IntPtr ob)
ClearObjectDict(ob);
}
self.tpHandle = IntPtr.Zero;
- Runtime.Py_CLEAR(ref self.tpHandle);
return 0;
}
}
diff --git a/src/runtime/constructorbinding.cs b/src/runtime/constructorbinding.cs
index 2ec698f90..82bcb49f1 100644
--- a/src/runtime/constructorbinding.cs
+++ b/src/runtime/constructorbinding.cs
@@ -29,8 +29,7 @@ internal class ConstructorBinding : ExtensionType
public ConstructorBinding(Type type, IntPtr pyTypeHndl, ConstructorBinder ctorBinder)
{
this.type = type;
- Runtime.XIncref(pyTypeHndl);
- this.pyTypeHndl = pyTypeHndl;
+ this.pyTypeHndl = pyTypeHndl; // steal a type reference
this.ctorBinder = ctorBinder;
repr = IntPtr.Zero;
}
@@ -144,7 +143,6 @@ public static IntPtr tp_repr(IntPtr ob)
{
var self = (ConstructorBinding)GetManagedObject(ob);
Runtime.XDecref(self.repr);
- Runtime.XDecref(self.pyTypeHndl);
self.Dealloc();
}
@@ -179,8 +177,7 @@ internal class BoundContructor : ExtensionType
public BoundContructor(Type type, IntPtr pyTypeHndl, ConstructorBinder ctorBinder, ConstructorInfo ci)
{
this.type = type;
- Runtime.XIncref(pyTypeHndl);
- this.pyTypeHndl = pyTypeHndl;
+ this.pyTypeHndl = pyTypeHndl; // steal a type reference
this.ctorBinder = ctorBinder;
ctorInfo = ci;
repr = IntPtr.Zero;
@@ -241,7 +238,6 @@ public static IntPtr tp_repr(IntPtr ob)
{
var self = (BoundContructor)GetManagedObject(ob);
Runtime.XDecref(self.repr);
- Runtime.XDecref(self.pyTypeHndl);
self.Dealloc();
}
diff --git a/src/runtime/interop.cs b/src/runtime/interop.cs
index 9aacbb07e..25b3882da 100644
--- a/src/runtime/interop.cs
+++ b/src/runtime/interop.cs
@@ -515,17 +515,6 @@ internal static ThunkInfo GetThunk(MethodInfo method, string funcType = null)
}
- [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
- internal struct Thunk
- {
- public Delegate fn;
-
- public Thunk(Delegate d)
- {
- fn = d;
- }
- }
-
internal class ThunkInfo
{
public readonly Delegate Target;
diff --git a/src/runtime/moduleobject.cs b/src/runtime/moduleobject.cs
index bdd1551a6..06f1c0581 100644
--- a/src/runtime/moduleobject.cs
+++ b/src/runtime/moduleobject.cs
@@ -53,6 +53,7 @@ public ModuleObject(string name)
Runtime.XDecref(pyfilename);
Runtime.XDecref(pydocstring);
+ Runtime.XIncref(dict);
Marshal.WriteIntPtr(pyHandle, ObjectOffset.DictOffset(pyHandle), dict);
InitializeModuleMembers();
@@ -328,8 +329,8 @@ public static int tp_traverse(IntPtr ob, IntPtr visit, IntPtr arg)
public static int tp_clear(IntPtr ob)
{
var self = (ModuleObject)GetManagedObject(ob);
- Runtime.XDecref(self.dict);
- self.dict = IntPtr.Zero;
+ Runtime.Py_CLEAR(ref self.dict);
+ ClearObjectDict(ob);
foreach (var attr in self.cache.Values)
{
Runtime.XDecref(attr.pyHandle);
diff --git a/src/runtime/typemanager.cs b/src/runtime/typemanager.cs
index 1e54b3345..e5c01ba3f 100644
--- a/src/runtime/typemanager.cs
+++ b/src/runtime/typemanager.cs
@@ -1112,9 +1112,6 @@ private void ResetSlots()
Runtime.XDecref(tp_bases);
tp_bases = Runtime.PyTuple_New(0);
Marshal.WriteIntPtr(_type, TypeOffset.tp_bases, tp_bases);
-
- // FIXME: release dict;
- Marshal.WriteIntPtr(_type, TypeOffset.tp_dictoffset, IntPtr.Zero);
}
private static void OnDestruct(IntPtr ob)
From 433d0f65872492b8ff71c0b69a84f5a95e1c5c43 Mon Sep 17 00:00:00 2001
From: amos402
Date: Sat, 18 Jan 2020 14:11:02 +0800
Subject: [PATCH 061/998] Manipulate SlotsHolder manually instead of Capsule
mechanism
---
src/runtime/metatype.cs | 10 ++--
src/runtime/typemanager.cs | 108 +++++++++----------------------------
2 files changed, 30 insertions(+), 88 deletions(-)
diff --git a/src/runtime/metatype.cs b/src/runtime/metatype.cs
index 51f3eddd7..68954ef0a 100644
--- a/src/runtime/metatype.cs
+++ b/src/runtime/metatype.cs
@@ -11,14 +11,14 @@ namespace Python.Runtime
internal class MetaType : ManagedType
{
private static IntPtr PyCLRMetaType;
-
+ private static SlotsHolder _metaSlotsHodler;
///
/// Metatype initialization. This bootstraps the CLR metatype to life.
///
public static IntPtr Initialize()
{
- PyCLRMetaType = TypeManager.CreateMetaType(typeof(MetaType));
+ PyCLRMetaType = TypeManager.CreateMetaType(typeof(MetaType), out _metaSlotsHodler);
return PyCLRMetaType;
}
@@ -26,10 +26,10 @@ public static void Release()
{
if (Runtime.Refcount(PyCLRMetaType) > 1)
{
- SlotsHolder.ReleaseTypeSlots(PyCLRMetaType);
+ _metaSlotsHodler.ResetSlots();
}
- Runtime.XDecref(PyCLRMetaType);
- PyCLRMetaType = IntPtr.Zero;
+ Runtime.Py_CLEAR(ref PyCLRMetaType);
+ _metaSlotsHodler = null;
}
///
diff --git a/src/runtime/typemanager.cs b/src/runtime/typemanager.cs
index e5c01ba3f..bd549550b 100644
--- a/src/runtime/typemanager.cs
+++ b/src/runtime/typemanager.cs
@@ -21,12 +21,11 @@ internal class TypeManager
internal static IntPtr subtype_clear;
private const BindingFlags tbFlags = BindingFlags.Public | BindingFlags.Static;
- private static Dictionary cache;
+ private static readonly Dictionary cache = new Dictionary();
+ private static readonly Dictionary _slotsHolders = new Dictionary();
public static void Initialize()
{
- cache = new Dictionary(128);
-
IntPtr type = SlotHelper.CreateObjectType();
subtype_traverse = Marshal.ReadIntPtr(type, TypeOffset.tp_traverse);
subtype_clear = Marshal.ReadIntPtr(type, TypeOffset.tp_clear);
@@ -42,15 +41,20 @@ internal static void RemoveTypes()
{
foreach (var tpHandle in cache.Values)
{
- // If refcount > 1, it needs to reset the managed slot,
- // otherwise it can dealloc without any trick.
- if (Runtime.Refcount(tpHandle) > 1)
+ SlotsHolder holder;
+ if (_slotsHolders.TryGetValue(tpHandle, out holder))
{
- SlotsHolder.ReleaseTypeSlots(tpHandle);
+ // If refcount > 1, it needs to reset the managed slot,
+ // otherwise it can dealloc without any trick.
+ if (Runtime.Refcount(tpHandle) > 1)
+ {
+ holder.ResetSlots();
+ }
}
Runtime.XDecref(tpHandle);
}
cache.Clear();
+ _slotsHolders.Clear();
}
///
@@ -115,7 +119,7 @@ internal static IntPtr CreateType(Type impl)
var offset = (IntPtr)ObjectOffset.DictOffset(type);
Marshal.WriteIntPtr(type, TypeOffset.tp_dictoffset, offset);
- SlotsHolder slotsHolder = new SlotsHolder(type);
+ SlotsHolder slotsHolder = CreateSolotsHolder(type);
InitializeSlots(type, impl, slotsHolder);
int flags = TypeFlags.Default | TypeFlags.Managed |
@@ -132,10 +136,6 @@ internal static IntPtr CreateType(Type impl)
Runtime.PyDict_SetItemString(dict, "__module__", mod);
Runtime.XDecref(mod);
- IntPtr capsule = slotsHolder.ToCapsule();
- Runtime.PyDict_SetItemString(dict, SlotsHolder.HolderKeyName, capsule);
- Runtime.XDecref(capsule);
-
InitMethods(type, impl);
return type;
}
@@ -189,7 +189,7 @@ internal static IntPtr CreateType(ManagedType impl, Type clrType)
Marshal.WriteIntPtr(type, TypeOffset.tp_dictoffset, (IntPtr)tp_dictoffset);
// we want to do this after the slot stuff above in case the class itself implements a slot method
- SlotsHolder slotsHolder = new SlotsHolder(type);
+ SlotsHolder slotsHolder = CreateSolotsHolder(type);
InitializeSlots(type, impl.GetType(), slotsHolder);
// add a __len__ slot for inheritors of ICollection and ICollection<>
@@ -227,10 +227,6 @@ internal static IntPtr CreateType(ManagedType impl, Type clrType)
Runtime.PyDict_SetItemString(dict, "__module__", mod);
Runtime.XDecref(mod);
- IntPtr capsule = slotsHolder.ToCapsule();
- Runtime.PyDict_SetItemString(dict, SlotsHolder.HolderKeyName, capsule);
- Runtime.XDecref(capsule);
-
// Hide the gchandle of the implementation in a magic type slot.
GCHandle gc = impl.AllocGCHandle();
Marshal.WriteIntPtr(type, TypeOffset.magic(), (IntPtr)gc);
@@ -365,7 +361,7 @@ internal static void FreeMethodDef(IntPtr mdef)
}
}
- internal static IntPtr CreateMetaType(Type impl)
+ internal static IntPtr CreateMetaType(Type impl, out SlotsHolder slotsHolder)
{
// The managed metatype is functionally little different than the
// standard Python metatype (PyType_Type). It overrides certain of
@@ -383,11 +379,9 @@ internal static IntPtr CreateMetaType(Type impl)
// tp_basicsize, tp_itemsize,
// tp_dictoffset, tp_weaklistoffset,
// tp_traverse, tp_clear, tp_is_gc, etc.
- //CopySlot(py_type, type, TypeOffset.tp_basicsize);
- //CopySlot(py_type, type, TypeOffset.tp_itemsize);
- SlotsHolder slotsHolder = new SlotsHolder(type);
// Override type slots with those of the managed implementation.
+ slotsHolder = new SlotsHolder(type);
InitializeSlots(type, impl, slotsHolder);
int flags = TypeFlags.Default;
@@ -464,10 +458,6 @@ internal static IntPtr CreateMetaType(Type impl)
IntPtr mod = Runtime.PyString_FromString("CLR");
Runtime.PyDict_SetItemString(dict, "__module__", mod);
- IntPtr capsule = slotsHolder.ToCapsule();
- Runtime.PyDict_SetItemString(dict, SlotsHolder.HolderKeyName, capsule);
- Runtime.XDecref(capsule);
-
//DebugUtil.DumpType(type);
return type;
@@ -502,7 +492,7 @@ internal static IntPtr BasicSubType(string name, IntPtr base_, Type impl)
CopySlot(base_, type, TypeOffset.tp_clear);
CopySlot(base_, type, TypeOffset.tp_is_gc);
- SlotsHolder slotsHolder = new SlotsHolder(type);
+ SlotsHolder slotsHolder = CreateSolotsHolder(type);
InitializeSlots(type, impl, slotsHolder);
if (Runtime.PyType_Ready(type) != 0)
@@ -514,9 +504,6 @@ internal static IntPtr BasicSubType(string name, IntPtr base_, Type impl)
IntPtr mod = Runtime.PyString_FromString("CLR");
Runtime.PyDict_SetItemString(tp_dict, "__module__", mod);
- IntPtr capsule = slotsHolder.ToCapsule();
- Runtime.PyDict_SetItemString(tp_dict, SlotsHolder.HolderKeyName, capsule);
- Runtime.XDecref(capsule);
return type;
}
@@ -986,18 +973,21 @@ internal static void CopySlot(IntPtr from, IntPtr to, int offset)
IntPtr fp = Marshal.ReadIntPtr(from, offset);
Marshal.WriteIntPtr(to, offset, fp);
}
+
+ private static SlotsHolder CreateSolotsHolder(IntPtr type)
+ {
+ var holder = new SlotsHolder(type);
+ _slotsHolders.Add(type, holder);
+ return holder;
+ }
}
class SlotsHolder
{
- public const string HolderKeyName = "_slots_holder";
public delegate void Resetor(IntPtr type, int offset);
- private GCHandle _handle;
- private Interop.DestructorFunc _destructor;
- private IntPtr _capsule;
- private IntPtr _type;
+ private readonly IntPtr _type;
private Dictionary _slots = new Dictionary();
private List _keepalive = new List();
private Dictionary _customResetors = new Dictionary();
@@ -1033,39 +1023,7 @@ public void KeeapAlive(Delegate d)
_keepalive.Add(d);
}
- public IntPtr ToCapsule()
- {
- if (_capsule != IntPtr.Zero)
- {
- Runtime.XIncref(_capsule);
- return _capsule;
- }
- _handle = GCHandle.Alloc(this);
- _destructor = OnDestruct;
- var fp = Marshal.GetFunctionPointerForDelegate(_destructor);
- _capsule = Runtime.PyCapsule_New((IntPtr)_handle, null, fp);
- return _capsule;
- }
-
- public static void ReleaseTypeSlots(IntPtr type)
- {
- IntPtr capsule = Runtime.PyObject_GetAttrString(type, HolderKeyName);
- if (capsule == IntPtr.Zero)
- {
- return;
- }
- var self = RecoverFromCapsule(capsule);
- self.ResetSlots();
- Runtime.XDecref(capsule);
-
- IntPtr tp_dict = Marshal.ReadIntPtr(type, TypeOffset.tp_dict);
- if (Runtime.PyDict_DelItemString(tp_dict, HolderKeyName) != 0)
- {
- throw new PythonException();
- }
- }
-
- private void ResetSlots()
+ public void ResetSlots()
{
if (_alreadyReset)
{
@@ -1114,21 +1072,6 @@ private void ResetSlots()
Marshal.WriteIntPtr(_type, TypeOffset.tp_bases, tp_bases);
}
- private static void OnDestruct(IntPtr ob)
- {
- var self = RecoverFromCapsule(ob);
- self._handle.Free();
- self.ResetSlots();
- }
-
- private static SlotsHolder RecoverFromCapsule(IntPtr ob)
- {
- var ptr = Runtime.PyCapsule_GetPointer(ob, null);
- PythonException.ThrowIfIsNull(ptr);
- GCHandle handle = GCHandle.FromIntPtr(ptr);
- return (SlotsHolder)handle.Target;
- }
-
private static IntPtr GetDefaultSlot(int offset)
{
if (offset == TypeOffset.tp_clear)
@@ -1178,7 +1121,6 @@ static class SlotHelper
{
public static IntPtr CreateObjectType()
{
- // TODO: extract the prepare actions as a common method
IntPtr globals = Runtime.PyDict_New();
if (Runtime.PyDict_SetItemString(globals, "__builtins__", Runtime.PyEval_GetBuiltins()) != 0)
{
From 1b466df0ad10985c562e6f29a81094a863918f6d Mon Sep 17 00:00:00 2001
From: amos402
Date: Thu, 23 Jan 2020 15:54:20 +0800
Subject: [PATCH 062/998] * Drop NativeCodePage dependency * Use
TypeOffset.members(memory of the first PythonMemberDef) as magic slot *
Improve accuracy of slot implementation detection * Fix refcnt errors
---
src/embed_tests/TestDomainReload.cs | 2 +
src/embed_tests/TestRuntime.cs | 13 +-
src/embed_tests/TestTypeManager.cs | 12 +-
src/embed_tests/pyimport.cs | 1 +
src/embed_tests/pyinitialize.cs | 2 +
src/runtime/Python.Runtime.csproj | 1 +
src/runtime/classderived.cs | 4 +
src/runtime/interop.cs | 32 +++
src/runtime/interop27.cs | 114 ++++++--
src/runtime/interop34.cs | 105 +++++--
src/runtime/interop35.cs | 116 ++++++--
src/runtime/interop36.cs | 116 ++++++--
src/runtime/interop37.cs | 116 ++++++--
src/runtime/interop38.cs | 113 ++++++--
src/runtime/platform/NativeCodePage.cs | 322 +++++++++++++++++++++
src/runtime/runtime.cs | 132 +++------
src/runtime/slots/mp_length.cs | 33 ++-
src/runtime/typemanager.cs | 370 +++++--------------------
tools/geninterop/geninterop.py | 133 ++++++---
19 files changed, 1177 insertions(+), 560 deletions(-)
create mode 100644 src/runtime/platform/NativeCodePage.cs
diff --git a/src/embed_tests/TestDomainReload.cs b/src/embed_tests/TestDomainReload.cs
index 8fddf6b93..5bfde11c1 100644
--- a/src/embed_tests/TestDomainReload.cs
+++ b/src/embed_tests/TestDomainReload.cs
@@ -64,6 +64,8 @@ public static void DomainReloadAndGC()
// objects is gone.
Assembly pythonRunner2 = BuildAssembly("test2");
RunAssemblyAndUnload(pythonRunner2, "test2");
+
+ PythonEngine.Shutdown();
}
//
diff --git a/src/embed_tests/TestRuntime.cs b/src/embed_tests/TestRuntime.cs
index 25b70fac5..fd02b4a82 100644
--- a/src/embed_tests/TestRuntime.cs
+++ b/src/embed_tests/TestRuntime.cs
@@ -27,15 +27,14 @@ public static void PlatformCache()
{
Runtime.Runtime.Initialize();
- Assert.That(Runtime.Runtime.Machine, Is.Not.EqualTo(MachineType.Other));
- Assert.That(!string.IsNullOrEmpty(Runtime.Runtime.MachineName));
+ Assert.That(NativeCodePageHelper.Machine, Is.Not.EqualTo(MachineType.Other));
+ Assert.That(!string.IsNullOrEmpty(NativeCodePageHelper.MachineName));
- Assert.That(Runtime.Runtime.OperatingSystem, Is.Not.EqualTo(OperatingSystemType.Other));
- Assert.That(!string.IsNullOrEmpty(Runtime.Runtime.OperatingSystemName));
+ Assert.That(NativeCodePageHelper.OperatingSystem, Is.Not.EqualTo(OperatingSystemType.Other));
+ Assert.That(!string.IsNullOrEmpty(NativeCodePageHelper.OperatingSystemName));
- // Don't shut down the runtime: if the python engine was initialized
- // but not shut down by another test, we'd end up in a bad state.
- }
+ Runtime.Runtime.Shutdown();
+ }
[Test]
public static void Py_IsInitializedValue()
diff --git a/src/embed_tests/TestTypeManager.cs b/src/embed_tests/TestTypeManager.cs
index 931c44236..43155e1bf 100644
--- a/src/embed_tests/TestTypeManager.cs
+++ b/src/embed_tests/TestTypeManager.cs
@@ -1,5 +1,6 @@
using NUnit.Framework;
using Python.Runtime;
+using Python.Runtime.Platform;
using System.Runtime.InteropServices;
namespace Python.EmbeddingTest
@@ -15,22 +16,21 @@ public static void Init()
[TearDown]
public static void Fini()
{
- // Don't shut down the runtime: if the python engine was initialized
- // but not shut down by another test, we'd end up in a bad state.
+ Runtime.Runtime.Shutdown();
}
[Test]
public static void TestNativeCode()
{
- Assert.That(() => { var _ = TypeManager.NativeCode.Active; }, Throws.Nothing);
- Assert.That(TypeManager.NativeCode.Active.Code.Length, Is.GreaterThan(0));
+ Assert.That(() => { var _ = NativeCodePageHelper.NativeCode.Active; }, Throws.Nothing);
+ Assert.That(NativeCodePageHelper.NativeCode.Active.Code.Length, Is.GreaterThan(0));
}
[Test]
public static void TestMemoryMapping()
{
- Assert.That(() => { var _ = TypeManager.CreateMemoryMapper(); }, Throws.Nothing);
- var mapper = TypeManager.CreateMemoryMapper();
+ Assert.That(() => { var _ = NativeCodePageHelper.CreateMemoryMapper(); }, Throws.Nothing);
+ var mapper = NativeCodePageHelper.CreateMemoryMapper();
// Allocate a read-write page.
int len = 12;
diff --git a/src/embed_tests/pyimport.cs b/src/embed_tests/pyimport.cs
index acb3433de..d76c95797 100644
--- a/src/embed_tests/pyimport.cs
+++ b/src/embed_tests/pyimport.cs
@@ -40,6 +40,7 @@ public void SetUp()
IntPtr str = Runtime.Runtime.PyString_FromString(testPath);
IntPtr path = Runtime.Runtime.PySys_GetObject("path");
Runtime.Runtime.PyList_Append(path, str);
+ Runtime.Runtime.XDecref(str);
}
[TearDown]
diff --git a/src/embed_tests/pyinitialize.cs b/src/embed_tests/pyinitialize.cs
index ea1d8d023..69ed127bd 100644
--- a/src/embed_tests/pyinitialize.cs
+++ b/src/embed_tests/pyinitialize.cs
@@ -26,6 +26,7 @@ public static void LoadDefaultArgs()
using (new PythonEngine())
using (var argv = new PyList(Runtime.Runtime.PySys_GetObject("argv")))
{
+ Runtime.Runtime.XIncref(argv.Handle);
Assert.AreNotEqual(0, argv.Length());
}
}
@@ -37,6 +38,7 @@ public static void LoadSpecificArgs()
using (new PythonEngine(args))
using (var argv = new PyList(Runtime.Runtime.PySys_GetObject("argv")))
{
+ Runtime.Runtime.XIncref(argv.Handle);
Assert.AreEqual(args[0], argv[0].ToString());
Assert.AreEqual(args[1], argv[1].ToString());
}
diff --git a/src/runtime/Python.Runtime.csproj b/src/runtime/Python.Runtime.csproj
index 508e8a668..a00f37440 100644
--- a/src/runtime/Python.Runtime.csproj
+++ b/src/runtime/Python.Runtime.csproj
@@ -143,6 +143,7 @@
+
diff --git a/src/runtime/classderived.cs b/src/runtime/classderived.cs
index ec3734ea5..f9c019cfe 100644
--- a/src/runtime/classderived.cs
+++ b/src/runtime/classderived.cs
@@ -99,6 +99,10 @@ internal static IntPtr ToPython(IPythonDerivedType obj)
// collected while Python still has a reference to it.
if (Runtime.Refcount(self.pyHandle) == 1)
{
+
+#if PYTHON_WITH_PYDEBUG
+ Runtime._Py_NewReference(self.pyHandle);
+#endif
GCHandle gc = GCHandle.Alloc(self, GCHandleType.Normal);
Marshal.WriteIntPtr(self.pyHandle, ObjectOffset.magic(self.tpHandle), (IntPtr)gc);
self.gcHandle.Free();
diff --git a/src/runtime/interop.cs b/src/runtime/interop.cs
index 25b3882da..d4b4b5119 100644
--- a/src/runtime/interop.cs
+++ b/src/runtime/interop.cs
@@ -69,6 +69,37 @@ public ModulePropertyAttribute()
}
}
+ internal static partial class TypeOffset
+ {
+ static class Helper
+ {
+ public static int magic;
+ public static readonly Dictionary NameMapping = new Dictionary();
+ }
+
+ static TypeOffset()
+ {
+ Type type = typeof(TypeOffset);
+ FieldInfo[] fields = type.GetFields();
+ int size = IntPtr.Size;
+ for (int i = 0; i < fields.Length; i++)
+ {
+ int offset = i * size;
+ FieldInfo fi = fields[i];
+ fi.SetValue(null, offset);
+ Helper.NameMapping[fi.Name] = offset;
+ }
+ // XXX: Use the members after PyHeapTypeObject as magic slot
+ Helper.magic = members;
+ }
+
+ public static int magic() => Helper.magic;
+
+ public static int GetSlotOffset(string name)
+ {
+ return Helper.NameMapping[name];
+ }
+ }
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
internal class ObjectOffset
@@ -556,4 +587,5 @@ struct PyMethodDef
public int ml_flags;
public IntPtr ml_doc;
}
+
}
diff --git a/src/runtime/interop27.cs b/src/runtime/interop27.cs
index 4782e9d3b..7186502fd 100644
--- a/src/runtime/interop27.cs
+++ b/src/runtime/interop27.cs
@@ -1,5 +1,6 @@
+
// Auto-generated by geninterop.py.
-// DO NOT MODIFIY BY HAND.
+// DO NOT MODIFY BY HAND.
#if PYTHON27
@@ -12,25 +13,10 @@
namespace Python.Runtime
{
- [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
- internal class TypeOffset
- {
- static TypeOffset()
- {
- Type type = typeof(TypeOffset);
- FieldInfo[] fi = type.GetFields();
- int size = IntPtr.Size;
- for (int i = 0; i < fi.Length; i++)
- {
- fi[i].SetValue(null, i * size);
- }
- }
-
- public static int magic()
- {
- return ob_size;
- }
+ [StructLayout(LayoutKind.Sequential)]
+ internal static partial class TypeOffset
+ {
// Auto-generated from PyHeapTypeObject in Python.h
public static int ob_refcnt = 0;
public static int ob_type = 0;
@@ -145,6 +131,94 @@ public static int magic()
/* here are optional user slots, followed by the members. */
public static int members = 0;
}
-}
+ [StructLayout(LayoutKind.Sequential)]
+ internal struct PyNumberMethods
+ {
+ public IntPtr nb_add;
+ public IntPtr nb_subtract;
+ public IntPtr nb_multiply;
+ public IntPtr nb_divide;
+ public IntPtr nb_remainder;
+ public IntPtr nb_divmod;
+ public IntPtr nb_power;
+ public IntPtr nb_negative;
+ public IntPtr nb_positive;
+ public IntPtr nb_absolute;
+ public IntPtr nb_nonzero;
+ public IntPtr nb_invert;
+ public IntPtr nb_lshift;
+ public IntPtr nb_rshift;
+ public IntPtr nb_and;
+ public IntPtr nb_xor;
+ public IntPtr nb_or;
+ public IntPtr nb_coerce;
+ public IntPtr nb_int;
+ public IntPtr nb_long;
+ public IntPtr nb_float;
+ public IntPtr nb_oct;
+ public IntPtr nb_hex;
+ public IntPtr nb_inplace_add;
+ public IntPtr nb_inplace_subtract;
+ public IntPtr nb_inplace_multiply;
+ public IntPtr nb_inplace_divide;
+ public IntPtr nb_inplace_remainder;
+ public IntPtr nb_inplace_power;
+ public IntPtr nb_inplace_lshift;
+ public IntPtr nb_inplace_rshift;
+ public IntPtr nb_inplace_and;
+ public IntPtr nb_inplace_xor;
+ public IntPtr nb_inplace_or;
+ public IntPtr nb_floor_divide;
+ public IntPtr nb_true_divide;
+ public IntPtr nb_inplace_floor_divide;
+ public IntPtr nb_inplace_true_divide;
+ public IntPtr nb_index;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ internal struct PySequenceMethods
+ {
+ public IntPtr sq_length;
+ public IntPtr sq_concat;
+ public IntPtr sq_repeat;
+ public IntPtr sq_item;
+ public IntPtr sq_slice;
+ public IntPtr sq_ass_item;
+ public IntPtr sq_ass_slice;
+ public IntPtr sq_contains;
+ public IntPtr sq_inplace_concat;
+ public IntPtr sq_inplace_repeat;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ internal struct PyMappingMethods
+ {
+ public IntPtr mp_length;
+ public IntPtr mp_subscript;
+ public IntPtr mp_ass_subscript;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ internal struct PyBufferProcs
+ {
+ public IntPtr bf_getreadbuffer;
+ public IntPtr bf_getwritebuffer;
+ public IntPtr bf_getsegcount;
+ public IntPtr bf_getcharbuffer;
+ public IntPtr bf_getbuffer;
+ public IntPtr bf_releasebuffer;
+ }
+
+ internal static partial class SlotTypes
+ {
+ public static readonly Type[] Types = {
+ typeof(PyNumberMethods),
+ typeof(PySequenceMethods),
+ typeof(PyMappingMethods),
+ typeof(PyBufferProcs),
+ };
+ }
+
+}
#endif
diff --git a/src/runtime/interop34.cs b/src/runtime/interop34.cs
index 6857ff2d0..ae5f55135 100644
--- a/src/runtime/interop34.cs
+++ b/src/runtime/interop34.cs
@@ -1,5 +1,6 @@
+
// Auto-generated by geninterop.py.
-// DO NOT MODIFIY BY HAND.
+// DO NOT MODIFY BY HAND.
#if PYTHON34
@@ -12,25 +13,10 @@
namespace Python.Runtime
{
- [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
- internal class TypeOffset
- {
- static TypeOffset()
- {
- Type type = typeof(TypeOffset);
- FieldInfo[] fi = type.GetFields();
- int size = IntPtr.Size;
- for (int i = 0; i < fi.Length; i++)
- {
- fi[i].SetValue(null, i * size);
- }
- }
-
- public static int magic()
- {
- return ob_size;
- }
+ [StructLayout(LayoutKind.Sequential)]
+ internal static partial class TypeOffset
+ {
// Auto-generated from PyHeapTypeObject in Python.h
public static int ob_refcnt = 0;
public static int ob_type = 0;
@@ -139,6 +125,85 @@ public static int magic()
/* here are optional user slots, followed by the members. */
public static int members = 0;
}
-}
+ [StructLayout(LayoutKind.Sequential)]
+ internal struct PyNumberMethods
+ {
+ public IntPtr nb_add;
+ public IntPtr nb_subtract;
+ public IntPtr nb_multiply;
+ public IntPtr nb_remainder;
+ public IntPtr nb_divmod;
+ public IntPtr nb_power;
+ public IntPtr nb_negative;
+ public IntPtr nb_positive;
+ public IntPtr nb_absolute;
+ public IntPtr nb_bool;
+ public IntPtr nb_invert;
+ public IntPtr nb_lshift;
+ public IntPtr nb_rshift;
+ public IntPtr nb_and;
+ public IntPtr nb_xor;
+ public IntPtr nb_or;
+ public IntPtr nb_int;
+ public IntPtr nb_reserved;
+ public IntPtr nb_float;
+ public IntPtr nb_inplace_add;
+ public IntPtr nb_inplace_subtract;
+ public IntPtr nb_inplace_multiply;
+ public IntPtr nb_inplace_remainder;
+ public IntPtr nb_inplace_power;
+ public IntPtr nb_inplace_lshift;
+ public IntPtr nb_inplace_rshift;
+ public IntPtr nb_inplace_and;
+ public IntPtr nb_inplace_xor;
+ public IntPtr nb_inplace_or;
+ public IntPtr nb_floor_divide;
+ public IntPtr nb_true_divide;
+ public IntPtr nb_inplace_floor_divide;
+ public IntPtr nb_inplace_true_divide;
+ public IntPtr nb_index;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ internal struct PySequenceMethods
+ {
+ public IntPtr sq_length;
+ public IntPtr sq_concat;
+ public IntPtr sq_repeat;
+ public IntPtr sq_item;
+ public IntPtr was_sq_slice;
+ public IntPtr sq_ass_item;
+ public IntPtr was_sq_ass_slice;
+ public IntPtr sq_contains;
+ public IntPtr sq_inplace_concat;
+ public IntPtr sq_inplace_repeat;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ internal struct PyMappingMethods
+ {
+ public IntPtr mp_length;
+ public IntPtr mp_subscript;
+ public IntPtr mp_ass_subscript;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ internal struct PyBufferProcs
+ {
+ public IntPtr bf_getbuffer;
+ public IntPtr bf_releasebuffer;
+ }
+
+ internal static partial class SlotTypes
+ {
+ public static readonly Type[] Types = {
+ typeof(PyNumberMethods),
+ typeof(PySequenceMethods),
+ typeof(PyMappingMethods),
+ typeof(PyBufferProcs),
+ };
+ }
+
+}
#endif
diff --git a/src/runtime/interop35.cs b/src/runtime/interop35.cs
index a30bfa4fd..d13da73d4 100644
--- a/src/runtime/interop35.cs
+++ b/src/runtime/interop35.cs
@@ -1,5 +1,6 @@
+
// Auto-generated by geninterop.py.
-// DO NOT MODIFIY BY HAND.
+// DO NOT MODIFY BY HAND.
#if PYTHON35
@@ -12,25 +13,10 @@
namespace Python.Runtime
{
- [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
- internal class TypeOffset
- {
- static TypeOffset()
- {
- Type type = typeof(TypeOffset);
- FieldInfo[] fi = type.GetFields();
- int size = IntPtr.Size;
- for (int i = 0; i < fi.Length; i++)
- {
- fi[i].SetValue(null, i * size);
- }
- }
-
- public static int magic()
- {
- return ob_size;
- }
+ [StructLayout(LayoutKind.Sequential)]
+ internal static partial class TypeOffset
+ {
// Auto-generated from PyHeapTypeObject in Python.h
public static int ob_refcnt = 0;
public static int ob_type = 0;
@@ -144,6 +130,96 @@ public static int magic()
/* here are optional user slots, followed by the members. */
public static int members = 0;
}
-}
+ [StructLayout(LayoutKind.Sequential)]
+ internal struct PyNumberMethods
+ {
+ public IntPtr nb_add;
+ public IntPtr nb_subtract;
+ public IntPtr nb_multiply;
+ public IntPtr nb_remainder;
+ public IntPtr nb_divmod;
+ public IntPtr nb_power;
+ public IntPtr nb_negative;
+ public IntPtr nb_positive;
+ public IntPtr nb_absolute;
+ public IntPtr nb_bool;
+ public IntPtr nb_invert;
+ public IntPtr nb_lshift;
+ public IntPtr nb_rshift;
+ public IntPtr nb_and;
+ public IntPtr nb_xor;
+ public IntPtr nb_or;
+ public IntPtr nb_int;
+ public IntPtr nb_reserved;
+ public IntPtr nb_float;
+ public IntPtr nb_inplace_add;
+ public IntPtr nb_inplace_subtract;
+ public IntPtr nb_inplace_multiply;
+ public IntPtr nb_inplace_remainder;
+ public IntPtr nb_inplace_power;
+ public IntPtr nb_inplace_lshift;
+ public IntPtr nb_inplace_rshift;
+ public IntPtr nb_inplace_and;
+ public IntPtr nb_inplace_xor;
+ public IntPtr nb_inplace_or;
+ public IntPtr nb_floor_divide;
+ public IntPtr nb_true_divide;
+ public IntPtr nb_inplace_floor_divide;
+ public IntPtr nb_inplace_true_divide;
+ public IntPtr nb_index;
+ public IntPtr nb_matrix_multiply;
+ public IntPtr nb_inplace_matrix_multiply;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ internal struct PySequenceMethods
+ {
+ public IntPtr sq_length;
+ public IntPtr sq_concat;
+ public IntPtr sq_repeat;
+ public IntPtr sq_item;
+ public IntPtr was_sq_slice;
+ public IntPtr sq_ass_item;
+ public IntPtr was_sq_ass_slice;
+ public IntPtr sq_contains;
+ public IntPtr sq_inplace_concat;
+ public IntPtr sq_inplace_repeat;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ internal struct PyMappingMethods
+ {
+ public IntPtr mp_length;
+ public IntPtr mp_subscript;
+ public IntPtr mp_ass_subscript;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ internal struct PyAsyncMethods
+ {
+ public IntPtr am_await;
+ public IntPtr am_aiter;
+ public IntPtr am_anext;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ internal struct PyBufferProcs
+ {
+ public IntPtr bf_getbuffer;
+ public IntPtr bf_releasebuffer;
+ }
+
+ internal static partial class SlotTypes
+ {
+ public static readonly Type[] Types = {
+ typeof(PyNumberMethods),
+ typeof(PySequenceMethods),
+ typeof(PyMappingMethods),
+ typeof(PyAsyncMethods),
+ typeof(PyBufferProcs),
+ };
+ }
+
+}
#endif
diff --git a/src/runtime/interop36.cs b/src/runtime/interop36.cs
index c46bcc2f5..d68539d56 100644
--- a/src/runtime/interop36.cs
+++ b/src/runtime/interop36.cs
@@ -1,5 +1,6 @@
+
// Auto-generated by geninterop.py.
-// DO NOT MODIFIY BY HAND.
+// DO NOT MODIFY BY HAND.
#if PYTHON36
@@ -12,25 +13,10 @@
namespace Python.Runtime
{
- [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
- internal class TypeOffset
- {
- static TypeOffset()
- {
- Type type = typeof(TypeOffset);
- FieldInfo[] fi = type.GetFields();
- int size = IntPtr.Size;
- for (int i = 0; i < fi.Length; i++)
- {
- fi[i].SetValue(null, i * size);
- }
- }
-
- public static int magic()
- {
- return ob_size;
- }
+ [StructLayout(LayoutKind.Sequential)]
+ internal static partial class TypeOffset
+ {
// Auto-generated from PyHeapTypeObject in Python.h
public static int ob_refcnt = 0;
public static int ob_type = 0;
@@ -144,6 +130,96 @@ public static int magic()
/* here are optional user slots, followed by the members. */
public static int members = 0;
}
-}
+ [StructLayout(LayoutKind.Sequential)]
+ internal struct PyNumberMethods
+ {
+ public IntPtr nb_add;
+ public IntPtr nb_subtract;
+ public IntPtr nb_multiply;
+ public IntPtr nb_remainder;
+ public IntPtr nb_divmod;
+ public IntPtr nb_power;
+ public IntPtr nb_negative;
+ public IntPtr nb_positive;
+ public IntPtr nb_absolute;
+ public IntPtr nb_bool;
+ public IntPtr nb_invert;
+ public IntPtr nb_lshift;
+ public IntPtr nb_rshift;
+ public IntPtr nb_and;
+ public IntPtr nb_xor;
+ public IntPtr nb_or;
+ public IntPtr nb_int;
+ public IntPtr nb_reserved;
+ public IntPtr nb_float;
+ public IntPtr nb_inplace_add;
+ public IntPtr nb_inplace_subtract;
+ public IntPtr nb_inplace_multiply;
+ public IntPtr nb_inplace_remainder;
+ public IntPtr nb_inplace_power;
+ public IntPtr nb_inplace_lshift;
+ public IntPtr nb_inplace_rshift;
+ public IntPtr nb_inplace_and;
+ public IntPtr nb_inplace_xor;
+ public IntPtr nb_inplace_or;
+ public IntPtr nb_floor_divide;
+ public IntPtr nb_true_divide;
+ public IntPtr nb_inplace_floor_divide;
+ public IntPtr nb_inplace_true_divide;
+ public IntPtr nb_index;
+ public IntPtr nb_matrix_multiply;
+ public IntPtr nb_inplace_matrix_multiply;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ internal struct PySequenceMethods
+ {
+ public IntPtr sq_length;
+ public IntPtr sq_concat;
+ public IntPtr sq_repeat;
+ public IntPtr sq_item;
+ public IntPtr was_sq_slice;
+ public IntPtr sq_ass_item;
+ public IntPtr was_sq_ass_slice;
+ public IntPtr sq_contains;
+ public IntPtr sq_inplace_concat;
+ public IntPtr sq_inplace_repeat;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ internal struct PyMappingMethods
+ {
+ public IntPtr mp_length;
+ public IntPtr mp_subscript;
+ public IntPtr mp_ass_subscript;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ internal struct PyAsyncMethods
+ {
+ public IntPtr am_await;
+ public IntPtr am_aiter;
+ public IntPtr am_anext;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ internal struct PyBufferProcs
+ {
+ public IntPtr bf_getbuffer;
+ public IntPtr bf_releasebuffer;
+ }
+
+ internal static partial class SlotTypes
+ {
+ public static readonly Type[] Types = {
+ typeof(PyNumberMethods),
+ typeof(PySequenceMethods),
+ typeof(PyMappingMethods),
+ typeof(PyAsyncMethods),
+ typeof(PyBufferProcs),
+ };
+ }
+
+}
#endif
diff --git a/src/runtime/interop37.cs b/src/runtime/interop37.cs
index d5fc76ad3..c85d06525 100644
--- a/src/runtime/interop37.cs
+++ b/src/runtime/interop37.cs
@@ -1,5 +1,6 @@
+
// Auto-generated by geninterop.py.
-// DO NOT MODIFIY BY HAND.
+// DO NOT MODIFY BY HAND.
#if PYTHON37
@@ -12,25 +13,10 @@
namespace Python.Runtime
{
- [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
- internal class TypeOffset
- {
- static TypeOffset()
- {
- Type type = typeof(TypeOffset);
- FieldInfo[] fi = type.GetFields();
- int size = IntPtr.Size;
- for (int i = 0; i < fi.Length; i++)
- {
- fi[i].SetValue(null, i * size);
- }
- }
-
- public static int magic()
- {
- return ob_size;
- }
+ [StructLayout(LayoutKind.Sequential)]
+ internal static partial class TypeOffset
+ {
// Auto-generated from PyHeapTypeObject in Python.h
public static int ob_refcnt = 0;
public static int ob_type = 0;
@@ -144,6 +130,96 @@ public static int magic()
/* here are optional user slots, followed by the members. */
public static int members = 0;
}
-}
+ [StructLayout(LayoutKind.Sequential)]
+ internal struct PyNumberMethods
+ {
+ public IntPtr nb_add;
+ public IntPtr nb_subtract;
+ public IntPtr nb_multiply;
+ public IntPtr nb_remainder;
+ public IntPtr nb_divmod;
+ public IntPtr nb_power;
+ public IntPtr nb_negative;
+ public IntPtr nb_positive;
+ public IntPtr nb_absolute;
+ public IntPtr nb_bool;
+ public IntPtr nb_invert;
+ public IntPtr nb_lshift;
+ public IntPtr nb_rshift;
+ public IntPtr nb_and;
+ public IntPtr nb_xor;
+ public IntPtr nb_or;
+ public IntPtr nb_int;
+ public IntPtr nb_reserved;
+ public IntPtr nb_float;
+ public IntPtr nb_inplace_add;
+ public IntPtr nb_inplace_subtract;
+ public IntPtr nb_inplace_multiply;
+ public IntPtr nb_inplace_remainder;
+ public IntPtr nb_inplace_power;
+ public IntPtr nb_inplace_lshift;
+ public IntPtr nb_inplace_rshift;
+ public IntPtr nb_inplace_and;
+ public IntPtr nb_inplace_xor;
+ public IntPtr nb_inplace_or;
+ public IntPtr nb_floor_divide;
+ public IntPtr nb_true_divide;
+ public IntPtr nb_inplace_floor_divide;
+ public IntPtr nb_inplace_true_divide;
+ public IntPtr nb_index;
+ public IntPtr nb_matrix_multiply;
+ public IntPtr nb_inplace_matrix_multiply;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ internal struct PySequenceMethods
+ {
+ public IntPtr sq_length;
+ public IntPtr sq_concat;
+ public IntPtr sq_repeat;
+ public IntPtr sq_item;
+ public IntPtr was_sq_slice;
+ public IntPtr sq_ass_item;
+ public IntPtr was_sq_ass_slice;
+ public IntPtr sq_contains;
+ public IntPtr sq_inplace_concat;
+ public IntPtr sq_inplace_repeat;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ internal struct PyMappingMethods
+ {
+ public IntPtr mp_length;
+ public IntPtr mp_subscript;
+ public IntPtr mp_ass_subscript;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ internal struct PyAsyncMethods
+ {
+ public IntPtr am_await;
+ public IntPtr am_aiter;
+ public IntPtr am_anext;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ internal struct PyBufferProcs
+ {
+ public IntPtr bf_getbuffer;
+ public IntPtr bf_releasebuffer;
+ }
+
+ internal static partial class SlotTypes
+ {
+ public static readonly Type[] Types = {
+ typeof(PyNumberMethods),
+ typeof(PySequenceMethods),
+ typeof(PyMappingMethods),
+ typeof(PyAsyncMethods),
+ typeof(PyBufferProcs),
+ };
+ }
+
+}
#endif
diff --git a/src/runtime/interop38.cs b/src/runtime/interop38.cs
index 9126bca6a..a87573e90 100644
--- a/src/runtime/interop38.cs
+++ b/src/runtime/interop38.cs
@@ -13,25 +13,10 @@
namespace Python.Runtime
{
- [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
- internal class TypeOffset
- {
- static TypeOffset()
- {
- Type type = typeof(TypeOffset);
- FieldInfo[] fi = type.GetFields();
- int size = IntPtr.Size;
- for (int i = 0; i < fi.Length; i++)
- {
- fi[i].SetValue(null, i * size);
- }
- }
-
- public static int magic()
- {
- return ob_size;
- }
+ [StructLayout(LayoutKind.Sequential)]
+ internal static partial class TypeOffset
+ {
// Auto-generated from PyHeapTypeObject in Python.h
public static int ob_refcnt = 0;
public static int ob_type = 0;
@@ -147,6 +132,96 @@ public static int magic()
/* here are optional user slots, followed by the members. */
public static int members = 0;
}
-}
+ [StructLayout(LayoutKind.Sequential)]
+ internal struct PyNumberMethods
+ {
+ public IntPtr nb_add;
+ public IntPtr nb_subtract;
+ public IntPtr nb_multiply;
+ public IntPtr nb_remainder;
+ public IntPtr nb_divmod;
+ public IntPtr nb_power;
+ public IntPtr nb_negative;
+ public IntPtr nb_positive;
+ public IntPtr nb_absolute;
+ public IntPtr nb_bool;
+ public IntPtr nb_invert;
+ public IntPtr nb_lshift;
+ public IntPtr nb_rshift;
+ public IntPtr nb_and;
+ public IntPtr nb_xor;
+ public IntPtr nb_or;
+ public IntPtr nb_int;
+ public IntPtr nb_reserved;
+ public IntPtr nb_float;
+ public IntPtr nb_inplace_add;
+ public IntPtr nb_inplace_subtract;
+ public IntPtr nb_inplace_multiply;
+ public IntPtr nb_inplace_remainder;
+ public IntPtr nb_inplace_power;
+ public IntPtr nb_inplace_lshift;
+ public IntPtr nb_inplace_rshift;
+ public IntPtr nb_inplace_and;
+ public IntPtr nb_inplace_xor;
+ public IntPtr nb_inplace_or;
+ public IntPtr nb_floor_divide;
+ public IntPtr nb_true_divide;
+ public IntPtr nb_inplace_floor_divide;
+ public IntPtr nb_inplace_true_divide;
+ public IntPtr nb_index;
+ public IntPtr nb_matrix_multiply;
+ public IntPtr nb_inplace_matrix_multiply;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ internal struct PySequenceMethods
+ {
+ public IntPtr sq_length;
+ public IntPtr sq_concat;
+ public IntPtr sq_repeat;
+ public IntPtr sq_item;
+ public IntPtr was_sq_slice;
+ public IntPtr sq_ass_item;
+ public IntPtr was_sq_ass_slice;
+ public IntPtr sq_contains;
+ public IntPtr sq_inplace_concat;
+ public IntPtr sq_inplace_repeat;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ internal struct PyMappingMethods
+ {
+ public IntPtr mp_length;
+ public IntPtr mp_subscript;
+ public IntPtr mp_ass_subscript;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ internal struct PyAsyncMethods
+ {
+ public IntPtr am_await;
+ public IntPtr am_aiter;
+ public IntPtr am_anext;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ internal struct PyBufferProcs
+ {
+ public IntPtr bf_getbuffer;
+ public IntPtr bf_releasebuffer;
+ }
+
+ internal static partial class SlotTypes
+ {
+ public static readonly Type[] Types = {
+ typeof(PyNumberMethods),
+ typeof(PySequenceMethods),
+ typeof(PyMappingMethods),
+ typeof(PyAsyncMethods),
+ typeof(PyBufferProcs),
+ };
+ }
+
+}
#endif
diff --git a/src/runtime/platform/NativeCodePage.cs b/src/runtime/platform/NativeCodePage.cs
new file mode 100644
index 000000000..3f89e68ab
--- /dev/null
+++ b/src/runtime/platform/NativeCodePage.cs
@@ -0,0 +1,322 @@
+using System;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+
+namespace Python.Runtime.Platform
+{
+ class NativeCodePageHelper
+ {
+ ///
+ /// Gets the operating system as reported by python's platform.system().
+ ///
+ public static OperatingSystemType OperatingSystem { get; private set; }
+
+ ///
+ /// Gets the operating system as reported by python's platform.system().
+ ///
+ public static string OperatingSystemName { get; private set; }
+
+ ///
+ /// Gets the machine architecture as reported by python's platform.machine().
+ ///
+ public static MachineType Machine { get; private set; }/* set in Initialize using python's platform.machine */
+
+ ///
+ /// Gets the machine architecture as reported by python's platform.machine().
+ ///
+ public static string MachineName { get; private set; }
+
+ ///
+ /// Initialized by InitializeNativeCodePage.
+ ///
+ /// This points to a page of memory allocated using mmap or VirtualAlloc
+ /// (depending on the system), and marked read and execute (not write).
+ /// Very much on purpose, the page is *not* released on a shutdown and
+ /// is instead leaked. See the TestDomainReload test case.
+ ///
+ /// The contents of the page are two native functions: one that returns 0,
+ /// one that returns 1.
+ ///
+ /// If python didn't keep its gc list through a Py_Finalize we could remove
+ /// this entire section.
+ ///
+ internal static IntPtr NativeCodePage = IntPtr.Zero;
+
+
+ static readonly Dictionary OperatingSystemTypeMapping = new Dictionary()
+ {
+ { "Windows", OperatingSystemType.Windows },
+ { "Darwin", OperatingSystemType.Darwin },
+ { "Linux", OperatingSystemType.Linux },
+ };
+
+ ///
+ /// Map lower-case version of the python machine name to the processor
+ /// type. There are aliases, e.g. x86_64 and amd64 are two names for
+ /// the same thing. Make sure to lower-case the search string, because
+ /// capitalization can differ.
+ ///
+ static readonly Dictionary MachineTypeMapping = new Dictionary()
+ {
+ ["i386"] = MachineType.i386,
+ ["i686"] = MachineType.i386,
+ ["x86"] = MachineType.i386,
+ ["x86_64"] = MachineType.x86_64,
+ ["amd64"] = MachineType.x86_64,
+ ["x64"] = MachineType.x86_64,
+ ["em64t"] = MachineType.x86_64,
+ ["armv7l"] = MachineType.armv7l,
+ ["armv8"] = MachineType.armv8,
+ ["aarch64"] = MachineType.aarch64,
+ };
+
+ ///
+ /// Structure to describe native code.
+ ///
+ /// Use NativeCode.Active to get the native code for the current platform.
+ ///
+ /// Generate the code by creating the following C code:
+ ///
+ /// int Return0() { return 0; }
+ /// int Return1() { return 1; }
+ ///
+ /// Then compiling on the target platform, e.g. with gcc or clang:
+ /// cc -c -fomit-frame-pointer -O2 foo.c
+ /// And then analyzing the resulting functions with a hex editor, e.g.:
+ /// objdump -disassemble foo.o
+ ///
+ internal class NativeCode
+ {
+ ///
+ /// The code, as a string of bytes.
+ ///
+ public byte[] Code { get; private set; }
+
+ ///
+ /// Where does the "return 0" function start?
+ ///
+ public int Return0 { get; private set; }
+
+ ///
+ /// Where does the "return 1" function start?
+ ///
+ public int Return1 { get; private set; }
+
+ public static NativeCode Active
+ {
+ get
+ {
+ switch (Machine)
+ {
+ case MachineType.i386:
+ return I386;
+ case MachineType.x86_64:
+ return X86_64;
+ default:
+ return null;
+ }
+ }
+ }
+
+ ///
+ /// Code for x86_64. See the class comment for how it was generated.
+ ///
+ public static readonly NativeCode X86_64 = new NativeCode()
+ {
+ Return0 = 0x10,
+ Return1 = 0,
+ Code = new byte[]
+ {
+ // First Return1:
+ 0xb8, 0x01, 0x00, 0x00, 0x00, // movl $1, %eax
+ 0xc3, // ret
+
+ // Now some padding so that Return0 can be 16-byte-aligned.
+ // I put Return1 first so there's not as much padding to type in.
+ 0x66, 0x2e, 0x0f, 0x1f, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, // nop
+
+ // Now Return0.
+ 0x31, 0xc0, // xorl %eax, %eax
+ 0xc3, // ret
+ }
+ };
+
+ ///
+ /// Code for X86.
+ ///
+ /// It's bitwise identical to X86_64, so we just point to it.
+ ///
+ ///
+ public static readonly NativeCode I386 = X86_64;
+ }
+
+ ///
+ /// Platform-dependent mmap and mprotect.
+ ///
+ internal interface IMemoryMapper
+ {
+ ///
+ /// Map at least numBytes of memory. Mark the page read-write (but not exec).
+ ///
+ IntPtr MapWriteable(int numBytes);
+
+ ///
+ /// Sets the mapped memory to be read-exec (but not write).
+ ///
+ void SetReadExec(IntPtr mappedMemory, int numBytes);
+ }
+
+ class WindowsMemoryMapper : IMemoryMapper
+ {
+ const UInt32 MEM_COMMIT = 0x1000;
+ const UInt32 MEM_RESERVE = 0x2000;
+ const UInt32 PAGE_READWRITE = 0x04;
+ const UInt32 PAGE_EXECUTE_READ = 0x20;
+
+ [DllImport("kernel32.dll")]
+ static extern IntPtr VirtualAlloc(IntPtr lpAddress, IntPtr dwSize, UInt32 flAllocationType, UInt32 flProtect);
+
+ [DllImport("kernel32.dll")]
+ static extern bool VirtualProtect(IntPtr lpAddress, IntPtr dwSize, UInt32 flNewProtect, out UInt32 lpflOldProtect);
+
+ public IntPtr MapWriteable(int numBytes)
+ {
+ return VirtualAlloc(IntPtr.Zero, new IntPtr(numBytes),
+ MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
+ }
+
+ public void SetReadExec(IntPtr mappedMemory, int numBytes)
+ {
+ UInt32 _;
+ VirtualProtect(mappedMemory, new IntPtr(numBytes), PAGE_EXECUTE_READ, out _);
+ }
+ }
+
+ class UnixMemoryMapper : IMemoryMapper
+ {
+ const int PROT_READ = 0x1;
+ const int PROT_WRITE = 0x2;
+ const int PROT_EXEC = 0x4;
+
+ const int MAP_PRIVATE = 0x2;
+ int MAP_ANONYMOUS
+ {
+ get
+ {
+ switch (OperatingSystem)
+ {
+ case OperatingSystemType.Darwin:
+ return 0x1000;
+ case OperatingSystemType.Linux:
+ return 0x20;
+ default:
+ throw new NotImplementedException($"mmap is not supported on {OperatingSystemName}");
+ }
+ }
+ }
+
+ [DllImport("libc")]
+ static extern IntPtr mmap(IntPtr addr, IntPtr len, int prot, int flags, int fd, IntPtr offset);
+
+ [DllImport("libc")]
+ static extern int mprotect(IntPtr addr, IntPtr len, int prot);
+
+ public IntPtr MapWriteable(int numBytes)
+ {
+ // MAP_PRIVATE must be set on linux, even though MAP_ANON implies it.
+ // It doesn't hurt on darwin, so just do it.
+ return mmap(IntPtr.Zero, new IntPtr(numBytes), PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, IntPtr.Zero);
+ }
+
+ public void SetReadExec(IntPtr mappedMemory, int numBytes)
+ {
+ mprotect(mappedMemory, new IntPtr(numBytes), PROT_READ | PROT_EXEC);
+ }
+ }
+
+ ///
+ /// Initializes the data about platforms.
+ ///
+ /// This must be the last step when initializing the runtime:
+ /// GetManagedString needs to have the cached values for types.
+ /// But it must run before initializing anything outside the runtime
+ /// because those rely on the platform data.
+ ///
+ public static void InitializePlatformData()
+ {
+ IntPtr op;
+ IntPtr fn;
+ IntPtr platformModule = Runtime.PyImport_ImportModule("platform");
+ IntPtr emptyTuple = Runtime.PyTuple_New(0);
+
+ fn = Runtime.PyObject_GetAttrString(platformModule, "system");
+ op = Runtime.PyObject_Call(fn, emptyTuple, IntPtr.Zero);
+ PythonException.ThrowIfIsNull(op);
+ OperatingSystemName = Runtime.GetManagedString(op);
+ Runtime.XDecref(op);
+ Runtime.XDecref(fn);
+
+ fn = Runtime.PyObject_GetAttrString(platformModule, "machine");
+ op = Runtime.PyObject_Call(fn, emptyTuple, IntPtr.Zero);
+ MachineName = Runtime.GetManagedString(op);
+ Runtime.XDecref(op);
+ Runtime.XDecref(fn);
+
+ Runtime.XDecref(emptyTuple);
+ Runtime.XDecref(platformModule);
+
+ // Now convert the strings into enum values so we can do switch
+ // statements rather than constant parsing.
+ OperatingSystemType OSType;
+ if (!OperatingSystemTypeMapping.TryGetValue(OperatingSystemName, out OSType))
+ {
+ OSType = OperatingSystemType.Other;
+ }
+ OperatingSystem = OSType;
+
+ MachineType MType;
+ if (!MachineTypeMapping.TryGetValue(MachineName.ToLower(), out MType))
+ {
+ MType = MachineType.Other;
+ }
+ Machine = MType;
+ }
+
+ internal static IMemoryMapper CreateMemoryMapper()
+ {
+ switch (OperatingSystem)
+ {
+ case OperatingSystemType.Darwin:
+ case OperatingSystemType.Linux:
+ return new UnixMemoryMapper();
+ case OperatingSystemType.Windows:
+ return new WindowsMemoryMapper();
+ default:
+ throw new NotImplementedException($"No support for {OperatingSystemName}");
+ }
+ }
+
+ ///
+ /// Initializes the native code page.
+ ///
+ /// Safe to call if we already initialized (this function is idempotent).
+ ///
+ ///
+ internal static void InitializeNativeCodePage()
+ {
+ // Do nothing if we already initialized.
+ if (NativeCodePage != IntPtr.Zero)
+ {
+ return;
+ }
+
+ // Allocate the page, write the native code into it, then set it
+ // to be executable.
+ IMemoryMapper mapper = CreateMemoryMapper();
+ int codeLength = NativeCode.Active.Code.Length;
+ NativeCodePage = mapper.MapWriteable(codeLength);
+ Marshal.Copy(NativeCode.Active.Code, 0, NativeCodePage, codeLength);
+ mapper.SetReadExec(NativeCodePage, codeLength);
+ }
+ }
+}
diff --git a/src/runtime/runtime.cs b/src/runtime/runtime.cs
index 18dc0c113..0123bc499 100644
--- a/src/runtime/runtime.cs
+++ b/src/runtime/runtime.cs
@@ -107,59 +107,13 @@ public class Runtime
internal static object IsFinalizingLock = new object();
internal static bool IsFinalizing;
+ private static bool _isInitialized = false;
+
internal static bool Is32Bit = IntPtr.Size == 4;
// .NET core: System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
internal static bool IsWindows = Environment.OSVersion.Platform == PlatformID.Win32NT;
- static readonly Dictionary OperatingSystemTypeMapping = new Dictionary()
- {
- { "Windows", OperatingSystemType.Windows },
- { "Darwin", OperatingSystemType.Darwin },
- { "Linux", OperatingSystemType.Linux },
- };
-
- ///
- /// Gets the operating system as reported by python's platform.system().
- ///
- public static OperatingSystemType OperatingSystem { get; private set; }
-
- ///
- /// Gets the operating system as reported by python's platform.system().
- ///
- public static string OperatingSystemName { get; private set; }
-
-
- ///
- /// Map lower-case version of the python machine name to the processor
- /// type. There are aliases, e.g. x86_64 and amd64 are two names for
- /// the same thing. Make sure to lower-case the search string, because
- /// capitalization can differ.
- ///
- static readonly Dictionary MachineTypeMapping = new Dictionary()
- {
- ["i386"] = MachineType.i386,
- ["i686"] = MachineType.i386,
- ["x86"] = MachineType.i386,
- ["x86_64"] = MachineType.x86_64,
- ["amd64"] = MachineType.x86_64,
- ["x64"] = MachineType.x86_64,
- ["em64t"] = MachineType.x86_64,
- ["armv7l"] = MachineType.armv7l,
- ["armv8"] = MachineType.armv8,
- ["aarch64"] = MachineType.aarch64,
- };
-
- ///
- /// Gets the machine architecture as reported by python's platform.machine().
- ///
- public static MachineType Machine { get; private set; }/* set in Initialize using python's platform.machine */
-
- ///
- /// Gets the machine architecture as reported by python's platform.machine().
- ///
- public static string MachineName { get; private set; }
-
internal static bool IsPython2 = pyversionnumber < 30;
internal static bool IsPython3 = pyversionnumber >= 30;
@@ -178,6 +132,12 @@ public class Runtime
///
internal static void Initialize(bool initSigs = false, bool softShutdown = false)
{
+ if (_isInitialized)
+ {
+ return;
+ }
+ _isInitialized = true;
+
if (Environment.GetEnvironmentVariable("PYTHONNET_SOFT_SHUTDOWN") == "1")
{
softShutdown = true;
@@ -329,10 +289,10 @@ internal static void Initialize(bool initSigs = false, bool softShutdown = false
// Initialize data about the platform we're running on. We need
// this for the type manager and potentially other details. Must
// happen after caching the python types, above.
- InitializePlatformData();
+ NativeCodePageHelper.InitializePlatformData();
IntPtr dllLocal = IntPtr.Zero;
- var loader = LibraryLoader.Get(OperatingSystem);
+ var loader = LibraryLoader.Get(NativeCodePageHelper.OperatingSystem);
if (_PythonDll != "__Internal")
{
@@ -362,60 +322,15 @@ internal static void Initialize(bool initSigs = false, bool softShutdown = false
AssemblyManager.UpdatePath();
}
- ///
- /// Initializes the data about platforms.
- ///
- /// This must be the last step when initializing the runtime:
- /// GetManagedString needs to have the cached values for types.
- /// But it must run before initializing anything outside the runtime
- /// because those rely on the platform data.
- ///
- private static void InitializePlatformData()
- {
- IntPtr op;
- IntPtr fn;
- IntPtr platformModule = PyImport_ImportModule("platform");
- IntPtr emptyTuple = PyTuple_New(0);
-
- fn = PyObject_GetAttrString(platformModule, "system");
- op = PyObject_Call(fn, emptyTuple, IntPtr.Zero);
- PythonException.ThrowIfIsNull(op);
- OperatingSystemName = GetManagedString(op);
- XDecref(op);
- XDecref(fn);
-
- fn = PyObject_GetAttrString(platformModule, "machine");
- op = PyObject_Call(fn, emptyTuple, IntPtr.Zero);
- MachineName = GetManagedString(op);
- XDecref(op);
- XDecref(fn);
- XDecref(emptyTuple);
- XDecref(platformModule);
-
- // Now convert the strings into enum values so we can do switch
- // statements rather than constant parsing.
- OperatingSystemType OSType;
- if (!OperatingSystemTypeMapping.TryGetValue(OperatingSystemName, out OSType))
- {
- OSType = OperatingSystemType.Other;
- }
- OperatingSystem = OSType;
-
- MachineType MType;
- if (!MachineTypeMapping.TryGetValue(MachineName.ToLower(), out MType))
- {
- MType = MachineType.Other;
- }
- Machine = MType;
- }
-
- internal static void Shutdown(bool soft)
+ internal static void Shutdown(bool soft = false)
{
- if (Py_IsInitialized() == 0)
+ if (Py_IsInitialized() == 0 || !_isInitialized)
{
return;
}
+ _isInitialized = false;
+
PyGILState_Ensure();
AssemblyManager.Shutdown();
@@ -531,7 +446,10 @@ private static void MoveClrInstancesOnwershipToPython()
// obj's tp_type will degenerate to a pure Python type after TypeManager.RemoveTypes(),
// thus just be safe to give it back to GC chain.
PyObject_GC_Track(obj.pyHandle);
- obj.gcHandle.Free();
+ if (obj.gcHandle.IsAllocated)
+ {
+ obj.gcHandle.Free();
+ }
obj.gcHandle = new GCHandle();
}
ManagedType.ClearTrackedObjects();
@@ -747,7 +665,11 @@ internal static unsafe void XDecref(IntPtr op)
internal static unsafe long Refcount(IntPtr op)
{
+#if PYTHON_WITH_PYDEBUG
+ var p = (void*)(op + TypeOffset.ob_refcnt);
+#else
var p = (void*)op;
+#endif
if ((void*)0 == p)
{
return 0;
@@ -1132,6 +1054,10 @@ internal static long PyObject_Size(IntPtr pointer)
[DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)]
internal static extern IntPtr PyObject_Dir(IntPtr pointer);
+#if PYTHON_WITH_PYDEBUG
+ [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern void _Py_NewReference(IntPtr ob);
+#endif
//====================================================================
// Python number API
@@ -1926,7 +1852,11 @@ internal static bool PyIter_Check(IntPtr pointer)
internal static extern string PyModule_GetFilename(IntPtr module);
#if PYTHON3
+#if PYTHON_WITH_PYDEBUG
+ [DllImport(_PythonDll, EntryPoint = "PyModule_Create2TraceRefs", CallingConvention = CallingConvention.Cdecl)]
+#else
[DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)]
+#endif
internal static extern IntPtr PyModule_Create2(IntPtr module, int apiver);
#endif
@@ -2188,7 +2118,7 @@ internal static void Py_CLEAR(ref IntPtr ob)
internal static void SetNoSiteFlag()
{
- var loader = LibraryLoader.Get(OperatingSystem);
+ var loader = LibraryLoader.Get(NativeCodePageHelper.OperatingSystem);
IntPtr dllLocal;
if (_PythonDll != "__Internal")
diff --git a/src/runtime/slots/mp_length.cs b/src/runtime/slots/mp_length.cs
index b0a2e8d79..42448a2e9 100644
--- a/src/runtime/slots/mp_length.cs
+++ b/src/runtime/slots/mp_length.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections;
using System.Collections.Generic;
+using System.Diagnostics;
using System.Linq;
using System.Reflection;
@@ -8,11 +9,41 @@ namespace Python.Runtime.Slots
{
internal static class mp_length_slot
{
+ private static MethodInfo _lengthMethod;
+ public static MethodInfo Method
+ {
+ get
+ {
+ if (_lengthMethod != null)
+ {
+ return _lengthMethod;
+ }
+ _lengthMethod = typeof(mp_length_slot).GetMethod(
+ nameof(mp_length_slot.mp_length),
+ BindingFlags.Static | BindingFlags.NonPublic);
+ Debug.Assert(_lengthMethod != null);
+ return _lengthMethod;
+ }
+ }
+
+ public static bool CanAssgin(Type clrType)
+ {
+ if (typeof(ICollection).IsAssignableFrom(clrType))
+ {
+ return true;
+ }
+ if (clrType.GetInterfaces().Any(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof(ICollection<>)))
+ {
+ return true;
+ }
+ return false;
+ }
+
///
/// Implements __len__ for classes that implement ICollection
/// (this includes any IList implementer or Array subclass)
///
- public static int mp_length(IntPtr ob)
+ private static int mp_length(IntPtr ob)
{
var co = ManagedType.GetManagedObject(ob) as CLRObject;
if (co == null)
diff --git a/src/runtime/typemanager.cs b/src/runtime/typemanager.cs
index bd549550b..888131c44 100644
--- a/src/runtime/typemanager.cs
+++ b/src/runtime/typemanager.cs
@@ -4,7 +4,6 @@
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
-using Python.Runtime.Platform;
using System.Diagnostics;
using Python.Runtime.Slots;
@@ -24,8 +23,17 @@ internal class TypeManager
private static readonly Dictionary cache = new Dictionary();
private static readonly Dictionary _slotsHolders = new Dictionary();
+ // Slots which must be set
+ private static readonly string[] _requiredSlots = new string[]
+ {
+ "tp_traverse",
+ "tp_clear",
+ };
+
public static void Initialize()
{
+ Debug.Assert(cache.Count == 0, "Cache should be empty",
+ "Some errors may occurred on last shutdown");
IntPtr type = SlotHelper.CreateObjectType();
subtype_traverse = Marshal.ReadIntPtr(type, TypeOffset.tp_traverse);
subtype_clear = Marshal.ReadIntPtr(type, TypeOffset.tp_clear);
@@ -192,11 +200,10 @@ internal static IntPtr CreateType(ManagedType impl, Type clrType)
SlotsHolder slotsHolder = CreateSolotsHolder(type);
InitializeSlots(type, impl.GetType(), slotsHolder);
- // add a __len__ slot for inheritors of ICollection and ICollection<>
- if (typeof(ICollection).IsAssignableFrom(clrType) || clrType.GetInterfaces().Any(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof(ICollection<>)))
+ if (Marshal.ReadIntPtr(type, TypeOffset.mp_length) == IntPtr.Zero
+ && mp_length_slot.CanAssgin(clrType))
{
- var method = typeof(mp_length_slot).GetMethod(nameof(mp_length_slot.mp_length));
- InitializeSlot(type, TypeOffset.mp_length, method, slotsHolder);
+ InitializeSlot(type, TypeOffset.mp_length, mp_length_slot.Method, slotsHolder);
}
if (base_ != IntPtr.Zero)
@@ -384,6 +391,9 @@ internal static IntPtr CreateMetaType(Type impl, out SlotsHolder slotsHolder)
slotsHolder = new SlotsHolder(type);
InitializeSlots(type, impl, slotsHolder);
+ Marshal.WriteIntPtr(type, TypeOffset.tp_traverse, Marshal.ReadIntPtr(Runtime.PyTypeType, TypeOffset.tp_traverse));
+ Marshal.WriteIntPtr(type, TypeOffset.tp_clear, Marshal.ReadIntPtr(Runtime.PyTypeType, TypeOffset.tp_clear));
+
int flags = TypeFlags.Default;
flags |= TypeFlags.Managed;
flags |= TypeFlags.HeapType;
@@ -396,7 +406,7 @@ internal static IntPtr CreateMetaType(Type impl, out SlotsHolder slotsHolder)
Debug.Assert(4 * IntPtr.Size == Marshal.SizeOf(typeof(PyMethodDef)));
IntPtr mdefStart = mdef;
ThunkInfo thunkInfo = Interop.GetThunk(typeof(MetaType).GetMethod("__instancecheck__"), "BinaryFunc");
- slotsHolder.KeeapAlive(thunkInfo.Target);
+ slotsHolder.KeeapAlive(thunkInfo);
{
IntPtr mdefAddr = mdef;
@@ -418,7 +428,7 @@ internal static IntPtr CreateMetaType(Type impl, out SlotsHolder slotsHolder)
);
thunkInfo = Interop.GetThunk(typeof(MetaType).GetMethod("__subclasscheck__"), "BinaryFunc");
- slotsHolder.KeeapAlive(thunkInfo.Target);
+ slotsHolder.KeeapAlive(thunkInfo);
{
IntPtr mdefAddr = mdef;
slotsHolder.AddDealloctor(() =>
@@ -514,6 +524,10 @@ internal static IntPtr BasicSubType(string name, IntPtr base_, Type impl)
internal static IntPtr AllocateTypeObject(string name)
{
IntPtr type = Runtime.PyType_GenericAlloc(Runtime.PyTypeType, 0);
+ // Clr type would not use __slots__,
+ // and the PyMemberDef after PyHeapTypeObject will have other uses(e.g. type handle),
+ // thus set the ob_size to 0 for avoiding slots iterations.
+ Marshal.WriteIntPtr(type, TypeOffset.ob_size, IntPtr.Zero);
// Cheat a little: we'll set tp_name to the internal char * of
// the Python version of the type name - otherwise we'd have to
@@ -553,226 +567,6 @@ internal static IntPtr AllocateTypeObject(string name)
return type;
}
-
- #region Native Code Page
- ///
- /// Initialized by InitializeNativeCodePage.
- ///
- /// This points to a page of memory allocated using mmap or VirtualAlloc
- /// (depending on the system), and marked read and execute (not write).
- /// Very much on purpose, the page is *not* released on a shutdown and
- /// is instead leaked. See the TestDomainReload test case.
- ///
- /// The contents of the page are two native functions: one that returns 0,
- /// one that returns 1.
- ///
- /// If python didn't keep its gc list through a Py_Finalize we could remove
- /// this entire section.
- ///
- internal static IntPtr NativeCodePage = IntPtr.Zero;
-
- ///
- /// Structure to describe native code.
- ///
- /// Use NativeCode.Active to get the native code for the current platform.
- ///
- /// Generate the code by creating the following C code:
- ///
- /// int Return0() { return 0; }
- /// int Return1() { return 1; }
- ///
- /// Then compiling on the target platform, e.g. with gcc or clang:
- /// cc -c -fomit-frame-pointer -O2 foo.c
- /// And then analyzing the resulting functions with a hex editor, e.g.:
- /// objdump -disassemble foo.o
- ///
- internal class NativeCode
- {
- ///
- /// The code, as a string of bytes.
- ///
- public byte[] Code { get; private set; }
-
- ///
- /// Where does the "return 0" function start?
- ///
- public int Return0 { get; private set; }
-
- ///
- /// Where does the "return 1" function start?
- ///
- public int Return1 { get; private set; }
-
- public static NativeCode Active
- {
- get
- {
- switch (Runtime.Machine)
- {
- case MachineType.i386:
- return I386;
- case MachineType.x86_64:
- return X86_64;
- default:
- return null;
- }
- }
- }
-
- ///
- /// Code for x86_64. See the class comment for how it was generated.
- ///
- public static readonly NativeCode X86_64 = new NativeCode()
- {
- Return0 = 0x10,
- Return1 = 0,
- Code = new byte[]
- {
- // First Return1:
- 0xb8, 0x01, 0x00, 0x00, 0x00, // movl $1, %eax
- 0xc3, // ret
-
- // Now some padding so that Return0 can be 16-byte-aligned.
- // I put Return1 first so there's not as much padding to type in.
- 0x66, 0x2e, 0x0f, 0x1f, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, // nop
-
- // Now Return0.
- 0x31, 0xc0, // xorl %eax, %eax
- 0xc3, // ret
- }
- };
-
- ///
- /// Code for X86.
- ///
- /// It's bitwise identical to X86_64, so we just point to it.
- ///
- ///
- public static readonly NativeCode I386 = X86_64;
- }
-
- ///
- /// Platform-dependent mmap and mprotect.
- ///
- internal interface IMemoryMapper
- {
- ///
- /// Map at least numBytes of memory. Mark the page read-write (but not exec).
- ///
- IntPtr MapWriteable(int numBytes);
-
- ///
- /// Sets the mapped memory to be read-exec (but not write).
- ///
- void SetReadExec(IntPtr mappedMemory, int numBytes);
- }
-
- class WindowsMemoryMapper : IMemoryMapper
- {
- const UInt32 MEM_COMMIT = 0x1000;
- const UInt32 MEM_RESERVE = 0x2000;
- const UInt32 PAGE_READWRITE = 0x04;
- const UInt32 PAGE_EXECUTE_READ = 0x20;
-
- [DllImport("kernel32.dll")]
- static extern IntPtr VirtualAlloc(IntPtr lpAddress, IntPtr dwSize, UInt32 flAllocationType, UInt32 flProtect);
-
- [DllImport("kernel32.dll")]
- static extern bool VirtualProtect(IntPtr lpAddress, IntPtr dwSize, UInt32 flNewProtect, out UInt32 lpflOldProtect);
-
- public IntPtr MapWriteable(int numBytes)
- {
- return VirtualAlloc(IntPtr.Zero, new IntPtr(numBytes),
- MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
- }
-
- public void SetReadExec(IntPtr mappedMemory, int numBytes)
- {
- UInt32 _;
- VirtualProtect(mappedMemory, new IntPtr(numBytes), PAGE_EXECUTE_READ, out _);
- }
- }
-
- class UnixMemoryMapper : IMemoryMapper
- {
- const int PROT_READ = 0x1;
- const int PROT_WRITE = 0x2;
- const int PROT_EXEC = 0x4;
-
- const int MAP_PRIVATE = 0x2;
- int MAP_ANONYMOUS
- {
- get
- {
- switch (Runtime.OperatingSystem)
- {
- case OperatingSystemType.Darwin:
- return 0x1000;
- case OperatingSystemType.Linux:
- return 0x20;
- default:
- throw new NotImplementedException($"mmap is not supported on {Runtime.OperatingSystemName}");
- }
- }
- }
-
- [DllImport("libc")]
- static extern IntPtr mmap(IntPtr addr, IntPtr len, int prot, int flags, int fd, IntPtr offset);
-
- [DllImport("libc")]
- static extern int mprotect(IntPtr addr, IntPtr len, int prot);
-
- public IntPtr MapWriteable(int numBytes)
- {
- // MAP_PRIVATE must be set on linux, even though MAP_ANON implies it.
- // It doesn't hurt on darwin, so just do it.
- return mmap(IntPtr.Zero, new IntPtr(numBytes), PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, IntPtr.Zero);
- }
-
- public void SetReadExec(IntPtr mappedMemory, int numBytes)
- {
- mprotect(mappedMemory, new IntPtr(numBytes), PROT_READ | PROT_EXEC);
- }
- }
-
- internal static IMemoryMapper CreateMemoryMapper()
- {
- switch (Runtime.OperatingSystem)
- {
- case OperatingSystemType.Darwin:
- case OperatingSystemType.Linux:
- return new UnixMemoryMapper();
- case OperatingSystemType.Windows:
- return new WindowsMemoryMapper();
- default:
- throw new NotImplementedException($"No support for {Runtime.OperatingSystemName}");
- }
- }
-
- ///
- /// Initializes the native code page.
- ///
- /// Safe to call if we already initialized (this function is idempotent).
- ///
- ///
- internal static void InitializeNativeCodePage()
- {
- // Do nothing if we already initialized.
- if (NativeCodePage != IntPtr.Zero)
- {
- return;
- }
-
- // Allocate the page, write the native code into it, then set it
- // to be executable.
- IMemoryMapper mapper = CreateMemoryMapper();
- int codeLength = NativeCode.Active.Code.Length;
- NativeCodePage = mapper.MapWriteable(codeLength);
- Marshal.Copy(NativeCode.Active.Code, 0, NativeCodePage, codeLength);
- mapper.SetReadExec(NativeCodePage, codeLength);
- }
- #endregion
-
///
/// Given a newly allocated Python type object and a managed Type that
/// provides the implementation for the type, connect the type slots of
@@ -791,12 +585,7 @@ internal static void InitializeSlots(IntPtr type, Type impl, SlotsHolder slotsHo
foreach (MethodInfo method in methods)
{
string name = method.Name;
- if (!(name.StartsWith("tp_") ||
- name.StartsWith("nb_") ||
- name.StartsWith("sq_") ||
- name.StartsWith("mp_") ||
- name.StartsWith("bf_")
- ))
+ if (!name.StartsWith("tp_") && !SlotTypes.IsSlotName(name))
{
continue;
}
@@ -814,58 +603,17 @@ internal static void InitializeSlots(IntPtr type, Type impl, SlotsHolder slotsHo
impl = impl.BaseType;
}
- var native = NativeCode.Active;
-
- // The garbage collection related slots always have to return 1 or 0
- // since .NET objects don't take part in Python's gc:
- // tp_traverse (returns 0)
- // tp_clear (returns 0)
- // tp_is_gc (returns 1)
- // These have to be defined, though, so by default we fill these with
- // static C# functions from this class.
- if (native != null)
- {
- // If we want to support domain reload, the C# implementation
- // cannot be used as the assembly may get released before
- // CPython calls these functions. Instead, for amd64 and x86 we
- // load them into a separate code page that is leaked
- // intentionally.
- InitializeNativeCodePage();
- IntPtr ret1 = NativeCodePage + native.Return1;
- IntPtr ret0 = NativeCodePage + native.Return0;
-
- InitializeSlot(type, ret0, "tp_traverse", false);
- InitializeSlot(type, ret0, "tp_clear", false);
- }
- else
+ foreach (string slot in _requiredSlots)
{
- if (!IsSlotSet(type, "tp_traverse"))
+ if (IsSlotSet(type, slot))
{
- var thunkRet0 = Interop.GetThunk(((Func)Return0).Method);
- var offset = GetSlotOffset("tp_traverse");
- Marshal.WriteIntPtr(type, offset, thunkRet0.Address);
- if (slotsHolder != null)
- {
- slotsHolder.Set(offset, thunkRet0);
- }
- }
- if (!IsSlotSet(type, "tp_clear"))
- {
- var thunkRet0 = Interop.GetThunk(((Func)Return0).Method);
- var offset = GetSlotOffset("tp_clear");
- Marshal.WriteIntPtr(type, offset, thunkRet0.Address);
- if (slotsHolder != null)
- {
- slotsHolder.Set(offset, thunkRet0);
- }
+ continue;
}
+ var offset = TypeOffset.GetSlotOffset(slot);
+ Marshal.WriteIntPtr(type, offset, SlotsHolder.GetDefaultSlot(offset));
}
}
- static int Return1(IntPtr _) => 1;
-
- static int Return0(IntPtr _) => 0;
-
///
/// Helper for InitializeSlots.
///
@@ -879,7 +627,7 @@ internal static void InitializeSlots(IntPtr type, Type impl, SlotsHolder slotsHo
/// Can override the slot when it existed
static void InitializeSlot(IntPtr type, IntPtr slot, string name, bool canOverride = true)
{
- var offset = GetSlotOffset(name);
+ var offset = TypeOffset.GetSlotOffset(name);
if (!canOverride && Marshal.ReadIntPtr(type, offset) != IntPtr.Zero)
{
return;
@@ -914,17 +662,9 @@ static void InitializeSlot(IntPtr type, int slotOffset, MethodInfo method, Slots
}
}
- static int GetSlotOffset(string name)
- {
- Type typeOffset = typeof(TypeOffset);
- FieldInfo fi = typeOffset.GetField(name);
- var offset = (int)fi.GetValue(typeOffset);
- return offset;
- }
-
static bool IsSlotSet(IntPtr type, string name)
{
- int offset = GetSlotOffset(name);
+ int offset = TypeOffset.GetSlotOffset(name);
return Marshal.ReadIntPtr(type, offset) != IntPtr.Zero;
}
@@ -989,7 +729,7 @@ class SlotsHolder
private readonly IntPtr _type;
private Dictionary _slots = new Dictionary();
- private List _keepalive = new List();
+ private List _keepalive = new List();
private Dictionary _customResetors = new Dictionary();
private List _deallocators = new List();
private bool _alreadyReset = false;
@@ -1018,9 +758,9 @@ public void AddDealloctor(Action deallocate)
_deallocators.Add(deallocate);
}
- public void KeeapAlive(Delegate d)
+ public void KeeapAlive(ThunkInfo thunk)
{
- _keepalive.Add(d);
+ _keepalive.Add(thunk);
}
public void ResetSlots()
@@ -1070,9 +810,29 @@ public void ResetSlots()
Runtime.XDecref(tp_bases);
tp_bases = Runtime.PyTuple_New(0);
Marshal.WriteIntPtr(_type, TypeOffset.tp_bases, tp_bases);
+ try
+ {
+ IntPtr handlePtr = Marshal.ReadIntPtr(_type, TypeOffset.magic());
+ if (handlePtr != IntPtr.Zero)
+ {
+ GCHandle handle = GCHandle.FromIntPtr(handlePtr);
+ if (handle.IsAllocated)
+ {
+ handle.Free();
+ }
+ Marshal.WriteIntPtr(_type, TypeOffset.magic(), IntPtr.Zero);
+ }
+
+ }
+ catch (Exception)
+ {
+
+ throw;
+ }
+
}
- private static IntPtr GetDefaultSlot(int offset)
+ public static IntPtr GetDefaultSlot(int offset)
{
if (offset == TypeOffset.tp_clear)
{
@@ -1148,4 +908,28 @@ public static IntPtr CreateObjectType()
return A;
}
}
+
+
+ static partial class SlotTypes
+ {
+ private static Dictionary _typeMap = new Dictionary();
+ private static Dictionary _nameMap = new Dictionary();
+
+ static SlotTypes()
+ {
+ foreach (var type in Types)
+ {
+ FieldInfo[] fields = type.GetFields();
+ foreach (var fi in fields)
+ {
+ _nameMap[fi.Name] = type;
+ }
+ }
+ }
+
+ public static bool IsSlotName(string name)
+ {
+ return _nameMap.ContainsKey(name);
+ }
+ }
}
diff --git a/tools/geninterop/geninterop.py b/tools/geninterop/geninterop.py
index 1f4751939..322d43ad9 100644
--- a/tools/geninterop/geninterop.py
+++ b/tools/geninterop/geninterop.py
@@ -21,6 +21,11 @@
import sysconfig
import subprocess
+if sys.version_info.major > 2:
+ from io import StringIO
+else:
+ from StringIO import StringIO
+
from pycparser import c_ast, c_parser
_log = logging.getLogger()
@@ -55,15 +60,18 @@ def __init__(self):
self.__struct_members_stack = []
self.__ptr_decl_depth = 0
self.__struct_members = {}
+ self.__decl_names = {}
def get_struct_members(self, name):
"""return a list of (name, type) of struct members"""
- if name in self.__typedefs:
- node = self.__get_leaf_node(self.__typedefs[name])
- name = node.name
- if name not in self.__struct_members:
- raise Exception("Unknown struct '%s'" % name)
- return self.__struct_members[name]
+ defs = self.__typedefs.get(name)
+ if defs is None:
+ return None
+ node = self.__get_leaf_node(defs)
+ name = node.name
+ if name is None:
+ name = defs.declname
+ return self.__struct_members.get(name)
def visit(self, node):
if isinstance(node, c_ast.FileAST):
@@ -92,6 +100,7 @@ def visit_typedef(self, typedef):
self.visit(typedef.type)
def visit_typedecl(self, typedecl):
+ self.__decl_names[typedecl.type] = typedecl.declname
self.visit(typedecl.type)
def visit_struct(self, struct):
@@ -160,7 +169,22 @@ def __get_leaf_node(self, node):
return node
def __get_struct_name(self, node):
- return node.name or "_struct_%d" % id(node)
+ return node.name or self.__decl_names.get(node) or "_struct_%d" % id(node)
+
+
+class Writer(object):
+
+ def __init__(self):
+ self._stream = StringIO()
+
+ def append(self, indent=0, code=""):
+ self._stream.write("%s%s\n" % (indent * " ", code))
+
+ def extend(self, s):
+ self._stream.write(s)
+
+ def to_string(self):
+ return self._stream.getvalue()
def preprocess_python_headers():
@@ -186,6 +210,7 @@ def preprocess_python_headers():
defines.extend([
"-D", "__inline=inline",
"-D", "__ptr32=",
+ "-D", "__ptr64=",
"-D", "__declspec(x)=",
])
@@ -209,9 +234,8 @@ def preprocess_python_headers():
return "\n".join(lines)
-def gen_interop_code(members):
- """Generate the TypeOffset C# class"""
+def gen_interop_head(writer):
defines = [
"PYTHON{0}{1}".format(PY_MAJOR, PY_MINOR)
]
@@ -241,27 +265,26 @@ def gen_interop_code(members):
namespace Python.Runtime
{
- [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
- internal class TypeOffset
- {
- static TypeOffset()
- {
- Type type = typeof(TypeOffset);
- FieldInfo[] fi = type.GetFields();
- int size = IntPtr.Size;
- for (int i = 0; i < fi.Length; i++)
- {
- fi[i].SetValue(null, i * size);
- }
- }
-
- public static int magic()
- {
- return ob_size;
- }
+""" % (filename, defines_str)
+ writer.extend(class_definition)
+
+
+def gen_interop_tail(writer):
+ tail = """}
+#endif
+"""
+ writer.extend(tail)
+
+def gen_heap_type_members(parser, writer):
+ """Generate the TypeOffset C# class"""
+ members = parser.get_struct_members("PyHeapTypeObject")
+ class_definition = """
+ [StructLayout(LayoutKind.Sequential)]
+ internal static partial class TypeOffset
+ {
// Auto-generated from PyHeapTypeObject in Python.h
-""" % (filename, defines_str)
+"""
# All the members are sizeof(void*) so we don't need to do any
# extra work to determine the size based on the type.
@@ -273,11 +296,36 @@ def gen_interop_code(members):
/* here are optional user slots, followed by the members. */
public static int members = 0;
}
-}
-#endif
"""
- return class_definition
+ writer.extend(class_definition)
+
+
+def gen_structure_code(parser, writer, type_name, indent):
+ members = parser.get_struct_members(type_name)
+ if members is None:
+ return False
+ out = writer.append
+ out(indent, "[StructLayout(LayoutKind.Sequential)]")
+ out(indent, "internal struct %s" % type_name)
+ out(indent, "{")
+ for name, tpy in members:
+ out(indent + 1, "public IntPtr %s;" % name)
+ out(indent, "}")
+ out()
+ return True
+
+
+def gen_supported_slot_record(writer, types, indent):
+ out = writer.append
+ out(indent, "internal static partial class SlotTypes")
+ out(indent, "{")
+ out(indent + 1, "public static readonly Type[] Types = {")
+ for name in types:
+ out(indent + 2, "typeof(%s)," % name)
+ out(indent + 1, "};")
+ out(indent, "}")
+ out()
def main():
@@ -290,10 +338,29 @@ def main():
ast_parser = AstParser()
ast_parser.visit(ast)
+ writer = Writer()
# generate the C# code
- members = ast_parser.get_struct_members("PyHeapTypeObject")
- interop_cs = gen_interop_code(members)
+ gen_interop_head(writer)
+
+ gen_heap_type_members(ast_parser, writer)
+ slots_types = [
+ "PyNumberMethods",
+ "PySequenceMethods",
+ "PyMappingMethods",
+ "PyAsyncMethods",
+ "PyBufferProcs",
+ ]
+ supported_types = []
+ indent = 1
+ for type_name in slots_types:
+ if not gen_structure_code(ast_parser, writer, type_name, indent):
+ continue
+ supported_types.append(type_name)
+ gen_supported_slot_record(writer, supported_types, indent)
+
+ gen_interop_tail(writer)
+ interop_cs = writer.to_string()
if len(sys.argv) > 1:
with open(sys.argv[1], "w") as fh:
fh.write(interop_cs)
From 49130c490cf092137f5a9e67b0025e789fed290f Mon Sep 17 00:00:00 2001
From: amos402
Date: Thu, 23 Jan 2020 16:27:10 +0800
Subject: [PATCH 063/998] Remove unused code
---
src/runtime/typemanager.cs | 11 +----------
1 file changed, 1 insertion(+), 10 deletions(-)
diff --git a/src/runtime/typemanager.cs b/src/runtime/typemanager.cs
index 888131c44..8a493ae77 100644
--- a/src/runtime/typemanager.cs
+++ b/src/runtime/typemanager.cs
@@ -810,8 +810,7 @@ public void ResetSlots()
Runtime.XDecref(tp_bases);
tp_bases = Runtime.PyTuple_New(0);
Marshal.WriteIntPtr(_type, TypeOffset.tp_bases, tp_bases);
- try
- {
+
IntPtr handlePtr = Marshal.ReadIntPtr(_type, TypeOffset.magic());
if (handlePtr != IntPtr.Zero)
{
@@ -822,14 +821,6 @@ public void ResetSlots()
}
Marshal.WriteIntPtr(_type, TypeOffset.magic(), IntPtr.Zero);
}
-
- }
- catch (Exception)
- {
-
- throw;
- }
-
}
public static IntPtr GetDefaultSlot(int offset)
From 28143d5f348a0edd891a2d8fa5249f7aab98582f Mon Sep 17 00:00:00 2001
From: Victor Milovanov
Date: Wed, 11 Sep 2019 21:59:27 -0700
Subject: [PATCH 064/998] enable expanding set of marshaling conversions via
PyObjectConversions
added sample TupleCodec (only supporting ValueTuple)
---
src/embed_tests/Codecs.cs | 86 ++++++++
.../Python.EmbeddingTest.15.csproj | 3 +-
src/embed_tests/Python.EmbeddingTest.csproj | 3 +-
src/embed_tests/packages.config | 5 +-
src/runtime/Codecs/TupleCodecs.cs | 129 ++++++++++++
src/runtime/Python.Runtime.csproj | 6 +-
src/runtime/converter.cs | 24 ++-
src/runtime/converterextensions.cs | 185 ++++++++++++++++++
src/runtime/pythonengine.cs | 2 +
src/runtime/runtime.cs | 9 +
10 files changed, 444 insertions(+), 8 deletions(-)
create mode 100644 src/embed_tests/Codecs.cs
create mode 100644 src/runtime/Codecs/TupleCodecs.cs
create mode 100644 src/runtime/converterextensions.cs
diff --git a/src/embed_tests/Codecs.cs b/src/embed_tests/Codecs.cs
new file mode 100644
index 000000000..600215cf0
--- /dev/null
+++ b/src/embed_tests/Codecs.cs
@@ -0,0 +1,86 @@
+namespace Python.EmbeddingTest {
+ using System;
+ using System.Collections.Generic;
+ using System.Text;
+ using NUnit.Framework;
+ using Python.Runtime;
+ using Python.Runtime.Codecs;
+
+ public class Codecs {
+ [SetUp]
+ public void SetUp() {
+ PythonEngine.Initialize();
+ }
+
+ [TearDown]
+ public void Dispose() {
+ PythonEngine.Shutdown();
+ }
+
+ [Test]
+ public void ConversionsGeneric() {
+ ConversionsGeneric, ValueTuple>();
+ }
+
+ static void ConversionsGeneric() {
+ TupleCodec.Register();
+ var tuple = Activator.CreateInstance(typeof(T), 42, "42", new object());
+ T restored = default;
+ using (Py.GIL())
+ using (var scope = Py.CreateScope()) {
+ void Accept(T value) => restored = value;
+ var accept = new Action(Accept).ToPython();
+ scope.Set(nameof(tuple), tuple);
+ scope.Set(nameof(accept), accept);
+ scope.Exec($"{nameof(accept)}({nameof(tuple)})");
+ Assert.AreEqual(expected: tuple, actual: restored);
+ }
+ }
+
+ [Test]
+ public void ConversionsObject() {
+ ConversionsObject, ValueTuple>();
+ }
+ static void ConversionsObject() {
+ TupleCodec.Register();
+ var tuple = Activator.CreateInstance(typeof(T), 42, "42", new object());
+ T restored = default;
+ using (Py.GIL())
+ using (var scope = Py.CreateScope()) {
+ void Accept(object value) => restored = (T)value;
+ var accept = new Action
///
/// Invoke the callable object with the given arguments, passed as a
- /// PyObject[]. A PythonException is raised if the invokation fails.
+ /// PyObject[]. A PythonException is raised if the invocation fails.
///
public PyObject Invoke(params PyObject[] args)
{
+ if (args == null) throw new ArgumentNullException(nameof(args));
+ if (args.Contains(null)) throw new ArgumentNullException();
+
var t = new PyTuple(args);
IntPtr r = Runtime.PyObject_Call(obj, t.obj, IntPtr.Zero);
t.Dispose();
@@ -659,10 +705,12 @@ public PyObject Invoke(params PyObject[] args)
///
///
/// Invoke the callable object with the given arguments, passed as a
- /// Python tuple. A PythonException is raised if the invokation fails.
+ /// Python tuple. A PythonException is raised if the invocation fails.
///
public PyObject Invoke(PyTuple args)
{
+ if (args == null) throw new ArgumentNullException(nameof(args));
+
IntPtr r = Runtime.PyObject_Call(obj, args.obj, IntPtr.Zero);
if (r == IntPtr.Zero)
{
@@ -677,12 +725,15 @@ public PyObject Invoke(PyTuple args)
///
///
/// Invoke the callable object with the given positional and keyword
- /// arguments. A PythonException is raised if the invokation fails.
+ /// arguments. A PythonException is raised if the invocation fails.
///
public PyObject Invoke(PyObject[] args, PyDict kw)
{
+ if (args == null) throw new ArgumentNullException(nameof(args));
+ if (args.Contains(null)) throw new ArgumentNullException();
+
var t = new PyTuple(args);
- IntPtr r = Runtime.PyObject_Call(obj, t.obj, kw != null ? kw.obj : IntPtr.Zero);
+ IntPtr r = Runtime.PyObject_Call(obj, t.obj, kw?.obj ?? IntPtr.Zero);
t.Dispose();
if (r == IntPtr.Zero)
{
@@ -697,11 +748,13 @@ public PyObject Invoke(PyObject[] args, PyDict kw)
///
///
/// Invoke the callable object with the given positional and keyword
- /// arguments. A PythonException is raised if the invokation fails.
+ /// arguments. A PythonException is raised if the invocation fails.
///
public PyObject Invoke(PyTuple args, PyDict kw)
{
- IntPtr r = Runtime.PyObject_Call(obj, args.obj, kw != null ? kw.obj : IntPtr.Zero);
+ if (args == null) throw new ArgumentNullException(nameof(args));
+
+ IntPtr r = Runtime.PyObject_Call(obj, args.obj, kw?.obj ?? IntPtr.Zero);
if (r == IntPtr.Zero)
{
throw new PythonException();
@@ -715,10 +768,14 @@ public PyObject Invoke(PyTuple args, PyDict kw)
///
///
/// Invoke the named method of the object with the given arguments.
- /// A PythonException is raised if the invokation is unsuccessful.
+ /// A PythonException is raised if the invocation is unsuccessful.
///
public PyObject InvokeMethod(string name, params PyObject[] args)
{
+ if (name == null) throw new ArgumentNullException(nameof(name));
+ if (args == null) throw new ArgumentNullException(nameof(args));
+ if (args.Contains(null)) throw new ArgumentNullException();
+
PyObject method = GetAttr(name);
PyObject result = method.Invoke(args);
method.Dispose();
@@ -731,10 +788,51 @@ public PyObject InvokeMethod(string name, params PyObject[] args)
///
///
/// Invoke the named method of the object with the given arguments.
- /// A PythonException is raised if the invokation is unsuccessful.
+ /// A PythonException is raised if the invocation is unsuccessful.
///
public PyObject InvokeMethod(string name, PyTuple args)
{
+ if (name == null) throw new ArgumentNullException(nameof(name));
+ if (args == null) throw new ArgumentNullException(nameof(args));
+
+ PyObject method = GetAttr(name);
+ PyObject result = method.Invoke(args);
+ method.Dispose();
+ return result;
+ }
+
+ ///
+ /// InvokeMethod Method
+ ///
+ ///
+ /// Invoke the named method of the object with the given arguments.
+ /// A PythonException is raised if the invocation is unsuccessful.
+ ///
+ public PyObject InvokeMethod(PyObject name, params PyObject[] args)
+ {
+ if (name == null) throw new ArgumentNullException(nameof(name));
+ if (args == null) throw new ArgumentNullException(nameof(args));
+ if (args.Contains(null)) throw new ArgumentNullException();
+
+ PyObject method = GetAttr(name);
+ PyObject result = method.Invoke(args);
+ method.Dispose();
+ return result;
+ }
+
+
+ ///
+ /// InvokeMethod Method
+ ///
+ ///
+ /// Invoke the named method of the object with the given arguments.
+ /// A PythonException is raised if the invocation is unsuccessful.
+ ///
+ public PyObject InvokeMethod(PyObject name, PyTuple args)
+ {
+ if (name == null) throw new ArgumentNullException(nameof(name));
+ if (args == null) throw new ArgumentNullException(nameof(args));
+
PyObject method = GetAttr(name);
PyObject result = method.Invoke(args);
method.Dispose();
@@ -748,10 +846,14 @@ public PyObject InvokeMethod(string name, PyTuple args)
///
/// Invoke the named method of the object with the given arguments
/// and keyword arguments. Keyword args are passed as a PyDict object.
- /// A PythonException is raised if the invokation is unsuccessful.
+ /// A PythonException is raised if the invocation is unsuccessful.
///
public PyObject InvokeMethod(string name, PyObject[] args, PyDict kw)
{
+ if (name == null) throw new ArgumentNullException(nameof(name));
+ if (args == null) throw new ArgumentNullException(nameof(args));
+ if (args.Contains(null)) throw new ArgumentNullException();
+
PyObject method = GetAttr(name);
PyObject result = method.Invoke(args, kw);
method.Dispose();
@@ -765,10 +867,13 @@ public PyObject InvokeMethod(string name, PyObject[] args, PyDict kw)
///
/// Invoke the named method of the object with the given arguments
/// and keyword arguments. Keyword args are passed as a PyDict object.
- /// A PythonException is raised if the invokation is unsuccessful.
+ /// A PythonException is raised if the invocation is unsuccessful.
///
public PyObject InvokeMethod(string name, PyTuple args, PyDict kw)
{
+ if (name == null) throw new ArgumentNullException(nameof(name));
+ if (args == null) throw new ArgumentNullException(nameof(args));
+
PyObject method = GetAttr(name);
PyObject result = method.Invoke(args, kw);
method.Dispose();
@@ -785,6 +890,8 @@ public PyObject InvokeMethod(string name, PyTuple args, PyDict kw)
///
public bool IsInstance(PyObject typeOrClass)
{
+ if (typeOrClass == null) throw new ArgumentNullException(nameof(typeOrClass));
+
int r = Runtime.PyObject_IsInstance(obj, typeOrClass.obj);
if (r < 0)
{
@@ -804,6 +911,8 @@ public bool IsInstance(PyObject typeOrClass)
///
public bool IsSubclass(PyObject typeOrClass)
{
+ if (typeOrClass == null) throw new ArgumentNullException(nameof(typeOrClass));
+
int r = Runtime.PyObject_IsSubclass(obj, typeOrClass.obj);
if (r < 0)
{
From 0b013787a3f3ba6c15ea9cbd63c8f4bea97a88b3 Mon Sep 17 00:00:00 2001
From: amos402
Date: Tue, 4 Feb 2020 11:14:34 +0800
Subject: [PATCH 074/998] All base type corrected, remove unnecessary slot set
---
src/runtime/typemanager.cs | 13 -------------
1 file changed, 13 deletions(-)
diff --git a/src/runtime/typemanager.cs b/src/runtime/typemanager.cs
index 0910c5128..3d6e66a03 100644
--- a/src/runtime/typemanager.cs
+++ b/src/runtime/typemanager.cs
@@ -391,9 +391,6 @@ internal static IntPtr CreateMetaType(Type impl, out SlotsHolder slotsHolder)
slotsHolder = new SlotsHolder(type);
InitializeSlots(type, impl, slotsHolder);
- Marshal.WriteIntPtr(type, TypeOffset.tp_traverse, Marshal.ReadIntPtr(Runtime.PyTypeType, TypeOffset.tp_traverse));
- Marshal.WriteIntPtr(type, TypeOffset.tp_clear, Marshal.ReadIntPtr(Runtime.PyTypeType, TypeOffset.tp_clear));
-
int flags = TypeFlags.Default;
flags |= TypeFlags.Managed;
flags |= TypeFlags.HeapType;
@@ -801,16 +798,6 @@ public void ResetSlots()
_deallocators.Clear();
// Custom reset
- IntPtr tp_base = Marshal.ReadIntPtr(_type, TypeOffset.tp_base);
- Runtime.XDecref(tp_base);
- Runtime.XIncref(Runtime.PyBaseObjectType);
- Marshal.WriteIntPtr(_type, TypeOffset.tp_base, Runtime.PyBaseObjectType);
-
- IntPtr tp_bases = Marshal.ReadIntPtr(_type, TypeOffset.tp_bases);
- Runtime.XDecref(tp_bases);
- tp_bases = Runtime.PyTuple_New(0);
- Marshal.WriteIntPtr(_type, TypeOffset.tp_bases, tp_bases);
-
IntPtr handlePtr = Marshal.ReadIntPtr(_type, TypeOffset.magic());
if (handlePtr != IntPtr.Zero)
{
From bf3d9f847594e2a5558e83e954614e9a9c0f5982 Mon Sep 17 00:00:00 2001
From: amos402
Date: Wed, 5 Feb 2020 18:54:20 +0800
Subject: [PATCH 075/998] Add basic `reload` shutdown mode
---
src/embed_tests/TestDomainReload.cs | 2 +-
src/runtime/classbase.cs | 1 +
src/runtime/classmanager.cs | 17 ++-
src/runtime/classobject.cs | 1 +
src/runtime/clrobject.cs | 1 +
src/runtime/constructorbinder.cs | 1 +
src/runtime/constructorbinding.cs | 4 +
src/runtime/delegateobject.cs | 1 +
src/runtime/eventbinding.cs | 7 ++
src/runtime/eventobject.cs | 1 +
src/runtime/extensiontype.cs | 15 +++
src/runtime/indexer.cs | 1 +
src/runtime/interop.cs | 81 ++++++++-------
src/runtime/managedtype.cs | 20 +++-
src/runtime/metatype.cs | 31 ++++++
src/runtime/methodbinder.cs | 1 +
src/runtime/methodbinding.cs | 1 +
src/runtime/methodobject.cs | 2 +
src/runtime/modulefunctionobject.cs | 1 +
src/runtime/moduleobject.cs | 19 ++++
src/runtime/nativecall.cs | 134 ++----------------------
src/runtime/propertyobject.cs | 1 +
src/runtime/pythonengine.cs | 15 +--
src/runtime/runtime.cs | 55 ++++++++--
src/runtime/runtime_state.cs | 79 +++++++++++++-
src/runtime/typemanager.cs | 156 +++++++++++++++-------------
26 files changed, 387 insertions(+), 261 deletions(-)
diff --git a/src/embed_tests/TestDomainReload.cs b/src/embed_tests/TestDomainReload.cs
index 5bfde11c1..d987ad30b 100644
--- a/src/embed_tests/TestDomainReload.cs
+++ b/src/embed_tests/TestDomainReload.cs
@@ -85,7 +85,7 @@ public static void RunPython() {
AppDomain.CurrentDomain.DomainUnload += OnDomainUnload;
string name = AppDomain.CurrentDomain.FriendlyName;
Console.WriteLine(string.Format(""[{0} in .NET] In PythonRunner.RunPython"", name));
- PythonEngine.Initialize(softShutdown: true);
+ PythonEngine.Initialize(mode: ShutdownMode.Reload);
using (Py.GIL()) {
try {
var pyScript = string.Format(""import clr\n""
diff --git a/src/runtime/classbase.cs b/src/runtime/classbase.cs
index e98bed638..3f10849fd 100644
--- a/src/runtime/classbase.cs
+++ b/src/runtime/classbase.cs
@@ -13,6 +13,7 @@ namespace Python.Runtime
/// concrete subclasses provide slot implementations appropriate for
/// each variety of reflected type.
///
+ [Serializable]
internal class ClassBase : ManagedType
{
internal Indexer indexer;
diff --git a/src/runtime/classmanager.cs b/src/runtime/classmanager.cs
index aa6fd6e51..3b9922a3d 100644
--- a/src/runtime/classmanager.cs
+++ b/src/runtime/classmanager.cs
@@ -19,7 +19,7 @@ namespace Python.Runtime
internal class ClassManager
{
private static Dictionary cache;
- private static Type dtype;
+ private static readonly Type dtype;
private ClassManager()
{
@@ -81,6 +81,17 @@ private static int OnVisit(IntPtr ob, IntPtr arg)
return 0;
}
+
+ internal static void StashPush(Stack stack)
+ {
+ stack.Push(cache);
+ }
+
+ internal static void StashPop(Stack stack)
+ {
+ cache = (Dictionary)stack.Pop();
+ }
+
///
/// Return the ClassBase-derived instance that implements a particular
/// reflected managed type, creating it if it doesn't yet exist.
@@ -239,7 +250,7 @@ private static void InitClassBase(Type type, ClassBase impl)
private static ClassInfo GetClassInfo(Type type)
{
- var ci = new ClassInfo(type);
+ var ci = new ClassInfo();
var methods = new Hashtable();
ArrayList list;
MethodInfo meth;
@@ -443,7 +454,7 @@ internal class ClassInfo
public Indexer indexer;
public Hashtable members;
- internal ClassInfo(Type t)
+ internal ClassInfo()
{
members = new Hashtable();
indexer = null;
diff --git a/src/runtime/classobject.cs b/src/runtime/classobject.cs
index 83d761fd0..9b63f76c7 100644
--- a/src/runtime/classobject.cs
+++ b/src/runtime/classobject.cs
@@ -9,6 +9,7 @@ namespace Python.Runtime
/// Python type objects. Each of those type objects is associated with
/// an instance of ClassObject, which provides its implementation.
///
+ [Serializable]
internal class ClassObject : ClassBase
{
internal ConstructorBinder binder;
diff --git a/src/runtime/clrobject.cs b/src/runtime/clrobject.cs
index 29c57b28f..7c4330219 100644
--- a/src/runtime/clrobject.cs
+++ b/src/runtime/clrobject.cs
@@ -9,6 +9,7 @@ internal class CLRObject : ManagedType
internal CLRObject(object ob, IntPtr tp)
{
+ System.Diagnostics.Debug.Assert(tp != IntPtr.Zero);
IntPtr py = Runtime.PyType_GenericAlloc(tp, 0);
long flags = Util.ReadCLong(tp, TypeOffset.tp_flags);
diff --git a/src/runtime/constructorbinder.cs b/src/runtime/constructorbinder.cs
index 1fc541920..c2d30217f 100644
--- a/src/runtime/constructorbinder.cs
+++ b/src/runtime/constructorbinder.cs
@@ -10,6 +10,7 @@ namespace Python.Runtime
/// 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 Type _containingType;
diff --git a/src/runtime/constructorbinding.cs b/src/runtime/constructorbinding.cs
index bd414229d..0c81c0a93 100644
--- a/src/runtime/constructorbinding.cs
+++ b/src/runtime/constructorbinding.cs
@@ -19,11 +19,14 @@ namespace Python.Runtime
/// 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 Type type; // The managed Type being wrapped in a ClassObject
private IntPtr pyTypeHndl; // The python type tells GetInstHandle which Type to create.
private ConstructorBinder ctorBinder;
+
+ [NonSerialized]
private IntPtr repr;
public ConstructorBinding(Type type, IntPtr pyTypeHndl, ConstructorBinder ctorBinder)
@@ -173,6 +176,7 @@ public static int tp_traverse(IntPtr ob, IntPtr visit, IntPtr arg)
/// 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
diff --git a/src/runtime/delegateobject.cs b/src/runtime/delegateobject.cs
index e1103cbc7..24060b8bb 100644
--- a/src/runtime/delegateobject.cs
+++ b/src/runtime/delegateobject.cs
@@ -8,6 +8,7 @@ namespace Python.Runtime
/// Each of those type objects is associated an instance of this class,
/// which provides its implementation.
///
+ [Serializable]
internal class DelegateObject : ClassBase
{
private MethodBinder binder;
diff --git a/src/runtime/eventbinding.cs b/src/runtime/eventbinding.cs
index dc3a6dfae..5dbace9d9 100644
--- a/src/runtime/eventbinding.cs
+++ b/src/runtime/eventbinding.cs
@@ -5,6 +5,7 @@ namespace Python.Runtime
///
/// Implements a Python event binding type, similar to a method binding.
///
+ [Serializable]
internal class EventBinding : ExtensionType
{
private EventObject e;
@@ -127,5 +128,11 @@ public static int tp_clear(IntPtr ob)
Runtime.Py_CLEAR(ref self.target);
return 0;
}
+
+ protected override void OnSave()
+ {
+ base.OnSave();
+ Runtime.XIncref(target);
+ }
}
}
diff --git a/src/runtime/eventobject.cs b/src/runtime/eventobject.cs
index 2a98896fe..0f2796a14 100644
--- a/src/runtime/eventobject.cs
+++ b/src/runtime/eventobject.cs
@@ -7,6 +7,7 @@ namespace Python.Runtime
///
/// Implements a Python descriptor type that provides access to CLR events.
///
+ [Serializable]
internal class EventObject : ExtensionType
{
internal string name;
diff --git a/src/runtime/extensiontype.cs b/src/runtime/extensiontype.cs
index c1aff3ca0..006d616b2 100644
--- a/src/runtime/extensiontype.cs
+++ b/src/runtime/extensiontype.cs
@@ -8,6 +8,7 @@ namespace Python.Runtime
/// type object, such as the types that represent CLR methods, fields,
/// etc. Instances implemented by this class do not support sub-typing.
///
+ [Serializable]
internal abstract class ExtensionType : ManagedType
{
public ExtensionType()
@@ -96,5 +97,19 @@ public static void tp_dealloc(IntPtr ob)
var self = (ExtensionType)GetManagedObject(ob);
self.Dealloc();
}
+
+ protected override void OnSave()
+ {
+ base.OnSave();
+ Runtime.XIncref(pyHandle);
+ }
+
+ protected override void OnLoad()
+ {
+ base.OnLoad();
+ GCHandle gc = AllocGCHandle(true);
+ Marshal.WriteIntPtr(pyHandle, ObjectOffset.magic(tpHandle), (IntPtr)gc);
+ Runtime.PyObject_GC_UnTrack(pyHandle);
+ }
}
}
diff --git a/src/runtime/indexer.cs b/src/runtime/indexer.cs
index 71f7e7aa1..0772b57c6 100644
--- a/src/runtime/indexer.cs
+++ b/src/runtime/indexer.cs
@@ -6,6 +6,7 @@ namespace Python.Runtime
///
/// Bundles the information required to support an indexer property.
///
+ [Serializable]
internal class Indexer
{
public MethodBinder GetterBinder;
diff --git a/src/runtime/interop.cs b/src/runtime/interop.cs
index d4b4b5119..58464ec7f 100644
--- a/src/runtime/interop.cs
+++ b/src/runtime/interop.cs
@@ -311,45 +311,45 @@ public static string GetSlotNameByOffset(int offset)
internal class TypeFlags
{
#if PYTHON2 // these flags were removed in Python 3
- public static int HaveGetCharBuffer = (1 << 0);
- public static int HaveSequenceIn = (1 << 1);
- public static int GC = 0;
- public static int HaveInPlaceOps = (1 << 3);
- public static int CheckTypes = (1 << 4);
- public static int HaveRichCompare = (1 << 5);
- public static int HaveWeakRefs = (1 << 6);
- public static int HaveIter = (1 << 7);
- public static int HaveClass = (1 << 8);
+ public const int HaveGetCharBuffer = (1 << 0);
+ public const int HaveSequenceIn = (1 << 1);
+ public const int GC = 0;
+ public const int HaveInPlaceOps = (1 << 3);
+ public const int CheckTypes = (1 << 4);
+ public const int HaveRichCompare = (1 << 5);
+ public const int HaveWeakRefs = (1 << 6);
+ public const int HaveIter = (1 << 7);
+ public const int HaveClass = (1 << 8);
#endif
- public static int HeapType = (1 << 9);
- public static int BaseType = (1 << 10);
- public static int Ready = (1 << 12);
- public static int Readying = (1 << 13);
- public static int HaveGC = (1 << 14);
+ public const int HeapType = (1 << 9);
+ public const int BaseType = (1 << 10);
+ public const int Ready = (1 << 12);
+ public const int Readying = (1 << 13);
+ public const int HaveGC = (1 << 14);
// 15 and 16 are reserved for stackless
- public static int HaveStacklessExtension = 0;
+ public const int HaveStacklessExtension = 0;
/* XXX Reusing reserved constants */
- public static int Managed = (1 << 15); // PythonNet specific
- public static int Subclass = (1 << 16); // PythonNet specific
- public static int HaveIndex = (1 << 17);
+ public const int Managed = (1 << 15); // PythonNet specific
+ public const int Subclass = (1 << 16); // PythonNet specific
+ public const int HaveIndex = (1 << 17);
/* Objects support nb_index in PyNumberMethods */
- public static int HaveVersionTag = (1 << 18);
- public static int ValidVersionTag = (1 << 19);
- public static int IsAbstract = (1 << 20);
- public static int HaveNewBuffer = (1 << 21);
+ public const int HaveVersionTag = (1 << 18);
+ public const int ValidVersionTag = (1 << 19);
+ public const int IsAbstract = (1 << 20);
+ public const int HaveNewBuffer = (1 << 21);
// TODO: Implement FastSubclass functions
- public static int IntSubclass = (1 << 23);
- public static int LongSubclass = (1 << 24);
- public static int ListSubclass = (1 << 25);
- public static int TupleSubclass = (1 << 26);
- public static int StringSubclass = (1 << 27);
- public static int UnicodeSubclass = (1 << 28);
- public static int DictSubclass = (1 << 29);
- public static int BaseExceptionSubclass = (1 << 30);
- public static int TypeSubclass = (1 << 31);
+ public const int IntSubclass = (1 << 23);
+ public const int LongSubclass = (1 << 24);
+ public const int ListSubclass = (1 << 25);
+ public const int TupleSubclass = (1 << 26);
+ public const int StringSubclass = (1 << 27);
+ public const int UnicodeSubclass = (1 << 28);
+ public const int DictSubclass = (1 << 29);
+ public const int BaseExceptionSubclass = (1 << 30);
+ public const int TypeSubclass = (1 << 31);
#if PYTHON2 // Default flags for Python 2
- public static int Default = (
+ public const int Default = (
HaveGetCharBuffer |
HaveSequenceIn |
HaveInPlaceOps |
@@ -361,7 +361,7 @@ internal class TypeFlags
HaveIndex |
0);
#elif PYTHON3 // Default flags for Python 3
- public static int Default = (
+ public const int Default = (
HaveStacklessExtension |
HaveVersionTag);
#endif
@@ -499,9 +499,16 @@ internal static ThunkInfo GetThunk(MethodInfo method, string funcType = null)
{
return ThunkInfo.Empty;
}
- Delegate d = Delegate.CreateDelegate(dt, method);
- var info = new ThunkInfo(d);
- return info;
+ try
+ {
+ Delegate d = Delegate.CreateDelegate(dt, method);
+ var info = new ThunkInfo(d);
+ return info;
+ }
+ catch (Exception)
+ {
+ throw;
+ }
}
@@ -579,7 +586,7 @@ struct PyGC_Head
}
- [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
+ [StructLayout(LayoutKind.Sequential)]
struct PyMethodDef
{
public IntPtr ml_name;
diff --git a/src/runtime/managedtype.cs b/src/runtime/managedtype.cs
index a93628524..cd735898e 100644
--- a/src/runtime/managedtype.cs
+++ b/src/runtime/managedtype.cs
@@ -11,9 +11,12 @@ namespace Python.Runtime
/// code. It defines the common fields that associate CLR and Python
/// objects and common utilities to convert between those identities.
///
+ [Serializable]
internal abstract class ManagedType
{
+ [NonSerialized]
internal GCHandle gcHandle; // Native handle
+
internal IntPtr pyHandle; // PyObject *
internal IntPtr tpHandle; // PyType *
@@ -190,6 +193,19 @@ protected void TypeClear()
ClearObjectDict(pyHandle);
}
+ internal void Save()
+ {
+ OnSave();
+ }
+
+ internal void Load()
+ {
+ OnLoad();
+ }
+
+ protected virtual void OnSave() { }
+ protected virtual void OnLoad() { }
+
protected static void ClearObjectDict(IntPtr ob)
{
IntPtr dict = GetObjectDict(ob);
@@ -201,12 +217,12 @@ protected static void ClearObjectDict(IntPtr ob)
Runtime.XDecref(dict);
}
- private static IntPtr GetObjectDict(IntPtr ob)
+ protected static IntPtr GetObjectDict(IntPtr ob)
{
return Marshal.ReadIntPtr(ob, ObjectOffset.DictOffset(ob));
}
- private static void SetObjectDict(IntPtr ob, IntPtr value)
+ protected static void SetObjectDict(IntPtr ob, IntPtr value)
{
Marshal.WriteIntPtr(ob, ObjectOffset.DictOffset(ob), value);
}
diff --git a/src/runtime/metatype.cs b/src/runtime/metatype.cs
index 68954ef0a..b54c2eb90 100644
--- a/src/runtime/metatype.cs
+++ b/src/runtime/metatype.cs
@@ -1,4 +1,6 @@
using System;
+using System.Collections;
+using System.IO;
using System.Runtime.InteropServices;
namespace Python.Runtime
@@ -13,6 +15,12 @@ internal class MetaType : ManagedType
private static IntPtr PyCLRMetaType;
private static SlotsHolder _metaSlotsHodler;
+ internal static readonly string[] CustomMethods = new string[]
+ {
+ "__instancecheck__",
+ "__subclasscheck__",
+ };
+
///
/// Metatype initialization. This bootstraps the CLR metatype to life.
///
@@ -32,6 +40,29 @@ public static void Release()
_metaSlotsHodler = null;
}
+ internal static void StashPush(Stack stack)
+ {
+ Runtime.XIncref(PyCLRMetaType);
+ stack.Push(PyCLRMetaType);
+ }
+
+ internal static IntPtr StashPop(Stack stack)
+ {
+ PyCLRMetaType = (IntPtr)stack.Pop();
+ _metaSlotsHodler = new SlotsHolder(PyCLRMetaType);
+ TypeManager.InitializeSlots(PyCLRMetaType, typeof(MetaType), _metaSlotsHodler);
+
+ IntPtr mdef = Marshal.ReadIntPtr(PyCLRMetaType, TypeOffset.tp_methods);
+ foreach (var methodName in CustomMethods)
+ {
+ var mi = typeof(MetaType).GetMethod(methodName);
+ ThunkInfo thunkInfo = Interop.GetThunk(mi, "BinaryFunc");
+ _metaSlotsHodler.KeeapAlive(thunkInfo);
+ mdef = TypeManager.WriteMethodDef(mdef, methodName, thunkInfo.Address);
+ }
+ return PyCLRMetaType;
+ }
+
///
/// Metatype __new__ implementation. This is called to create a new
/// class / type when a reflected class is subclassed.
diff --git a/src/runtime/methodbinder.cs b/src/runtime/methodbinder.cs
index 8a7fc1930..0773263ef 100644
--- a/src/runtime/methodbinder.cs
+++ b/src/runtime/methodbinder.cs
@@ -13,6 +13,7 @@ namespace Python.Runtime
/// a set of Python arguments. This is also used as a base class for the
/// ConstructorBinder, a minor variation used to invoke constructors.
///
+ [Serializable]
internal class MethodBinder
{
public ArrayList list;
diff --git a/src/runtime/methodbinding.cs b/src/runtime/methodbinding.cs
index 1caec322c..c14e592d5 100644
--- a/src/runtime/methodbinding.cs
+++ b/src/runtime/methodbinding.cs
@@ -9,6 +9,7 @@ namespace Python.Runtime
/// standard Python method bindings, but the same type is used to bind
/// both static and instance methods.
///
+ [Serializable]
internal class MethodBinding : ExtensionType
{
internal MethodInfo info;
diff --git a/src/runtime/methodobject.cs b/src/runtime/methodobject.cs
index 3942aae52..14fb0cd19 100644
--- a/src/runtime/methodobject.cs
+++ b/src/runtime/methodobject.cs
@@ -10,6 +10,7 @@ namespace Python.Runtime
///
/// TODO: ForbidPythonThreadsAttribute per method info
///
+ [Serializable]
internal class MethodObject : ExtensionType
{
internal MethodInfo[] info;
@@ -17,6 +18,7 @@ internal class MethodObject : ExtensionType
internal MethodBinding unbound;
internal MethodBinder binder;
internal bool is_static = false;
+ [NonSerialized]
internal IntPtr doc;
internal Type type;
diff --git a/src/runtime/modulefunctionobject.cs b/src/runtime/modulefunctionobject.cs
index 8f8692af9..e7a2c515a 100644
--- a/src/runtime/modulefunctionobject.cs
+++ b/src/runtime/modulefunctionobject.cs
@@ -7,6 +7,7 @@ namespace Python.Runtime
///
/// Module level functions
///
+ [Serializable]
internal class ModuleFunctionObject : MethodObject
{
public ModuleFunctionObject(Type type, string name, MethodInfo[] info, bool allow_threads)
diff --git a/src/runtime/moduleobject.cs b/src/runtime/moduleobject.cs
index 06f1c0581..0d42914e5 100644
--- a/src/runtime/moduleobject.cs
+++ b/src/runtime/moduleobject.cs
@@ -10,9 +10,12 @@ namespace Python.Runtime
/// Implements a Python type that provides access to CLR namespaces. The
/// type behaves like a Python module, and can contain other sub-modules.
///
+ [Serializable]
internal class ModuleObject : ExtensionType
{
+ [NonSerialized]
private Dictionary cache;
+
internal string moduleName;
internal IntPtr dict;
protected string _namespace;
@@ -338,6 +341,21 @@ public static int tp_clear(IntPtr ob)
self.cache.Clear();
return 0;
}
+
+ protected override void OnSave()
+ {
+ base.OnSave();
+ Runtime.XIncref(dict);
+ Runtime.XIncref(GetObjectDict(pyHandle));
+ }
+
+ protected override void OnLoad()
+ {
+ base.OnLoad();
+ cache = new Dictionary();
+ Runtime.XIncref(dict);
+ SetObjectDict(pyHandle, dict);
+ }
}
///
@@ -345,6 +363,7 @@ public static int tp_clear(IntPtr ob)
/// to import assemblies. It has a fixed module name "clr" and doesn't
/// provide a namespace.
///
+ [Serializable]
internal class CLRModule : ModuleObject
{
protected static bool hacked = false;
diff --git a/src/runtime/nativecall.cs b/src/runtime/nativecall.cs
index 4a7bf05c8..ec0bf338c 100644
--- a/src/runtime/nativecall.cs
+++ b/src/runtime/nativecall.cs
@@ -23,7 +23,6 @@ namespace Python.Runtime
///
internal class NativeCall
{
-#if NETSTANDARD
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate void Void_1_Delegate(IntPtr a1);
@@ -32,148 +31,27 @@ internal class NativeCall
public static void Void_Call_1(IntPtr fp, IntPtr a1)
{
- var d = Marshal.GetDelegateForFunctionPointer(fp);
+ var d = GetDelegate(fp);
d(a1);
}
public static IntPtr Call_3(IntPtr fp, IntPtr a1, IntPtr a2, IntPtr a3)
{
- var d = Marshal.GetDelegateForFunctionPointer(fp);
+ var d = GetDelegate(fp);
return d(a1, a2, a3);
}
public static int Int_Call_3(IntPtr fp, IntPtr a1, IntPtr a2, IntPtr a3)
{
- var d = Marshal.GetDelegateForFunctionPointer(fp);
+ var d = GetDelegate(fp);
return d(a1, a2, a3);
}
-#else
- private static AssemblyBuilder aBuilder;
- private static ModuleBuilder mBuilder;
- public static INativeCall Impl;
-
- static NativeCall()
- {
- // The static constructor is responsible for generating the
- // assembly and the methods that implement the IJW thunks.
- //
- // To do this, we actually use reflection on the INativeCall
- // interface (defined below) and generate the required thunk
- // code based on the method signatures.
-
- var aname = new AssemblyName { Name = "e__NativeCall_Assembly" };
- var aa = AssemblyBuilderAccess.Run;
-
- aBuilder = Thread.GetDomain().DefineDynamicAssembly(aname, aa);
- mBuilder = aBuilder.DefineDynamicModule("e__NativeCall_Module");
-
- var ta = TypeAttributes.Public;
- TypeBuilder tBuilder = mBuilder.DefineType("e__NativeCall", ta);
-
- Type iType = typeof(INativeCall);
- tBuilder.AddInterfaceImplementation(iType);
-
- // Use reflection to loop over the INativeCall interface methods,
- // calling GenerateThunk to create a managed thunk for each one.
-
- foreach (MethodInfo method in iType.GetMethods())
- {
- GenerateThunk(tBuilder, method);
- }
-
- Type theType = tBuilder.CreateType();
-
- Impl = (INativeCall)Activator.CreateInstance(theType);
- }
-
- private static void GenerateThunk(TypeBuilder tb, MethodInfo method)
- {
- ParameterInfo[] pi = method.GetParameters();
- int count = pi.Length;
- int argc = count - 1;
-
- var args = new Type[count];
- for (var i = 0; i < count; i++)
- {
- args[i] = pi[i].ParameterType;
- }
-
- MethodBuilder mb = tb.DefineMethod(
- method.Name,
- MethodAttributes.Public |
- MethodAttributes.Virtual,
- method.ReturnType,
- args
- );
-
- // Build the method signature for the actual native function.
- // This is essentially the signature of the wrapper method
- // minus the first argument (the passed in function pointer).
-
- var nargs = new Type[argc];
- for (var i = 1; i < count; i++)
- {
- nargs[i - 1] = args[i];
- }
-
- // IL generation: the (implicit) first argument of the method
- // is the 'this' pointer and the second is the function pointer.
- // This code pushes the real args onto the stack, followed by
- // the function pointer, then the calli opcode to make the call.
-
- ILGenerator il = mb.GetILGenerator();
-
- for (var i = 0; i < argc; i++)
- {
- il.Emit(OpCodes.Ldarg_S, i + 2);
- }
-
- il.Emit(OpCodes.Ldarg_1);
-
- il.EmitCalli(OpCodes.Calli,
- CallingConvention.Cdecl,
- method.ReturnType,
- nargs
- );
-
- il.Emit(OpCodes.Ret);
-
- tb.DefineMethodOverride(mb, method);
- }
-
-
- public static void Void_Call_1(IntPtr fp, IntPtr a1)
- {
- Impl.Void_Call_1(fp, a1);
- }
-
- public static IntPtr Call_3(IntPtr fp, IntPtr a1, IntPtr a2, IntPtr a3)
- {
- return Impl.Call_3(fp, a1, a2, a3);
- }
-
- public static int Int_Call_3(IntPtr fp, IntPtr a1, IntPtr a2, IntPtr a3)
+ private static T GetDelegate(IntPtr fp) where T: Delegate
{
- return Impl.Int_Call_3(fp, a1, a2, a3);
+ // Use Marshal.GetDelegateForFunctionPointer<> directly after upgrade the framework
+ return (T)Marshal.GetDelegateForFunctionPointer(fp, typeof(T));
}
-#endif
- }
-
-#if !NETSTANDARD
- ///
- /// Defines native call signatures to be generated by NativeCall.
- ///
- public interface INativeCall
- {
- void Void_Call_0(IntPtr funcPtr);
-
- void Void_Call_1(IntPtr funcPtr, IntPtr arg1);
-
- int Int_Call_3(IntPtr funcPtr, IntPtr t, IntPtr n, IntPtr v);
-
- IntPtr Call_3(IntPtr funcPtr, IntPtr a1, IntPtr a2, IntPtr a3);
}
-#endif
}
diff --git a/src/runtime/propertyobject.cs b/src/runtime/propertyobject.cs
index f2c97f163..ac1d077f9 100644
--- a/src/runtime/propertyobject.cs
+++ b/src/runtime/propertyobject.cs
@@ -7,6 +7,7 @@ namespace Python.Runtime
///
/// Implements a Python descriptor type that manages CLR properties.
///
+ [Serializable]
internal class PropertyObject : ExtensionType
{
private PropertyInfo info;
diff --git a/src/runtime/pythonengine.cs b/src/runtime/pythonengine.cs
index 8688fe17f..301eea44e 100644
--- a/src/runtime/pythonengine.cs
+++ b/src/runtime/pythonengine.cs
@@ -12,7 +12,7 @@ namespace Python.Runtime
///
public class PythonEngine : IDisposable
{
- public static bool SoftShutdown => Runtime.SoftShutdown;
+ public static ShutdownMode ShutdownMode => Runtime.ShutdownMode;
private static DelegateManager delegateManager;
private static bool initialized;
@@ -152,9 +152,9 @@ public static void Initialize()
Initialize(setSysArgv: true);
}
- public static void Initialize(bool setSysArgv = true, bool initSigs = false, bool softShutdown = false)
+ public static void Initialize(bool setSysArgv = true, bool initSigs = false, ShutdownMode mode = ShutdownMode.Normal)
{
- Initialize(Enumerable.Empty(), setSysArgv: setSysArgv, initSigs: initSigs, softShutdown);
+ Initialize(Enumerable.Empty(), setSysArgv: setSysArgv, initSigs: initSigs, mode);
}
///
@@ -167,7 +167,7 @@ public static void Initialize(bool setSysArgv = true, bool initSigs = false, boo
/// 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, bool softShutdown = false)
+ public static void Initialize(IEnumerable args, bool setSysArgv = true, bool initSigs = false, ShutdownMode mode = ShutdownMode.Normal)
{
if (initialized)
{
@@ -179,7 +179,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, softShutdown);
+ Runtime.Initialize(initSigs, mode);
initialized = true;
Exceptions.Clear();
@@ -187,7 +187,7 @@ public static void Initialize(IEnumerable args, bool setSysArgv = true,
AppDomain.CurrentDomain.DomainUnload += OnDomainUnload;
// Remember to shut down the runtime.
- AddShutdownHandler(() => Runtime.Shutdown(softShutdown));
+ AddShutdownHandler(() => Runtime.Shutdown(mode));
// The global scope gets used implicitly quite early on, remember
// to clear it out when we shut down.
@@ -198,8 +198,9 @@ public static void Initialize(IEnumerable args, bool setSysArgv = true,
Py.SetArgv(args);
}
- if (!softShutdown)
+ if (mode == ShutdownMode.Normal)
{
+ // TOOD: Check if this can be remove completely or not.
// register the atexit callback (this doesn't use Py_AtExit as the C atexit
// callbacks are called after python is fully finalized but the python ones
// are called while the python engine is still running).
diff --git a/src/runtime/runtime.cs b/src/runtime/runtime.cs
index 0123bc499..71a58dfef 100644
--- a/src/runtime/runtime.cs
+++ b/src/runtime/runtime.cs
@@ -6,6 +6,8 @@
using System.Collections.Generic;
using Python.Runtime.Platform;
using System.Linq;
+using System.IO;
+using System.Runtime.Serialization.Formatters.Binary;
namespace Python.Runtime
{
@@ -124,13 +126,13 @@ public class Runtime
///
internal static readonly Encoding PyEncoding = _UCS == 2 ? Encoding.Unicode : Encoding.UTF32;
- public static bool SoftShutdown { get; private set; }
+ public static ShutdownMode ShutdownMode { get; private set; }
private static PyReferenceCollection _pyRefs = new PyReferenceCollection();
///
/// Initialize the runtime...
///
- internal static void Initialize(bool initSigs = false, bool softShutdown = false)
+ internal static void Initialize(bool initSigs = false, ShutdownMode mode = ShutdownMode.Normal)
{
if (_isInitialized)
{
@@ -140,10 +142,14 @@ internal static void Initialize(bool initSigs = false, bool softShutdown = false
if (Environment.GetEnvironmentVariable("PYTHONNET_SOFT_SHUTDOWN") == "1")
{
- softShutdown = true;
+ mode = ShutdownMode.Soft;
+ }
+ else if (Environment.GetEnvironmentVariable("PYTHONNET_RELOAD_SHUTDOWN") == "1")
+ {
+ mode = ShutdownMode.Reload;
}
- SoftShutdown = softShutdown;
+ ShutdownMode = mode;
if (Py_IsInitialized() == 0)
{
Py_InitializeEx(initSigs ? 1 : 0);
@@ -151,7 +157,7 @@ internal static void Initialize(bool initSigs = false, bool softShutdown = false
{
PyEval_InitThreads();
}
- if (softShutdown)
+ if (mode == ShutdownMode.Soft)
{
RuntimeState.Save();
}
@@ -308,7 +314,16 @@ internal static void Initialize(bool initSigs = false, bool softShutdown = false
// Initialize modules that depend on the runtime class.
AssemblyManager.Initialize();
- PyCLRMetaType = MetaType.Initialize(); // Steal a reference
+
+ if (mode == ShutdownMode.Reload && RuntimeData.HasStashData())
+ {
+ RuntimeData.StashPop();
+ }
+ else
+ {
+ PyCLRMetaType = MetaType.Initialize(); // Steal a reference
+ }
+
Exceptions.Initialize();
ImportHook.Initialize();
@@ -323,7 +338,7 @@ internal static void Initialize(bool initSigs = false, bool softShutdown = false
}
- internal static void Shutdown(bool soft = false)
+ internal static void Shutdown(ShutdownMode mode = ShutdownMode.Normal)
{
if (Py_IsInitialized() == 0 || !_isInitialized)
{
@@ -333,12 +348,17 @@ internal static void Shutdown(bool soft = false)
PyGILState_Ensure();
+ RuntimeData.Stash();
+
AssemblyManager.Shutdown();
Exceptions.Shutdown();
ImportHook.Shutdown();
Finalizer.Shutdown();
- ClearClrModules();
+ if (mode != ShutdownMode.Reload)
+ {
+ ClearClrModules();
+ }
RemoveClrRootModule();
MoveClrInstancesOnwershipToPython();
@@ -348,10 +368,13 @@ internal static void Shutdown(bool soft = false)
MetaType.Release();
PyCLRMetaType = IntPtr.Zero;
- if (soft)
+ if (mode != ShutdownMode.Normal)
{
PyGC_Collect();
- RuntimeState.Restore();
+ if (mode == ShutdownMode.Soft)
+ {
+ RuntimeState.Restore();
+ }
ResetPyMembers();
GC.Collect();
try
@@ -657,7 +680,7 @@ internal static unsafe void XDecref(IntPtr op)
{
return;
}
- NativeCall.Impl.Void_Call_1(new IntPtr(f), op);
+ NativeCall.Void_Call_1(new IntPtr(f), op);
}
}
#endif
@@ -2099,6 +2122,8 @@ internal static void Py_CLEAR(ref IntPtr ob)
[DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)]
internal static extern IntPtr PyCapsule_GetPointer(IntPtr capsule, string name);
+ [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern int PyCapsule_SetPointer(IntPtr capsule, IntPtr pointer);
//====================================================================
// Miscellaneous
@@ -2151,6 +2176,14 @@ internal static IntPtr GetBuiltins()
}
+ public enum ShutdownMode
+ {
+ Normal,
+ Soft,
+ Reload,
+ }
+
+
class PyReferenceCollection
{
private List> _actions = new List>();
diff --git a/src/runtime/runtime_state.cs b/src/runtime/runtime_state.cs
index bb91b125c..d7f6f5558 100644
--- a/src/runtime/runtime_state.cs
+++ b/src/runtime/runtime_state.cs
@@ -1,8 +1,11 @@
using System;
+using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
+using System.IO;
using System.Runtime.InteropServices;
-using System.Linq;
+using System.Runtime.Serialization.Formatters.Binary;
+
using static Python.Runtime.Runtime;
namespace Python.Runtime
@@ -233,4 +236,78 @@ private static unsafe IntPtr GetNextGCNode(IntPtr node)
return ((PyGC_Head*)node)->gc.gc_next;
}
}
+
+
+ class RuntimeData
+ {
+ internal static void Stash()
+ {
+ var formatter = new BinaryFormatter();
+ var ms = new MemoryStream();
+ var stack = new Stack();
+ MetaType.StashPush(stack);
+ TypeManager.StashPush(stack);
+ ClassManager.StashPush(stack);
+ var objs = ManagedType.GetManagedObjects();
+ foreach (var obj in objs)
+ {
+ obj.Save();
+ }
+ stack.Push(objs);
+ formatter.Serialize(ms, stack);
+
+ byte[] data = ms.GetBuffer();
+ // TODO: use buffer api instead
+ System.Diagnostics.Debug.Assert(data.Length <= int.MaxValue);
+ IntPtr mem = PyMem_Malloc(data.LongLength + IntPtr.Size);
+ Marshal.WriteIntPtr(mem, (IntPtr)data.LongLength);
+ Marshal.Copy(data, 0, mem + IntPtr.Size, data.Length);
+
+ IntPtr capsule = PySys_GetObject("clr_data");
+ if (capsule != IntPtr.Zero)
+ {
+ IntPtr oldData = PyCapsule_GetPointer(capsule, null);
+ PyMem_Free(oldData);
+ PyCapsule_SetPointer(capsule, IntPtr.Zero);
+ }
+ capsule = PyCapsule_New(mem, null, IntPtr.Zero);
+ PySys_SetObject("clr_data", capsule);
+ XDecref(capsule);
+ }
+
+ internal static void StashPop()
+ {
+ IntPtr capsule = PySys_GetObject("clr_data");
+ if (capsule == IntPtr.Zero)
+ {
+ return;
+ }
+ IntPtr mem = PyCapsule_GetPointer(capsule, null);
+ int length = (int)Marshal.ReadIntPtr(mem);
+ byte[] data = new byte[length];
+ Marshal.Copy(mem + IntPtr.Size, data, 0, length);
+ var ms = new MemoryStream(data);
+ var formatter = new BinaryFormatter();
+
+ var stack = (Stack)formatter.Deserialize(ms);
+
+ var loadObjs = (ICollection)stack.Pop();
+ var objs = ManagedType.GetManagedObjects();
+ foreach (var obj in loadObjs)
+ {
+ obj.Load();
+ objs.Add(obj);
+ }
+
+ ClassManager.StashPop(stack);
+ TypeManager.StashPop(stack);
+ PyCLRMetaType = MetaType.StashPop(stack);
+ }
+
+ public static bool HasStashData()
+ {
+ return PySys_GetObject("clr_data") != IntPtr.Zero;
+ }
+
+ }
}
diff --git a/src/runtime/typemanager.cs b/src/runtime/typemanager.cs
index 3d6e66a03..d824c1fb4 100644
--- a/src/runtime/typemanager.cs
+++ b/src/runtime/typemanager.cs
@@ -20,7 +20,7 @@ internal class TypeManager
internal static IntPtr subtype_clear;
private const BindingFlags tbFlags = BindingFlags.Public | BindingFlags.Static;
- private static readonly Dictionary cache = new Dictionary();
+ private static Dictionary cache = new Dictionary();
private static readonly Dictionary _slotsHolders = new Dictionary();
// Slots which must be set
@@ -65,6 +65,30 @@ internal static void RemoveTypes()
_slotsHolders.Clear();
}
+ internal static void StashPush(Stack stack)
+ {
+ foreach (var tpHandle in cache.Values)
+ {
+ Runtime.XIncref(tpHandle);
+ }
+ //formatter.Serialize(stream, cache);
+ stack.Push(cache);
+ }
+
+ internal static void StashPop(Stack stack)
+ {
+ Debug.Assert(cache == null || cache.Count == 0);
+ cache = (Dictionary)stack.Pop();
+ foreach (var entry in cache)
+ {
+ Type type = entry.Key;
+ IntPtr handle = entry.Value;
+ SlotsHolder holder = CreateSolotsHolder(handle);
+ InitializeSlots(handle, type, holder);
+ // FIXME: mp_length_slot.CanAssgin(clrType)
+ }
+ }
+
///
/// Return value: Borrowed reference.
/// Given a managed Type derived from ExtensionType, get the handle to
@@ -381,96 +405,89 @@ internal static IntPtr CreateMetaType(Type impl, out SlotsHolder slotsHolder)
Marshal.WriteIntPtr(type, TypeOffset.tp_base, py_type);
Runtime.XIncref(py_type);
+ const int flags = TypeFlags.Default
+ | TypeFlags.Managed
+ | TypeFlags.HeapType
+ | TypeFlags.HaveGC;
+ Util.WriteCLong(type, TypeOffset.tp_flags, flags);
+
// Slots will inherit from TypeType, it's not neccesary for setting them.
// Inheried slots:
// tp_basicsize, tp_itemsize,
// tp_dictoffset, tp_weaklistoffset,
// tp_traverse, tp_clear, tp_is_gc, etc.
+ slotsHolder = SetupMetaSlots(impl, type);
+
+ if (Runtime.PyType_Ready(type) != 0)
+ {
+ throw new PythonException();
+ }
+ IntPtr dict = Marshal.ReadIntPtr(type, TypeOffset.tp_dict);
+ IntPtr mod = Runtime.PyString_FromString("CLR");
+ Runtime.PyDict_SetItemString(dict, "__module__", mod);
+
+ //DebugUtil.DumpType(type);
+
+ return type;
+ }
+
+ internal static SlotsHolder SetupMetaSlots(Type impl, IntPtr type)
+ {
// Override type slots with those of the managed implementation.
- slotsHolder = new SlotsHolder(type);
+ SlotsHolder slotsHolder = new SlotsHolder(type);
InitializeSlots(type, impl, slotsHolder);
- int flags = TypeFlags.Default;
- flags |= TypeFlags.Managed;
- flags |= TypeFlags.HeapType;
- flags |= TypeFlags.HaveGC;
- Util.WriteCLong(type, TypeOffset.tp_flags, flags);
-
- // We need space for 3 PyMethodDef structs, each of them
- // 4 int-ptrs in size.
- IntPtr mdef = Runtime.PyMem_Malloc(3 * 4 * IntPtr.Size);
- Debug.Assert(4 * IntPtr.Size == Marshal.SizeOf(typeof(PyMethodDef)));
+ // We need space for 3 PyMethodDef structs.
+ int mdefSize = (MetaType.CustomMethods.Length + 1) * Marshal.SizeOf(typeof(PyMethodDef));
+ IntPtr mdef = Runtime.PyMem_Malloc(mdefSize);
IntPtr mdefStart = mdef;
- ThunkInfo thunkInfo = Interop.GetThunk(typeof(MetaType).GetMethod("__instancecheck__"), "BinaryFunc");
- slotsHolder.KeeapAlive(thunkInfo);
+ foreach (var methodName in MetaType.CustomMethods)
+ {
+ mdef = AddCustomMetaMethod(methodName, type, mdef, slotsHolder);
+ }
+ mdef = WriteMethodDefSentinel(mdef);
+ Debug.Assert((long)(mdefStart + mdefSize) <= (long)mdef);
+ Marshal.WriteIntPtr(type, TypeOffset.tp_methods, mdefStart);
+ // XXX: Hard code with mode check.
+ if (Runtime.ShutdownMode != ShutdownMode.Reload)
{
- IntPtr mdefAddr = mdef;
- slotsHolder.AddDealloctor(() =>
+ slotsHolder.Set(TypeOffset.tp_methods, (t, offset) =>
{
- IntPtr t = type;
- IntPtr tp_dict = Marshal.ReadIntPtr(t, TypeOffset.tp_dict);
- if (Runtime.PyDict_DelItemString(tp_dict, "__instancecheck__") != 0)
- {
- Runtime.PyErr_Print();
- }
- FreeMethodDef(mdefAddr);
+ var p = Marshal.ReadIntPtr(t, offset);
+ Runtime.PyMem_Free(p);
+ Marshal.WriteIntPtr(t, offset, IntPtr.Zero);
});
}
- mdef = WriteMethodDef(
- mdef,
- "__instancecheck__",
- thunkInfo.Address
- );
+ return slotsHolder;
+ }
- thunkInfo = Interop.GetThunk(typeof(MetaType).GetMethod("__subclasscheck__"), "BinaryFunc");
+ private static IntPtr AddCustomMetaMethod(string name, IntPtr type, IntPtr mdef, SlotsHolder slotsHolder)
+ {
+ MethodInfo mi = typeof(MetaType).GetMethod(name);
+ ThunkInfo thunkInfo = Interop.GetThunk(mi, "BinaryFunc");
slotsHolder.KeeapAlive(thunkInfo);
+
+ // XXX: Hard code with mode check.
+ if (Runtime.ShutdownMode != ShutdownMode.Reload)
{
IntPtr mdefAddr = mdef;
slotsHolder.AddDealloctor(() =>
{
- IntPtr t = type;
- IntPtr tp_dict = Marshal.ReadIntPtr(t, TypeOffset.tp_dict);
- if (Runtime.PyDict_DelItemString(tp_dict, "__subclasscheck__") != 0)
+ IntPtr tp_dict = Marshal.ReadIntPtr(type, TypeOffset.tp_dict);
+ if (Runtime.PyDict_DelItemString(tp_dict, name) != 0)
{
Runtime.PyErr_Print();
+ Debug.Fail($"Cannot remove {name} from metatype");
}
FreeMethodDef(mdefAddr);
});
}
- mdef = WriteMethodDef(
- mdef,
- "__subclasscheck__",
- thunkInfo.Address
- );
-
- // FIXME: mdef is not used
- mdef = WriteMethodDefSentinel(mdef);
-
- Marshal.WriteIntPtr(type, TypeOffset.tp_methods, mdefStart);
- slotsHolder.Set(TypeOffset.tp_methods, (t, offset) =>
- {
- var p = Marshal.ReadIntPtr(t, offset);
- Runtime.PyMem_Free(p);
- Marshal.WriteIntPtr(t, offset, IntPtr.Zero);
- });
-
- if (Runtime.PyType_Ready(type) != 0)
- {
- throw new PythonException();
- }
-
- IntPtr dict = Marshal.ReadIntPtr(type, TypeOffset.tp_dict);
- IntPtr mod = Runtime.PyString_FromString("CLR");
- Runtime.PyDict_SetItemString(dict, "__module__", mod);
-
- //DebugUtil.DumpType(type);
-
- return type;
+ mdef = WriteMethodDef(mdef, name, thunkInfo.Address);
+ return mdef;
}
-
internal static IntPtr BasicSubType(string name, IntPtr base_, Type impl)
{
// Utility to create a subtype of a std Python type, but with
@@ -543,22 +560,19 @@ internal static IntPtr AllocateTypeObject(string name)
Runtime.XIncref(temp);
Marshal.WriteIntPtr(type, TypeOffset.qualname, temp);
#endif
-
- long ptr = type.ToInt64(); // 64-bit safe
-
- temp = new IntPtr(ptr + TypeOffset.nb_add);
+ temp = type + TypeOffset.nb_add;
Marshal.WriteIntPtr(type, TypeOffset.tp_as_number, temp);
- temp = new IntPtr(ptr + TypeOffset.sq_length);
+ temp = type + TypeOffset.sq_length;
Marshal.WriteIntPtr(type, TypeOffset.tp_as_sequence, temp);
- temp = new IntPtr(ptr + TypeOffset.mp_length);
+ temp = type + TypeOffset.mp_length;
Marshal.WriteIntPtr(type, TypeOffset.tp_as_mapping, temp);
#if PYTHON3
- temp = new IntPtr(ptr + TypeOffset.bf_getbuffer);
+ temp = type + TypeOffset.bf_getbuffer;
#elif PYTHON2
- temp = new IntPtr(ptr + TypeOffset.bf_getreadbuffer);
+ temp = type + TypeOffset.bf_getreadbuffer;
#endif
Marshal.WriteIntPtr(type, TypeOffset.tp_as_buffer, temp);
return type;
@@ -602,7 +616,7 @@ internal static void InitializeSlots(IntPtr type, Type impl, SlotsHolder slotsHo
foreach (string slot in _requiredSlots)
{
- if (IsSlotSet(type, slot))
+ if (seen.Contains(slot))
{
continue;
}
From da97502006791bb0597446766ad00a6f9d291895 Mon Sep 17 00:00:00 2001
From: amos402
Date: Wed, 5 Feb 2020 21:25:22 +0800
Subject: [PATCH 076/998] Data synchronization for PyScopeTest.TestThread
---
src/embed_tests/TestPyScope.cs | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/src/embed_tests/TestPyScope.cs b/src/embed_tests/TestPyScope.cs
index 21c0d2b3f..1f03eae54 100644
--- a/src/embed_tests/TestPyScope.cs
+++ b/src/embed_tests/TestPyScope.cs
@@ -337,10 +337,13 @@ public void TestThread()
//add function to the scope
//can be call many times, more efficient than ast
ps.Exec(
+ "import threading\n"+
+ "lock = threading.Lock()\n"+
"def update():\n" +
- " global res, th_cnt\n" +
- " res += bb + 1\n" +
- " th_cnt += 1\n"
+ " global res, th_cnt\n" +
+ " with lock:\n" +
+ " res += bb + 1\n" +
+ " th_cnt += 1\n"
);
}
int th_cnt = 3;
From e8b31605691462d5bd143c30d57269902a144d05 Mon Sep 17 00:00:00 2001
From: amos402
Date: Thu, 6 Feb 2020 13:33:35 +0800
Subject: [PATCH 077/998] Disable `ShutdownMode.Reload` on `NETSTANDARD`
---
src/runtime/runtime.cs | 22 ++++++++++++++++++----
src/runtime/typemanager.cs | 6 +++++-
2 files changed, 23 insertions(+), 5 deletions(-)
diff --git a/src/runtime/runtime.cs b/src/runtime/runtime.cs
index 71a58dfef..063ed6d99 100644
--- a/src/runtime/runtime.cs
+++ b/src/runtime/runtime.cs
@@ -144,10 +144,13 @@ internal static void Initialize(bool initSigs = false, ShutdownMode mode = Shutd
{
mode = ShutdownMode.Soft;
}
+#if !NETSTANDARD
else if (Environment.GetEnvironmentVariable("PYTHONNET_RELOAD_SHUTDOWN") == "1")
{
mode = ShutdownMode.Reload;
}
+#endif
+ //mode = ShutdownMode.Reload;
ShutdownMode = mode;
if (Py_IsInitialized() == 0)
@@ -314,16 +317,16 @@ internal static void Initialize(bool initSigs = false, ShutdownMode mode = Shutd
// Initialize modules that depend on the runtime class.
AssemblyManager.Initialize();
-
+#if !NETSTANDARD
if (mode == ShutdownMode.Reload && RuntimeData.HasStashData())
{
RuntimeData.StashPop();
}
else
+#endif
{
PyCLRMetaType = MetaType.Initialize(); // Steal a reference
}
-
Exceptions.Initialize();
ImportHook.Initialize();
@@ -348,17 +351,23 @@ internal static void Shutdown(ShutdownMode mode = ShutdownMode.Normal)
PyGILState_Ensure();
- RuntimeData.Stash();
-
+#if !NETSTANDARD
+ if (mode == ShutdownMode.Reload)
+ {
+ RuntimeData.Stash();
+ }
+#endif
AssemblyManager.Shutdown();
Exceptions.Shutdown();
ImportHook.Shutdown();
Finalizer.Shutdown();
+#if !NETSTANDARD
if (mode != ShutdownMode.Reload)
{
ClearClrModules();
}
+#endif
RemoveClrRootModule();
MoveClrInstancesOnwershipToPython();
@@ -970,6 +979,9 @@ internal static bool PyObject_IsIterable(IntPtr pointer)
[DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)]
internal static extern IntPtr PyObject_GetAttrString(IntPtr pointer, string name);
+ [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern IntPtr PyObject_GetAttrString(IntPtr pointer, IntPtr name);
+
[DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)]
internal static extern int PyObject_SetAttrString(IntPtr pointer, string name, IntPtr value);
@@ -2180,7 +2192,9 @@ public enum ShutdownMode
{
Normal,
Soft,
+#if !NETSTANDARD
Reload,
+#endif
}
diff --git a/src/runtime/typemanager.cs b/src/runtime/typemanager.cs
index d824c1fb4..6475fa919 100644
--- a/src/runtime/typemanager.cs
+++ b/src/runtime/typemanager.cs
@@ -71,7 +71,6 @@ internal static void StashPush(Stack stack)
{
Runtime.XIncref(tpHandle);
}
- //formatter.Serialize(stream, cache);
stack.Push(cache);
}
@@ -450,6 +449,8 @@ internal static SlotsHolder SetupMetaSlots(Type impl, IntPtr type)
Debug.Assert((long)(mdefStart + mdefSize) <= (long)mdef);
Marshal.WriteIntPtr(type, TypeOffset.tp_methods, mdefStart);
+
+#if !NETSTANDARD
// XXX: Hard code with mode check.
if (Runtime.ShutdownMode != ShutdownMode.Reload)
{
@@ -460,6 +461,7 @@ internal static SlotsHolder SetupMetaSlots(Type impl, IntPtr type)
Marshal.WriteIntPtr(t, offset, IntPtr.Zero);
});
}
+#endif
return slotsHolder;
}
@@ -469,8 +471,10 @@ private static IntPtr AddCustomMetaMethod(string name, IntPtr type, IntPtr mdef,
ThunkInfo thunkInfo = Interop.GetThunk(mi, "BinaryFunc");
slotsHolder.KeeapAlive(thunkInfo);
+#if !NETSTANDARD
// XXX: Hard code with mode check.
if (Runtime.ShutdownMode != ShutdownMode.Reload)
+#endif
{
IntPtr mdefAddr = mdef;
slotsHolder.AddDealloctor(() =>
From 80d4fa0bcb342ff8a41e2b7c27c349e32fda381b Mon Sep 17 00:00:00 2001
From: amos402
Date: Sun, 9 Feb 2020 20:54:24 +0800
Subject: [PATCH 078/998] * Serialize CLRObject * Domain reload test
---
src/embed_tests/DomainCode.cs | 151 ++++++++++++++++++
.../Python.EmbeddingTest.15.csproj | 4 +
src/embed_tests/TestDomainReload.cs | 135 +++++++++-------
src/runtime/clrobject.cs | 17 +-
src/runtime/extensiontype.cs | 4 +-
src/runtime/managedtype.cs | 19 ++-
src/runtime/moduleobject.cs | 5 +-
src/runtime/pyobject.cs | 4 +
src/runtime/pythonengine.cs | 9 +-
src/runtime/runtime.cs | 22 ++-
src/runtime/runtime_state.cs | 15 +-
11 files changed, 298 insertions(+), 87 deletions(-)
create mode 100644 src/embed_tests/DomainCode.cs
diff --git a/src/embed_tests/DomainCode.cs b/src/embed_tests/DomainCode.cs
new file mode 100644
index 000000000..0c694ce5f
--- /dev/null
+++ b/src/embed_tests/DomainCode.cs
@@ -0,0 +1,151 @@
+using Python.Runtime;
+using System;
+using System.Diagnostics;
+using System.Reflection;
+
+//
+// The code we'll test. All that really matters is
+// using GIL { Python.Exec(pyScript); }
+// but the rest is useful for debugging.
+//
+// What matters in the python code is gc.collect and clr.AddReference.
+//
+// Note that the language version is 2.0, so no $"foo{bar}" syntax.
+//
+static class PythonRunner
+{
+ static readonly Action XIncref;
+ static readonly Action XDecref;
+
+ static PythonRunner()
+ {
+ const BindingFlags flags = BindingFlags.Static | BindingFlags.NonPublic;
+ MethodInfo incMethod = typeof(Runtime).GetMethod("XIncref", flags);
+ MethodInfo decMethod = typeof(Runtime).GetMethod("XDecref", flags);
+
+ XIncref = (Action)Delegate.CreateDelegate(typeof(Action), incMethod);
+ XDecref = (Action)Delegate.CreateDelegate(typeof(Action), decMethod);
+ }
+
+ public static void RunPython()
+ {
+ AppDomain.CurrentDomain.DomainUnload += OnDomainUnload;
+ string name = AppDomain.CurrentDomain.FriendlyName;
+ Console.WriteLine(string.Format("[{0} in .NET] In PythonRunner.RunPython", name));
+ PythonEngine.Initialize(mode: ShutdownMode.Reload);
+ using (Py.GIL())
+ {
+ try
+ {
+ var pyScript = string.Format("import clr\n"
+ + "print('[{0} in python] imported clr')\n"
+ + "clr.AddReference('System')\n"
+ + "print('[{0} in python] allocated a clr object')\n"
+ + "import gc\n"
+ + "gc.collect()\n"
+ + "print('[{0} in python] collected garbage')\n",
+ name);
+ PythonEngine.Exec(pyScript);
+ }
+ catch (Exception e)
+ {
+ Console.WriteLine(string.Format("[{0} in .NET] Caught exception: {1}", name, e));
+ }
+ }
+ PythonEngine.BeginAllowThreads();
+ }
+
+
+ private static IntPtr _state;
+
+ public static void InitPython(ShutdownMode mode)
+ {
+ PythonEngine.Initialize(mode: mode);
+ _state = PythonEngine.BeginAllowThreads();
+ }
+
+ public static void ShutdownPython()
+ {
+ PythonEngine.EndAllowThreads(_state);
+ PythonEngine.Shutdown();
+ }
+
+ public static void ShutdownPythonCompletely()
+ {
+ PythonEngine.EndAllowThreads(_state);
+ PythonEngine.ShutdownMode = ShutdownMode.Normal;
+ PythonEngine.Shutdown();
+ }
+
+ public static IntPtr GetTestObject()
+ {
+ try
+ {
+ Type type = typeof(Python.EmbeddingTest.Domain.MyClass);
+ string code = string.Format(@"
+import clr
+clr.AddReference('{0}')
+
+from Python.EmbeddingTest.Domain import MyClass
+obj = MyClass()
+obj.Method()
+obj.StaticMethod()
+", Assembly.GetExecutingAssembly().FullName);
+
+ using (Py.GIL())
+ using (var scope = Py.CreateScope())
+ {
+ scope.Exec(code);
+ using (PyObject obj = scope.Get("obj"))
+ {
+ Debug.Assert(obj.AsManagedObject(type).GetType() == type);
+ // We only needs its Python handle
+ XIncref(obj.Handle);
+ return obj.Handle;
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ Debug.WriteLine(e);
+ throw;
+ }
+ }
+
+ public static void RunTestObject(IntPtr handle)
+ {
+ using (Py.GIL())
+ {
+
+ using (PyObject obj = new PyObject(handle))
+ {
+ obj.InvokeMethod("Method");
+ obj.InvokeMethod("StaticMethod");
+ }
+ }
+ }
+
+ public static void ReleaseTestObject(IntPtr handle)
+ {
+ using (Py.GIL())
+ {
+ XDecref(handle);
+ }
+ }
+
+ static void OnDomainUnload(object sender, EventArgs e)
+ {
+ Console.WriteLine(string.Format("[{0} in .NET] unloading", AppDomain.CurrentDomain.FriendlyName));
+ }
+}
+
+
+namespace Python.EmbeddingTest.Domain
+{
+ [Serializable]
+ public class MyClass
+ {
+ public void Method() { }
+ public static void StaticMethod() { }
+ }
+}
diff --git a/src/embed_tests/Python.EmbeddingTest.15.csproj b/src/embed_tests/Python.EmbeddingTest.15.csproj
index 4f6b2de46..a55f32e53 100644
--- a/src/embed_tests/Python.EmbeddingTest.15.csproj
+++ b/src/embed_tests/Python.EmbeddingTest.15.csproj
@@ -65,9 +65,13 @@
$(DefineConstants)
+
+
+
+
diff --git a/src/embed_tests/TestDomainReload.cs b/src/embed_tests/TestDomainReload.cs
index d987ad30b..c1b7a8d91 100644
--- a/src/embed_tests/TestDomainReload.cs
+++ b/src/embed_tests/TestDomainReload.cs
@@ -1,5 +1,9 @@
using System;
using System.CodeDom.Compiler;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
using System.Reflection;
using NUnit.Framework;
using Python.Runtime;
@@ -68,46 +72,40 @@ public static void DomainReloadAndGC()
PythonEngine.Shutdown();
}
- //
- // The code we'll test. All that really matters is
- // using GIL { Python.Exec(pyScript); }
- // but the rest is useful for debugging.
- //
- // What matters in the python code is gc.collect and clr.AddReference.
- //
- // Note that the language version is 2.0, so no $"foo{bar}" syntax.
- //
- const string TestCode = @"
- using Python.Runtime;
- using System;
- class PythonRunner {
- public static void RunPython() {
- AppDomain.CurrentDomain.DomainUnload += OnDomainUnload;
- string name = AppDomain.CurrentDomain.FriendlyName;
- Console.WriteLine(string.Format(""[{0} in .NET] In PythonRunner.RunPython"", name));
- PythonEngine.Initialize(mode: ShutdownMode.Reload);
- using (Py.GIL()) {
- try {
- var pyScript = string.Format(""import clr\n""
- + ""print('[{0} in python] imported clr')\n""
- + ""clr.AddReference('System')\n""
- + ""print('[{0} in python] allocated a clr object')\n""
- + ""import gc\n""
- + ""gc.collect()\n""
- + ""print('[{0} in python] collected garbage')\n"",
- name);
- PythonEngine.Exec(pyScript);
- } catch(Exception e) {
- Console.WriteLine(string.Format(""[{0} in .NET] Caught exception: {1}"", name, e));
- }
- }
- PythonEngine.BeginAllowThreads();
- }
- static void OnDomainUnload(object sender, EventArgs e) {
- System.Console.WriteLine(string.Format(""[{0} in .NET] unloading"", AppDomain.CurrentDomain.FriendlyName));
- }
- }";
+ [Test]
+ public static void CrossDomainObject()
+ {
+ IntPtr handle;
+ Type type = typeof(Proxy);
+ Assembly assembly = BuildAssembly("test_domain_reload");
+ {
+ AppDomain domain = CreateDomain("test_domain_reload");
+ var theProxy = (Proxy)domain.CreateInstanceAndUnwrap(
+ type.Assembly.FullName,
+ type.FullName);
+ theProxy.InitAssembly(assembly.Location);
+ theProxy.Call("InitPython", ShutdownMode.Reload);
+ handle = (IntPtr)theProxy.Call("GetTestObject");
+ theProxy.Call("ShutdownPython");
+ AppDomain.Unload(domain);
+ }
+ {
+ AppDomain domain = CreateDomain("test_domain_reload");
+ var theProxy = (Proxy)domain.CreateInstanceAndUnwrap(
+ type.Assembly.FullName,
+ type.FullName);
+ theProxy.InitAssembly(assembly.Location);
+ theProxy.Call("InitPython", ShutdownMode.Reload);
+
+ // handle refering a clr object created in previous domain,
+ // it should had been deserialized and became callable agian.
+ theProxy.Call("RunTestObject", handle);
+ theProxy.Call("ShutdownPythonCompletely");
+ AppDomain.Unload(domain);
+ }
+ Assert.IsTrue(Runtime.Runtime.Py_IsInitialized() == 0);
+ }
///
/// Build an assembly out of the source code above.
@@ -119,15 +117,19 @@ static void OnDomainUnload(object sender, EventArgs e) {
static Assembly BuildAssembly(string assemblyName)
{
var provider = CodeDomProvider.CreateProvider("CSharp");
-
var compilerparams = new CompilerParameters();
- compilerparams.ReferencedAssemblies.Add("Python.Runtime.dll");
+ var assemblies = from assembly in AppDomain.CurrentDomain.GetAssemblies()
+ where !assembly.IsDynamic
+ select assembly.Location;
+ compilerparams.ReferencedAssemblies.AddRange(assemblies.ToArray());
+
compilerparams.GenerateExecutable = false;
compilerparams.GenerateInMemory = false;
- compilerparams.IncludeDebugInformation = false;
+ compilerparams.IncludeDebugInformation = true;
compilerparams.OutputAssembly = assemblyName;
- var results = provider.CompileAssemblyFromSource(compilerparams, TestCode);
+ var dir = Path.GetDirectoryName(new StackTrace(true).GetFrame(0).GetFileName());
+ var results = provider.CompileAssemblyFromFile(compilerparams, Path.Combine(dir, "DomainCode.cs"));
if (results.Errors.HasErrors)
{
var errors = new System.Text.StringBuilder("Compiler Errors:\n");
@@ -168,6 +170,13 @@ public void RunPython()
Console.WriteLine("[Proxy] Leaving RunPython");
}
+
+ public object Call(string methodName, params object[] args)
+ {
+ var pythonrunner = theAssembly.GetType("PythonRunner");
+ var method = pythonrunner.GetMethod(methodName);
+ return method.Invoke(null, args);
+ }
}
///
@@ -178,26 +187,11 @@ static void RunAssemblyAndUnload(Assembly assembly, string assemblyName)
{
Console.WriteLine($"[Program.Main] === creating domain for assembly {assembly.FullName}");
- // Create the domain. Make sure to set PrivateBinPath to a relative
- // path from the CWD (namely, 'bin').
- // See https://stackoverflow.com/questions/24760543/createinstanceandunwrap-in-another-domain
- var currentDomain = AppDomain.CurrentDomain;
- var domainsetup = new AppDomainSetup()
- {
- ApplicationBase = currentDomain.SetupInformation.ApplicationBase,
- ConfigurationFile = currentDomain.SetupInformation.ConfigurationFile,
- LoaderOptimization = LoaderOptimization.SingleDomain,
- PrivateBinPath = "."
- };
- var domain = AppDomain.CreateDomain(
- $"My Domain {assemblyName}",
- currentDomain.Evidence,
- domainsetup);
-
+ AppDomain domain = CreateDomain(assemblyName);
// Create a Proxy object in the new domain, where we want the
// assembly (and Python .NET) to reside
- Type type = typeof(Proxy);
System.IO.Directory.SetCurrentDirectory(AppDomain.CurrentDomain.BaseDirectory);
+ Type type = typeof(Proxy);
var theProxy = (Proxy)domain.CreateInstanceAndUnwrap(
type.Assembly.FullName,
type.FullName);
@@ -214,6 +208,7 @@ static void RunAssemblyAndUnload(Assembly assembly, string assemblyName)
try
{
Console.WriteLine($"[Program.Main] The Proxy object is valid ({theProxy}). Unexpected domain unload behavior");
+ Assert.Fail($"{theProxy} should be invlaid now");
}
catch (AppDomainUnloadedException)
{
@@ -221,6 +216,26 @@ static void RunAssemblyAndUnload(Assembly assembly, string assemblyName)
}
}
+ private static AppDomain CreateDomain(string name)
+ {
+ // Create the domain. Make sure to set PrivateBinPath to a relative
+ // path from the CWD (namely, 'bin').
+ // See https://stackoverflow.com/questions/24760543/createinstanceandunwrap-in-another-domain
+ var currentDomain = AppDomain.CurrentDomain;
+ var domainsetup = new AppDomainSetup()
+ {
+ ApplicationBase = currentDomain.SetupInformation.ApplicationBase,
+ ConfigurationFile = currentDomain.SetupInformation.ConfigurationFile,
+ LoaderOptimization = LoaderOptimization.SingleDomain,
+ PrivateBinPath = "."
+ };
+ var domain = AppDomain.CreateDomain(
+ $"My Domain {name}",
+ currentDomain.Evidence,
+ domainsetup);
+ return domain;
+ }
+
///
/// Resolves the assembly. Why doesn't this just work normally?
///
diff --git a/src/runtime/clrobject.cs b/src/runtime/clrobject.cs
index 7c4330219..b004eb23b 100644
--- a/src/runtime/clrobject.cs
+++ b/src/runtime/clrobject.cs
@@ -3,6 +3,7 @@
namespace Python.Runtime
{
+ [Serializable]
internal class CLRObject : ManagedType
{
internal object inst;
@@ -23,7 +24,7 @@ internal CLRObject(object ob, IntPtr tp)
}
}
- GCHandle gc = AllocGCHandle();
+ GCHandle gc = AllocGCHandle(TrackTypes.Wrapper);
Marshal.WriteIntPtr(py, ObjectOffset.magic(tp), (IntPtr)gc);
tpHandle = tp;
pyHandle = py;
@@ -68,5 +69,19 @@ internal static IntPtr GetInstHandle(object ob)
CLRObject co = GetInstance(ob);
return co.pyHandle;
}
+
+ protected override void OnSave()
+ {
+ base.OnSave();
+ Runtime.XIncref(pyHandle);
+ }
+
+ protected override void OnLoad()
+ {
+ base.OnLoad();
+ GCHandle gc = AllocGCHandle(TrackTypes.Wrapper);
+ Marshal.WriteIntPtr(pyHandle, ObjectOffset.magic(tpHandle), (IntPtr)gc);
+ Runtime.XDecref(pyHandle);
+ }
}
}
diff --git a/src/runtime/extensiontype.cs b/src/runtime/extensiontype.cs
index 006d616b2..fc839644e 100644
--- a/src/runtime/extensiontype.cs
+++ b/src/runtime/extensiontype.cs
@@ -29,7 +29,7 @@ public ExtensionType()
IntPtr py = Runtime.PyType_GenericAlloc(tp, 0);
- GCHandle gc = AllocGCHandle(true);
+ GCHandle gc = AllocGCHandle(TrackTypes.Extension);
Marshal.WriteIntPtr(py, ObjectOffset.magic(tp), (IntPtr)gc);
// We have to support gc because the type machinery makes it very
@@ -107,7 +107,7 @@ protected override void OnSave()
protected override void OnLoad()
{
base.OnLoad();
- GCHandle gc = AllocGCHandle(true);
+ GCHandle gc = AllocGCHandle(TrackTypes.Extension);
Marshal.WriteIntPtr(pyHandle, ObjectOffset.magic(tpHandle), (IntPtr)gc);
Runtime.PyObject_GC_UnTrack(pyHandle);
}
diff --git a/src/runtime/managedtype.cs b/src/runtime/managedtype.cs
index cd735898e..99e897570 100644
--- a/src/runtime/managedtype.cs
+++ b/src/runtime/managedtype.cs
@@ -14,13 +14,20 @@ namespace Python.Runtime
[Serializable]
internal abstract class ManagedType
{
+ internal enum TrackTypes
+ {
+ Untrack,
+ Extension,
+ Wrapper,
+ }
+
[NonSerialized]
internal GCHandle gcHandle; // Native handle
internal IntPtr pyHandle; // PyObject *
internal IntPtr tpHandle; // PyType *
- private static readonly HashSet _managedObjs = new HashSet();
+ private static readonly Dictionary _managedObjs = new Dictionary();
internal void IncrRefCount()
{
@@ -48,12 +55,12 @@ internal long RefCount
}
}
- internal GCHandle AllocGCHandle(bool track = false)
+ internal GCHandle AllocGCHandle(TrackTypes track = TrackTypes.Untrack)
{
gcHandle = GCHandle.Alloc(this);
- if (track)
+ if (track != TrackTypes.Untrack)
{
- _managedObjs.Add(this);
+ _managedObjs.Add(this, track);
}
return gcHandle;
}
@@ -64,7 +71,7 @@ internal void FreeGCHandle()
if (gcHandle.IsAllocated)
{
gcHandle.Free();
- gcHandle = new GCHandle();
+ gcHandle = default;
}
}
@@ -129,7 +136,7 @@ internal static bool IsManagedType(IntPtr ob)
return false;
}
- internal static ICollection GetManagedObjects()
+ internal static IDictionary GetManagedObjects()
{
return _managedObjs;
}
diff --git a/src/runtime/moduleobject.cs b/src/runtime/moduleobject.cs
index 0d42914e5..8661d74ae 100644
--- a/src/runtime/moduleobject.cs
+++ b/src/runtime/moduleobject.cs
@@ -345,15 +345,16 @@ public static int tp_clear(IntPtr ob)
protected override void OnSave()
{
base.OnSave();
+ System.Diagnostics.Debug.Assert(dict == GetObjectDict(pyHandle));
+ // Decref twice in tp_clear, equilibrate them.
+ Runtime.XIncref(dict);
Runtime.XIncref(dict);
- Runtime.XIncref(GetObjectDict(pyHandle));
}
protected override void OnLoad()
{
base.OnLoad();
cache = new Dictionary();
- Runtime.XIncref(dict);
SetObjectDict(pyHandle, dict);
}
}
diff --git a/src/runtime/pyobject.cs b/src/runtime/pyobject.cs
index 8ae99ecd0..58359e5c5 100644
--- a/src/runtime/pyobject.cs
+++ b/src/runtime/pyobject.cs
@@ -20,6 +20,7 @@ public interface IPyDisposable : IDisposable
/// PY3: https://docs.python.org/3/c-api/object.html
/// for details.
///
+ //[Serializable]
public class PyObject : DynamicObject, IEnumerable, IPyDisposable
{
#if TRACE_ALLOC
@@ -30,7 +31,10 @@ public class PyObject : DynamicObject, IEnumerable, IPyDisposable
#endif
protected internal IntPtr obj = IntPtr.Zero;
+
+ [NonSerialized]
private bool disposed = false;
+ [NonSerialized]
private bool _finalized = false;
///
diff --git a/src/runtime/pythonengine.cs b/src/runtime/pythonengine.cs
index 301eea44e..044377a6b 100644
--- a/src/runtime/pythonengine.cs
+++ b/src/runtime/pythonengine.cs
@@ -12,7 +12,12 @@ namespace Python.Runtime
///
public class PythonEngine : IDisposable
{
- public static ShutdownMode ShutdownMode => Runtime.ShutdownMode;
+ public static ShutdownMode ShutdownMode
+ {
+ get => Runtime.ShutdownMode;
+ set => Runtime.ShutdownMode = value;
+ }
+
private static DelegateManager delegateManager;
private static bool initialized;
@@ -187,7 +192,7 @@ public static void Initialize(IEnumerable args, bool setSysArgv = true,
AppDomain.CurrentDomain.DomainUnload += OnDomainUnload;
// Remember to shut down the runtime.
- AddShutdownHandler(() => Runtime.Shutdown(mode));
+ AddShutdownHandler(Runtime.Shutdown);
// The global scope gets used implicitly quite early on, remember
// to clear it out when we shut down.
diff --git a/src/runtime/runtime.cs b/src/runtime/runtime.cs
index 063ed6d99..9e6eef50d 100644
--- a/src/runtime/runtime.cs
+++ b/src/runtime/runtime.cs
@@ -126,7 +126,7 @@ public class Runtime
///
internal static readonly Encoding PyEncoding = _UCS == 2 ? Encoding.Unicode : Encoding.UTF32;
- public static ShutdownMode ShutdownMode { get; private set; }
+ public static ShutdownMode ShutdownMode { get; internal set; }
private static PyReferenceCollection _pyRefs = new PyReferenceCollection();
///
@@ -321,6 +321,7 @@ internal static void Initialize(bool initSigs = false, ShutdownMode mode = Shutd
if (mode == ShutdownMode.Reload && RuntimeData.HasStashData())
{
RuntimeData.StashPop();
+ RuntimeData.ClearStash();
}
else
#endif
@@ -341,7 +342,7 @@ internal static void Initialize(bool initSigs = false, ShutdownMode mode = Shutd
}
- internal static void Shutdown(ShutdownMode mode = ShutdownMode.Normal)
+ internal static void Shutdown()
{
if (Py_IsInitialized() == 0 || !_isInitialized)
{
@@ -351,6 +352,7 @@ internal static void Shutdown(ShutdownMode mode = ShutdownMode.Normal)
PyGILState_Ensure();
+ var mode = ShutdownMode;
#if !NETSTANDARD
if (mode == ShutdownMode.Reload)
{
@@ -468,16 +470,20 @@ private static void MoveClrInstancesOnwershipToPython()
{
var copyObjs = ManagedType.GetManagedObjects().ToArray();
var objs = ManagedType.GetManagedObjects();
- foreach (var obj in copyObjs)
+ foreach (var entry in copyObjs)
{
- if (!objs.Contains(obj))
+ ManagedType obj = entry.Key;
+ if (!objs.ContainsKey(obj))
{
continue;
}
- obj.CallTypeClear();
- // obj's tp_type will degenerate to a pure Python type after TypeManager.RemoveTypes(),
- // thus just be safe to give it back to GC chain.
- PyObject_GC_Track(obj.pyHandle);
+ if (entry.Value == ManagedType.TrackTypes.Extension)
+ {
+ obj.CallTypeClear();
+ // obj's tp_type will degenerate to a pure Python type after TypeManager.RemoveTypes(),
+ // thus just be safe to give it back to GC chain.
+ PyObject_GC_Track(obj.pyHandle);
+ }
if (obj.gcHandle.IsAllocated)
{
obj.gcHandle.Free();
diff --git a/src/runtime/runtime_state.cs b/src/runtime/runtime_state.cs
index d7f6f5558..ae01169ab 100644
--- a/src/runtime/runtime_state.cs
+++ b/src/runtime/runtime_state.cs
@@ -249,7 +249,7 @@ internal static void Stash()
TypeManager.StashPush(stack);
ClassManager.StashPush(stack);
var objs = ManagedType.GetManagedObjects();
- foreach (var obj in objs)
+ foreach (var obj in objs.Keys)
{
obj.Save();
}
@@ -291,14 +291,13 @@ internal static void StashPop()
var stack = (Stack)formatter.Deserialize(ms);
- var loadObjs = (ICollection)stack.Pop();
- var objs = ManagedType.GetManagedObjects();
- foreach (var obj in loadObjs)
+ var loadObjs = (IDictionary)stack.Pop();
+ foreach (var entry in loadObjs)
{
+ ManagedType obj = entry.Key;
obj.Load();
- objs.Add(obj);
}
-
+ Debug.Assert(ManagedType.GetManagedObjects().Count == loadObjs.Count);
ClassManager.StashPop(stack);
TypeManager.StashPop(stack);
PyCLRMetaType = MetaType.StashPop(stack);
@@ -309,5 +308,9 @@ public static bool HasStashData()
return PySys_GetObject("clr_data") != IntPtr.Zero;
}
+ public static void ClearStash()
+ {
+ PySys_SetObject("clr_data", IntPtr.Zero);
+ }
}
}
From 9874cd123393900364bf0f0f1c1a20af5dbf7763 Mon Sep 17 00:00:00 2001
From: amos402
Date: Mon, 10 Feb 2020 03:07:45 +0800
Subject: [PATCH 079/998] Add ShutdownMode.Default refer to normal mode if no
EnvironmentVariable control exists
---
src/embed_tests/DomainCode.cs | 18 +++++++++++++-----
src/embed_tests/TestRuntime.cs | 4 ++++
src/runtime/pythonengine.cs | 2 +-
src/runtime/runtime.cs | 27 +++++++++++++++++----------
4 files changed, 35 insertions(+), 16 deletions(-)
diff --git a/src/embed_tests/DomainCode.cs b/src/embed_tests/DomainCode.cs
index 0c694ce5f..48f4011d4 100644
--- a/src/embed_tests/DomainCode.cs
+++ b/src/embed_tests/DomainCode.cs
@@ -114,15 +114,23 @@ from Python.EmbeddingTest.Domain import MyClass
public static void RunTestObject(IntPtr handle)
{
- using (Py.GIL())
+ try
{
-
- using (PyObject obj = new PyObject(handle))
+ using (Py.GIL())
{
- obj.InvokeMethod("Method");
- obj.InvokeMethod("StaticMethod");
+
+ using (PyObject obj = new PyObject(handle))
+ {
+ obj.InvokeMethod("Method");
+ obj.InvokeMethod("StaticMethod");
+ }
}
}
+ catch (Exception e)
+ {
+ Debug.WriteLine(e);
+ throw;
+ }
}
public static void ReleaseTestObject(IntPtr handle)
diff --git a/src/embed_tests/TestRuntime.cs b/src/embed_tests/TestRuntime.cs
index fd02b4a82..d4f4b1c98 100644
--- a/src/embed_tests/TestRuntime.cs
+++ b/src/embed_tests/TestRuntime.cs
@@ -39,6 +39,10 @@ public static void PlatformCache()
[Test]
public static void Py_IsInitializedValue()
{
+ if (Runtime.Runtime.Py_IsInitialized() == 1)
+ {
+ Runtime.Runtime.PyGILState_Ensure();
+ }
Runtime.Runtime.Py_Finalize();
Assert.AreEqual(0, Runtime.Runtime.Py_IsInitialized());
Runtime.Runtime.Py_Initialize();
diff --git a/src/runtime/pythonengine.cs b/src/runtime/pythonengine.cs
index 044377a6b..05a31ff68 100644
--- a/src/runtime/pythonengine.cs
+++ b/src/runtime/pythonengine.cs
@@ -172,7 +172,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.Normal)
+ public static void Initialize(IEnumerable args, bool setSysArgv = true, bool initSigs = false, ShutdownMode mode = ShutdownMode.Default)
{
if (initialized)
{
diff --git a/src/runtime/runtime.cs b/src/runtime/runtime.cs
index 9e6eef50d..922e13b46 100644
--- a/src/runtime/runtime.cs
+++ b/src/runtime/runtime.cs
@@ -132,7 +132,7 @@ public class Runtime
///
/// Initialize the runtime...
///
- internal static void Initialize(bool initSigs = false, ShutdownMode mode = ShutdownMode.Normal)
+ internal static void Initialize(bool initSigs = false, ShutdownMode mode = ShutdownMode.Default)
{
if (_isInitialized)
{
@@ -140,17 +140,23 @@ internal static void Initialize(bool initSigs = false, ShutdownMode mode = Shutd
}
_isInitialized = true;
- if (Environment.GetEnvironmentVariable("PYTHONNET_SOFT_SHUTDOWN") == "1")
+ if (mode == ShutdownMode.Default)
{
- mode = ShutdownMode.Soft;
- }
-#if !NETSTANDARD
- else if (Environment.GetEnvironmentVariable("PYTHONNET_RELOAD_SHUTDOWN") == "1")
- {
- mode = ShutdownMode.Reload;
- }
+ if (Environment.GetEnvironmentVariable("PYTHONNET_SOFT_SHUTDOWN") == "1")
+ {
+ mode = ShutdownMode.Soft;
+ }
+ #if !NETSTANDARD
+ else if (Environment.GetEnvironmentVariable("PYTHONNET_RELOAD_SHUTDOWN") == "1")
+ {
+ mode = ShutdownMode.Reload;
+ }
#endif
- //mode = ShutdownMode.Reload;
+ else
+ {
+ mode = ShutdownMode.Normal;
+ }
+ }
ShutdownMode = mode;
if (Py_IsInitialized() == 0)
@@ -2196,6 +2202,7 @@ internal static IntPtr GetBuiltins()
public enum ShutdownMode
{
+ Default,
Normal,
Soft,
#if !NETSTANDARD
From 8da561b232456f9567ea7ab13ea89bab3f4586ae Mon Sep 17 00:00:00 2001
From: amos402
Date: Mon, 10 Feb 2020 03:20:48 +0800
Subject: [PATCH 080/998] Not generating the debug info for generated modules
---
src/embed_tests/TestDomainReload.cs | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/embed_tests/TestDomainReload.cs b/src/embed_tests/TestDomainReload.cs
index c1b7a8d91..4c2756d59 100644
--- a/src/embed_tests/TestDomainReload.cs
+++ b/src/embed_tests/TestDomainReload.cs
@@ -119,13 +119,13 @@ static Assembly BuildAssembly(string assemblyName)
var provider = CodeDomProvider.CreateProvider("CSharp");
var compilerparams = new CompilerParameters();
var assemblies = from assembly in AppDomain.CurrentDomain.GetAssemblies()
- where !assembly.IsDynamic
+ where !assembly.IsDynamic && !string.IsNullOrEmpty(assembly.Location)
select assembly.Location;
compilerparams.ReferencedAssemblies.AddRange(assemblies.ToArray());
compilerparams.GenerateExecutable = false;
compilerparams.GenerateInMemory = false;
- compilerparams.IncludeDebugInformation = true;
+ compilerparams.IncludeDebugInformation = false;
compilerparams.OutputAssembly = assemblyName;
var dir = Path.GetDirectoryName(new StackTrace(true).GetFrame(0).GetFileName());
From 9499c644af4f6111c24e19b3d378155d54e07295 Mon Sep 17 00:00:00 2001
From: amos402
Date: Tue, 11 Feb 2020 02:32:42 +0800
Subject: [PATCH 081/998] `TestDomainReload` use itself assembly instead of
dynamic creation
---
src/embed_tests/DomainCode.cs | 159 -----------------------
src/embed_tests/TestDomainReload.cs | 194 ++++++++++++++++++++++------
2 files changed, 157 insertions(+), 196 deletions(-)
delete mode 100644 src/embed_tests/DomainCode.cs
diff --git a/src/embed_tests/DomainCode.cs b/src/embed_tests/DomainCode.cs
deleted file mode 100644
index 48f4011d4..000000000
--- a/src/embed_tests/DomainCode.cs
+++ /dev/null
@@ -1,159 +0,0 @@
-using Python.Runtime;
-using System;
-using System.Diagnostics;
-using System.Reflection;
-
-//
-// The code we'll test. All that really matters is
-// using GIL { Python.Exec(pyScript); }
-// but the rest is useful for debugging.
-//
-// What matters in the python code is gc.collect and clr.AddReference.
-//
-// Note that the language version is 2.0, so no $"foo{bar}" syntax.
-//
-static class PythonRunner
-{
- static readonly Action XIncref;
- static readonly Action XDecref;
-
- static PythonRunner()
- {
- const BindingFlags flags = BindingFlags.Static | BindingFlags.NonPublic;
- MethodInfo incMethod = typeof(Runtime).GetMethod("XIncref", flags);
- MethodInfo decMethod = typeof(Runtime).GetMethod("XDecref", flags);
-
- XIncref = (Action)Delegate.CreateDelegate(typeof(Action), incMethod);
- XDecref = (Action)Delegate.CreateDelegate(typeof(Action), decMethod);
- }
-
- public static void RunPython()
- {
- AppDomain.CurrentDomain.DomainUnload += OnDomainUnload;
- string name = AppDomain.CurrentDomain.FriendlyName;
- Console.WriteLine(string.Format("[{0} in .NET] In PythonRunner.RunPython", name));
- PythonEngine.Initialize(mode: ShutdownMode.Reload);
- using (Py.GIL())
- {
- try
- {
- var pyScript = string.Format("import clr\n"
- + "print('[{0} in python] imported clr')\n"
- + "clr.AddReference('System')\n"
- + "print('[{0} in python] allocated a clr object')\n"
- + "import gc\n"
- + "gc.collect()\n"
- + "print('[{0} in python] collected garbage')\n",
- name);
- PythonEngine.Exec(pyScript);
- }
- catch (Exception e)
- {
- Console.WriteLine(string.Format("[{0} in .NET] Caught exception: {1}", name, e));
- }
- }
- PythonEngine.BeginAllowThreads();
- }
-
-
- private static IntPtr _state;
-
- public static void InitPython(ShutdownMode mode)
- {
- PythonEngine.Initialize(mode: mode);
- _state = PythonEngine.BeginAllowThreads();
- }
-
- public static void ShutdownPython()
- {
- PythonEngine.EndAllowThreads(_state);
- PythonEngine.Shutdown();
- }
-
- public static void ShutdownPythonCompletely()
- {
- PythonEngine.EndAllowThreads(_state);
- PythonEngine.ShutdownMode = ShutdownMode.Normal;
- PythonEngine.Shutdown();
- }
-
- public static IntPtr GetTestObject()
- {
- try
- {
- Type type = typeof(Python.EmbeddingTest.Domain.MyClass);
- string code = string.Format(@"
-import clr
-clr.AddReference('{0}')
-
-from Python.EmbeddingTest.Domain import MyClass
-obj = MyClass()
-obj.Method()
-obj.StaticMethod()
-", Assembly.GetExecutingAssembly().FullName);
-
- using (Py.GIL())
- using (var scope = Py.CreateScope())
- {
- scope.Exec(code);
- using (PyObject obj = scope.Get("obj"))
- {
- Debug.Assert(obj.AsManagedObject(type).GetType() == type);
- // We only needs its Python handle
- XIncref(obj.Handle);
- return obj.Handle;
- }
- }
- }
- catch (Exception e)
- {
- Debug.WriteLine(e);
- throw;
- }
- }
-
- public static void RunTestObject(IntPtr handle)
- {
- try
- {
- using (Py.GIL())
- {
-
- using (PyObject obj = new PyObject(handle))
- {
- obj.InvokeMethod("Method");
- obj.InvokeMethod("StaticMethod");
- }
- }
- }
- catch (Exception e)
- {
- Debug.WriteLine(e);
- throw;
- }
- }
-
- public static void ReleaseTestObject(IntPtr handle)
- {
- using (Py.GIL())
- {
- XDecref(handle);
- }
- }
-
- static void OnDomainUnload(object sender, EventArgs e)
- {
- Console.WriteLine(string.Format("[{0} in .NET] unloading", AppDomain.CurrentDomain.FriendlyName));
- }
-}
-
-
-namespace Python.EmbeddingTest.Domain
-{
- [Serializable]
- public class MyClass
- {
- public void Method() { }
- public static void StaticMethod() { }
- }
-}
diff --git a/src/embed_tests/TestDomainReload.cs b/src/embed_tests/TestDomainReload.cs
index 4c2756d59..044c90bf1 100644
--- a/src/embed_tests/TestDomainReload.cs
+++ b/src/embed_tests/TestDomainReload.cs
@@ -54,22 +54,12 @@ class TestDomainReload
[Test]
public static void DomainReloadAndGC()
{
- // We're set up to run in the directory that includes the bin directory.
- System.IO.Directory.SetCurrentDirectory(AppDomain.CurrentDomain.BaseDirectory);
-
- Assembly pythonRunner1 = BuildAssembly("test1");
- RunAssemblyAndUnload(pythonRunner1, "test1");
+ RunAssemblyAndUnload("test1");
Assert.That(Runtime.Runtime.Py_IsInitialized() != 0,
"On soft-shutdown mode, Python runtime should still running");
- // This caused a crash because objects allocated in pythonRunner1
- // still existed in memory, but the code to do python GC on those
- // objects is gone.
- Assembly pythonRunner2 = BuildAssembly("test2");
- RunAssemblyAndUnload(pythonRunner2, "test2");
-
- PythonEngine.Shutdown();
+ RunAssemblyAndUnload("test2");
}
[Test]
@@ -77,13 +67,11 @@ public static void CrossDomainObject()
{
IntPtr handle;
Type type = typeof(Proxy);
- Assembly assembly = BuildAssembly("test_domain_reload");
{
AppDomain domain = CreateDomain("test_domain_reload");
var theProxy = (Proxy)domain.CreateInstanceAndUnwrap(
type.Assembly.FullName,
type.FullName);
- theProxy.InitAssembly(assembly.Location);
theProxy.Call("InitPython", ShutdownMode.Reload);
handle = (IntPtr)theProxy.Call("GetTestObject");
theProxy.Call("ShutdownPython");
@@ -95,7 +83,6 @@ public static void CrossDomainObject()
var theProxy = (Proxy)domain.CreateInstanceAndUnwrap(
type.Assembly.FullName,
type.FullName);
- theProxy.InitAssembly(assembly.Location);
theProxy.Call("InitPython", ShutdownMode.Reload);
// handle refering a clr object created in previous domain,
@@ -129,7 +116,8 @@ static Assembly BuildAssembly(string assemblyName)
compilerparams.OutputAssembly = assemblyName;
var dir = Path.GetDirectoryName(new StackTrace(true).GetFrame(0).GetFileName());
- var results = provider.CompileAssemblyFromFile(compilerparams, Path.Combine(dir, "DomainCode.cs"));
+ string DomainCodePath = Path.Combine(dir, "DomainCode.cs");
+ var results = provider.CompileAssemblyFromFile(compilerparams, DomainCodePath);
if (results.Errors.HasErrors)
{
var errors = new System.Text.StringBuilder("Compiler Errors:\n");
@@ -152,28 +140,16 @@ static Assembly BuildAssembly(string assemblyName)
///
class Proxy : MarshalByRefObject
{
- Assembly theAssembly = null;
-
- public void InitAssembly(string assemblyPath)
- {
- theAssembly = Assembly.LoadFile(System.IO.Path.GetFullPath(assemblyPath));
- }
-
public void RunPython()
{
Console.WriteLine("[Proxy] Entering RunPython");
-
- // Call into the new assembly. Will execute Python code
- var pythonrunner = theAssembly.GetType("PythonRunner");
- var runPythonMethod = pythonrunner.GetMethod("RunPython");
- runPythonMethod.Invoke(null, new object[] { });
-
+ PythonRunner.RunPython();
Console.WriteLine("[Proxy] Leaving RunPython");
}
public object Call(string methodName, params object[] args)
{
- var pythonrunner = theAssembly.GetType("PythonRunner");
+ var pythonrunner = typeof(PythonRunner);
var method = pythonrunner.GetMethod(methodName);
return method.Invoke(null, args);
}
@@ -183,26 +159,24 @@ public object Call(string methodName, params object[] args)
/// Create a domain, run the assembly in it (the RunPython function),
/// and unload the domain.
///
- static void RunAssemblyAndUnload(Assembly assembly, string assemblyName)
+ static void RunAssemblyAndUnload(string domainName)
{
- Console.WriteLine($"[Program.Main] === creating domain for assembly {assembly.FullName}");
+ Console.WriteLine($"[Program.Main] === creating domain {domainName}");
- AppDomain domain = CreateDomain(assemblyName);
+ AppDomain domain = CreateDomain(domainName);
// Create a Proxy object in the new domain, where we want the
// assembly (and Python .NET) to reside
- System.IO.Directory.SetCurrentDirectory(AppDomain.CurrentDomain.BaseDirectory);
Type type = typeof(Proxy);
var theProxy = (Proxy)domain.CreateInstanceAndUnwrap(
type.Assembly.FullName,
type.FullName);
// From now on use the Proxy to call into the new assembly
- theProxy.InitAssembly(assemblyName);
theProxy.RunPython();
- Console.WriteLine($"[Program.Main] Before Domain Unload on {assembly.FullName}");
+ Console.WriteLine($"[Program.Main] Before Domain Unload on {domainName}");
AppDomain.Unload(domain);
- Console.WriteLine($"[Program.Main] After Domain Unload on {assembly.FullName}");
+ Console.WriteLine($"[Program.Main] After Domain Unload on {domainName}");
// Validate that the assembly does not exist anymore
try
@@ -254,5 +228,151 @@ static Assembly ResolveAssembly(object sender, ResolveEventArgs args)
return null;
}
}
+
+
+ //
+ // The code we'll test. All that really matters is
+ // using GIL { Python.Exec(pyScript); }
+ // but the rest is useful for debugging.
+ //
+ // What matters in the python code is gc.collect and clr.AddReference.
+ //
+ // Note that the language version is 2.0, so no $"foo{bar}" syntax.
+ //
+ static class PythonRunner
+ {
+ public static void RunPython()
+ {
+ AppDomain.CurrentDomain.DomainUnload += OnDomainUnload;
+ string name = AppDomain.CurrentDomain.FriendlyName;
+ Console.WriteLine(string.Format("[{0} in .NET] In PythonRunner.RunPython", name));
+ PythonEngine.Initialize(mode: ShutdownMode.Reload);
+ using (Py.GIL())
+ {
+ try
+ {
+ var pyScript = string.Format("import clr\n"
+ + "print('[{0} in python] imported clr')\n"
+ + "clr.AddReference('System')\n"
+ + "print('[{0} in python] allocated a clr object')\n"
+ + "import gc\n"
+ + "gc.collect()\n"
+ + "print('[{0} in python] collected garbage')\n",
+ name);
+ PythonEngine.Exec(pyScript);
+ }
+ catch (Exception e)
+ {
+ Console.WriteLine(string.Format("[{0} in .NET] Caught exception: {1}", name, e));
+ }
+ }
+ PythonEngine.BeginAllowThreads();
+ }
+
+
+ private static IntPtr _state;
+
+ public static void InitPython(ShutdownMode mode)
+ {
+ PythonEngine.Initialize(mode: mode);
+ _state = PythonEngine.BeginAllowThreads();
+ }
+
+ public static void ShutdownPython()
+ {
+ PythonEngine.EndAllowThreads(_state);
+ PythonEngine.Shutdown();
+ }
+
+ public static void ShutdownPythonCompletely()
+ {
+ PythonEngine.EndAllowThreads(_state);
+ PythonEngine.ShutdownMode = ShutdownMode.Normal;
+ PythonEngine.Shutdown();
+ }
+
+ public static IntPtr GetTestObject()
+ {
+ try
+ {
+ Type type = typeof(Python.EmbeddingTest.Domain.MyClass);
+ string code = string.Format(@"
+import clr
+clr.AddReference('{0}')
+
+from Python.EmbeddingTest.Domain import MyClass
+obj = MyClass()
+obj.Method()
+obj.StaticMethod()
+", Assembly.GetExecutingAssembly().FullName);
+
+ using (Py.GIL())
+ using (var scope = Py.CreateScope())
+ {
+ scope.Exec(code);
+ using (PyObject obj = scope.Get("obj"))
+ {
+ Debug.Assert(obj.AsManagedObject(type).GetType() == type);
+ // We only needs its Python handle
+ Runtime.Runtime.XIncref(obj.Handle);
+ return obj.Handle;
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ Debug.WriteLine(e);
+ throw;
+ }
+ }
+
+ public static void RunTestObject(IntPtr handle)
+ {
+ try
+ {
+ using (Py.GIL())
+ {
+
+ using (PyObject obj = new PyObject(handle))
+ {
+ obj.InvokeMethod("Method");
+ obj.InvokeMethod("StaticMethod");
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ Debug.WriteLine(e);
+ throw;
+ }
+ }
+
+ public static void ReleaseTestObject(IntPtr handle)
+ {
+ using (Py.GIL())
+ {
+ Runtime.Runtime.XDecref(handle);
+ }
+ }
+
+ static void OnDomainUnload(object sender, EventArgs e)
+ {
+ Console.WriteLine(string.Format("[{0} in .NET] unloading", AppDomain.CurrentDomain.FriendlyName));
+ }
+ }
+
}
+
+
+namespace Python.EmbeddingTest.Domain
+{
+ [Serializable]
+ public class MyClass
+ {
+ public void Method() { }
+ public static void StaticMethod() { }
+ }
+}
+
+
#endif
From 2b84394f00d0db083ea82b8ca6d1831d8e9e9b81 Mon Sep 17 00:00:00 2001
From: amos402
Date: Thu, 13 Feb 2020 01:16:20 +0800
Subject: [PATCH 082/998] * Fix refcnt error * Pick `SlotHelper` from #958
---
src/runtime/runtime.cs | 27 +++------------------------
src/runtime/typemanager.cs | 34 ++++++++++++++++++++++++++++++++++
2 files changed, 37 insertions(+), 24 deletions(-)
diff --git a/src/runtime/runtime.cs b/src/runtime/runtime.cs
index 5ead02378..7da70ddcd 100644
--- a/src/runtime/runtime.cs
+++ b/src/runtime/runtime.cs
@@ -340,30 +340,9 @@ internal static void Initialize(bool initSigs = false)
private static IntPtr Get_PyObject_NextNotImplemented()
{
- IntPtr globals = PyDict_New();
- if (PyDict_SetItemString(globals, "__builtins__", PyEval_GetBuiltins()) != 0)
- {
- XDecref(globals);
- throw new PythonException();
- }
- const string code = "class A(object): pass";
- IntPtr res = PyRun_String(code, (IntPtr)RunFlagType.File, globals, globals);
- if (res == IntPtr.Zero)
- {
- try
- {
- throw new PythonException();
- }
- finally
- {
- XDecref(globals);
- }
- }
- XDecref(res);
- IntPtr A = PyDict_GetItemString(globals, "A");
- IntPtr iternext = Marshal.ReadIntPtr(A, TypeOffset.tp_iternext);
- XDecref(globals);
- XDecref(A);
+ IntPtr pyType = SlotHelper.CreateObjectType();
+ IntPtr iternext = Marshal.ReadIntPtr(pyType, TypeOffset.tp_iternext);
+ Runtime.XDecref(pyType);
return iternext;
}
diff --git a/src/runtime/typemanager.cs b/src/runtime/typemanager.cs
index bb920b74f..c541f9dc2 100644
--- a/src/runtime/typemanager.cs
+++ b/src/runtime/typemanager.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections;
using System.Collections.Generic;
+using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
@@ -820,4 +821,37 @@ internal static void CopySlot(IntPtr from, IntPtr to, int offset)
Marshal.WriteIntPtr(to, offset, fp);
}
}
+
+
+ static class SlotHelper
+ {
+ public static IntPtr CreateObjectType()
+ {
+ IntPtr globals = Runtime.PyDict_New();
+ if (Runtime.PyDict_SetItemString(globals, "__builtins__", Runtime.PyEval_GetBuiltins()) != 0)
+ {
+ Runtime.XDecref(globals);
+ throw new PythonException();
+ }
+ const string code = "class A(object): pass";
+ IntPtr res = Runtime.PyRun_String(code, (IntPtr)RunFlagType.File, globals, globals);
+ if (res == IntPtr.Zero)
+ {
+ try
+ {
+ throw new PythonException();
+ }
+ finally
+ {
+ Runtime.XDecref(globals);
+ }
+ }
+ Runtime.XDecref(res);
+ IntPtr A = Runtime.PyDict_GetItemString(globals, "A");
+ Debug.Assert(A != IntPtr.Zero);
+ Runtime.XIncref(A);
+ Runtime.XDecref(globals);
+ return A;
+ }
+ }
}
From 5ade069d280c69986787ee118080ae7e3e7441b3 Mon Sep 17 00:00:00 2001
From: amos402
Date: Thu, 13 Feb 2020 01:56:56 +0800
Subject: [PATCH 083/998] Validate return value
---
src/runtime/platform/NativeCodePage.cs | 53 ++++++++++++++++----------
1 file changed, 33 insertions(+), 20 deletions(-)
diff --git a/src/runtime/platform/NativeCodePage.cs b/src/runtime/platform/NativeCodePage.cs
index 3f89e68ab..f4b595706 100644
--- a/src/runtime/platform/NativeCodePage.cs
+++ b/src/runtime/platform/NativeCodePage.cs
@@ -244,26 +244,39 @@ public void SetReadExec(IntPtr mappedMemory, int numBytes)
///
public static void InitializePlatformData()
{
- IntPtr op;
- IntPtr fn;
- IntPtr platformModule = Runtime.PyImport_ImportModule("platform");
- IntPtr emptyTuple = Runtime.PyTuple_New(0);
-
- fn = Runtime.PyObject_GetAttrString(platformModule, "system");
- op = Runtime.PyObject_Call(fn, emptyTuple, IntPtr.Zero);
- PythonException.ThrowIfIsNull(op);
- OperatingSystemName = Runtime.GetManagedString(op);
- Runtime.XDecref(op);
- Runtime.XDecref(fn);
-
- fn = Runtime.PyObject_GetAttrString(platformModule, "machine");
- op = Runtime.PyObject_Call(fn, emptyTuple, IntPtr.Zero);
- MachineName = Runtime.GetManagedString(op);
- Runtime.XDecref(op);
- Runtime.XDecref(fn);
-
- Runtime.XDecref(emptyTuple);
- Runtime.XDecref(platformModule);
+ IntPtr op = IntPtr.Zero;
+ IntPtr fn = IntPtr.Zero;
+ IntPtr platformModule = IntPtr.Zero;
+ IntPtr emptyTuple = IntPtr.Zero;
+ try
+ {
+ platformModule = Runtime.PyImport_ImportModule("platform");
+ PythonException.ThrowIfIsNull(platformModule);
+
+ fn = Runtime.PyObject_GetAttrString(platformModule, "system");
+ PythonException.ThrowIfIsNull(fn);
+
+ emptyTuple = Runtime.PyTuple_New(0);
+ op = Runtime.PyObject_Call(fn, emptyTuple, IntPtr.Zero);
+ PythonException.ThrowIfIsNull(op);
+
+ OperatingSystemName = Runtime.GetManagedString(op);
+
+ fn = Runtime.PyObject_GetAttrString(platformModule, "machine");
+ PythonException.ThrowIfIsNull(fn);
+
+ op = Runtime.PyObject_Call(fn, emptyTuple, IntPtr.Zero);
+ PythonException.ThrowIfIsNull(op);
+ MachineName = Runtime.GetManagedString(op);
+ }
+ finally
+ {
+ Runtime.XDecref(op);
+ Runtime.XDecref(fn);
+
+ Runtime.XDecref(emptyTuple);
+ Runtime.XDecref(platformModule);
+ }
// Now convert the strings into enum values so we can do switch
// statements rather than constant parsing.
From f4bb77a1d4c5d8386816203506e908450c90ae52 Mon Sep 17 00:00:00 2001
From: amos402
Date: Thu, 13 Feb 2020 11:26:00 +0800
Subject: [PATCH 084/998] * API for getting the default shutdown mode * Make
the reload test won't bother other tests * Clear obsolete code
---
.../Python.EmbeddingTest.15.csproj | 4 -
src/embed_tests/TestDomainReload.cs | 107 ++++++++----------
src/runtime/pythonengine.cs | 5 +-
src/runtime/runtime.cs | 34 +++---
4 files changed, 67 insertions(+), 83 deletions(-)
diff --git a/src/embed_tests/Python.EmbeddingTest.15.csproj b/src/embed_tests/Python.EmbeddingTest.15.csproj
index a55f32e53..4f6b2de46 100644
--- a/src/embed_tests/Python.EmbeddingTest.15.csproj
+++ b/src/embed_tests/Python.EmbeddingTest.15.csproj
@@ -65,13 +65,9 @@
$(DefineConstants)
-
-
-
-
diff --git a/src/embed_tests/TestDomainReload.cs b/src/embed_tests/TestDomainReload.cs
index 044c90bf1..96a1d4fce 100644
--- a/src/embed_tests/TestDomainReload.cs
+++ b/src/embed_tests/TestDomainReload.cs
@@ -1,9 +1,5 @@
using System;
-using System.CodeDom.Compiler;
-using System.Collections.Generic;
using System.Diagnostics;
-using System.IO;
-using System.Linq;
using System.Reflection;
using NUnit.Framework;
using Python.Runtime;
@@ -54,6 +50,7 @@ class TestDomainReload
[Test]
public static void DomainReloadAndGC()
{
+ Assert.IsFalse(PythonEngine.IsInitialized);
RunAssemblyAndUnload("test1");
Assert.That(Runtime.Runtime.Py_IsInitialized() != 0,
@@ -65,72 +62,47 @@ public static void DomainReloadAndGC()
[Test]
public static void CrossDomainObject()
{
- IntPtr handle;
+ IntPtr handle = IntPtr.Zero;
Type type = typeof(Proxy);
{
AppDomain domain = CreateDomain("test_domain_reload");
- var theProxy = (Proxy)domain.CreateInstanceAndUnwrap(
- type.Assembly.FullName,
- type.FullName);
- theProxy.Call("InitPython", ShutdownMode.Reload);
- handle = (IntPtr)theProxy.Call("GetTestObject");
- theProxy.Call("ShutdownPython");
- AppDomain.Unload(domain);
+ try
+ {
+ var theProxy = (Proxy)domain.CreateInstanceAndUnwrap(
+ type.Assembly.FullName,
+ type.FullName);
+ theProxy.Call("InitPython", ShutdownMode.Reload);
+ handle = (IntPtr)theProxy.Call("GetTestObject");
+ theProxy.Call("ShutdownPython");
+ }
+ finally
+ {
+ AppDomain.Unload(domain);
+ }
}
{
AppDomain domain = CreateDomain("test_domain_reload");
- var theProxy = (Proxy)domain.CreateInstanceAndUnwrap(
- type.Assembly.FullName,
- type.FullName);
- theProxy.Call("InitPython", ShutdownMode.Reload);
-
- // handle refering a clr object created in previous domain,
- // it should had been deserialized and became callable agian.
- theProxy.Call("RunTestObject", handle);
- theProxy.Call("ShutdownPythonCompletely");
- AppDomain.Unload(domain);
- }
- Assert.IsTrue(Runtime.Runtime.Py_IsInitialized() == 0);
- }
-
- ///
- /// Build an assembly out of the source code above.
- ///
- /// This creates a file .dll in order
- /// to support the statement "proxy.theAssembly = assembly" below.
- /// That statement needs a file, can't run via memory.
- ///
- static Assembly BuildAssembly(string assemblyName)
- {
- var provider = CodeDomProvider.CreateProvider("CSharp");
- var compilerparams = new CompilerParameters();
- var assemblies = from assembly in AppDomain.CurrentDomain.GetAssemblies()
- where !assembly.IsDynamic && !string.IsNullOrEmpty(assembly.Location)
- select assembly.Location;
- compilerparams.ReferencedAssemblies.AddRange(assemblies.ToArray());
-
- compilerparams.GenerateExecutable = false;
- compilerparams.GenerateInMemory = false;
- compilerparams.IncludeDebugInformation = false;
- compilerparams.OutputAssembly = assemblyName;
-
- var dir = Path.GetDirectoryName(new StackTrace(true).GetFrame(0).GetFileName());
- string DomainCodePath = Path.Combine(dir, "DomainCode.cs");
- var results = provider.CompileAssemblyFromFile(compilerparams, DomainCodePath);
- if (results.Errors.HasErrors)
- {
- var errors = new System.Text.StringBuilder("Compiler Errors:\n");
- foreach (CompilerError error in results.Errors)
+ try
+ {
+ var theProxy = (Proxy)domain.CreateInstanceAndUnwrap(
+ type.Assembly.FullName,
+ type.FullName);
+ theProxy.Call("InitPython", ShutdownMode.Reload);
+
+ // handle refering a clr object created in previous domain,
+ // it should had been deserialized and became callable agian.
+ theProxy.Call("RunTestObject", handle);
+ theProxy.Call("ShutdownPythonCompletely");
+ }
+ finally
{
- errors.AppendFormat("Line {0},{1}\t: {2}\n",
- error.Line, error.Column, error.ErrorText);
+ AppDomain.Unload(domain);
}
- throw new Exception(errors.ToString());
}
- else
+ if (PythonEngine.DefaultShutdownMode == ShutdownMode.Normal)
{
- return results.CompiledAssembly;
+ Assert.IsTrue(Runtime.Runtime.Py_IsInitialized() == 0);
}
}
@@ -246,7 +218,7 @@ public static void RunPython()
AppDomain.CurrentDomain.DomainUnload += OnDomainUnload;
string name = AppDomain.CurrentDomain.FriendlyName;
Console.WriteLine(string.Format("[{0} in .NET] In PythonRunner.RunPython", name));
- PythonEngine.Initialize(mode: ShutdownMode.Reload);
+ PythonEngine.Initialize();
using (Py.GIL())
{
try
@@ -287,8 +259,21 @@ public static void ShutdownPython()
public static void ShutdownPythonCompletely()
{
PythonEngine.EndAllowThreads(_state);
- PythonEngine.ShutdownMode = ShutdownMode.Normal;
+ // 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.
+ if (PythonEngine.DefaultShutdownMode != ShutdownMode.Reload)
+ {
+ PythonEngine.ShutdownMode = ShutdownMode.Soft;
+ }
PythonEngine.Shutdown();
+ if (PythonEngine.DefaultShutdownMode == ShutdownMode.Normal)
+ {
+ // Normal mode will shutdown the VM, so it needs to be shutdown
+ // for avoiding influence with other tests.
+ Runtime.Runtime.Shutdown();
+ }
}
public static IntPtr GetTestObject()
diff --git a/src/runtime/pythonengine.cs b/src/runtime/pythonengine.cs
index 05a31ff68..b18ce56c6 100644
--- a/src/runtime/pythonengine.cs
+++ b/src/runtime/pythonengine.cs
@@ -17,7 +17,8 @@ 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;
@@ -157,7 +158,7 @@ public static void Initialize()
Initialize(setSysArgv: true);
}
- public static void Initialize(bool setSysArgv = true, bool initSigs = false, ShutdownMode mode = ShutdownMode.Normal)
+ public static void Initialize(bool setSysArgv = true, bool initSigs = false, ShutdownMode mode = ShutdownMode.Default)
{
Initialize(Enumerable.Empty(), setSysArgv: setSysArgv, initSigs: initSigs, mode);
}
diff --git a/src/runtime/runtime.cs b/src/runtime/runtime.cs
index 6b70711fe..4b6f37ea8 100644
--- a/src/runtime/runtime.cs
+++ b/src/runtime/runtime.cs
@@ -142,23 +142,10 @@ internal static void Initialize(bool initSigs = false, ShutdownMode mode = Shutd
if (mode == ShutdownMode.Default)
{
- if (Environment.GetEnvironmentVariable("PYTHONNET_SOFT_SHUTDOWN") == "1")
- {
- mode = ShutdownMode.Soft;
- }
- #if !NETSTANDARD
- else if (Environment.GetEnvironmentVariable("PYTHONNET_RELOAD_SHUTDOWN") == "1")
- {
- mode = ShutdownMode.Reload;
- }
-#endif
- else
- {
- mode = ShutdownMode.Normal;
- }
+ mode = GetDefaultShutdownMode();
}
-
ShutdownMode = mode;
+
if (Py_IsInitialized() == 0)
{
Py_InitializeEx(initSigs ? 1 : 0);
@@ -408,6 +395,21 @@ internal static void Shutdown()
Py_Finalize();
}
+ internal static ShutdownMode GetDefaultShutdownMode()
+ {
+ if (Environment.GetEnvironmentVariable("PYTHONNET_SOFT_SHUTDOWN") == "1")
+ {
+ return ShutdownMode.Soft;
+ }
+#if !NETSTANDARD
+ else if (Environment.GetEnvironmentVariable("PYTHONNET_RELOAD_SHUTDOWN") == "1")
+ {
+ return ShutdownMode.Reload;
+ }
+#endif
+ return ShutdownMode.Normal;
+ }
+
// called *without* the GIL acquired by clr._AtExit
internal static int AtExit()
{
@@ -1984,7 +1986,7 @@ internal static IntPtr PyType_GenericAlloc(IntPtr type, long n)
private static extern IntPtr PyType_GenericAlloc(IntPtr type, IntPtr n);
///
- /// Finalize a type object. This should be called on all type objects to finish their initialization. This function is responsible for adding inherited slots from a types base class. Return 0 on success, or return -1 and sets an exception on error.
+ /// Finalize a type object. This should be called on all type objects to finish their initialization. This function is responsible for adding inherited slots from a type’s base class. Return 0 on success, or return -1 and sets an exception on error.
///
[DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)]
internal static extern int PyType_Ready(IntPtr type);
From 670bd745be9f13dd2e8fff044b2691641e15dd37 Mon Sep 17 00:00:00 2001
From: amos402
Date: Thu, 13 Feb 2020 11:36:56 +0800
Subject: [PATCH 085/998] In domain test, use soft mode if default mode is
normal
---
src/embed_tests/TestDomainReload.cs | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/src/embed_tests/TestDomainReload.cs b/src/embed_tests/TestDomainReload.cs
index 96a1d4fce..1637a0b1a 100644
--- a/src/embed_tests/TestDomainReload.cs
+++ b/src/embed_tests/TestDomainReload.cs
@@ -218,7 +218,12 @@ public static void RunPython()
AppDomain.CurrentDomain.DomainUnload += OnDomainUnload;
string name = AppDomain.CurrentDomain.FriendlyName;
Console.WriteLine(string.Format("[{0} in .NET] In PythonRunner.RunPython", name));
- PythonEngine.Initialize();
+ var mode = PythonEngine.DefaultShutdownMode;
+ if (mode == ShutdownMode.Normal)
+ {
+ mode = ShutdownMode.Soft;
+ }
+ PythonEngine.Initialize(mode: mode);
using (Py.GIL())
{
try
From 3cb56f16bdd6dc978520f630714bf3f24474132a Mon Sep 17 00:00:00 2001
From: amos402
Date: Thu, 13 Feb 2020 18:11:18 +0800
Subject: [PATCH 086/998] Avoid Domain tests influence other tests when the
default is not Soft or Reload
---
src/embed_tests/TestDomainReload.cs | 22 +++++++++++++---------
src/runtime/runtime.cs | 10 ++++++++--
src/runtime/runtime_state.cs | 2 +-
3 files changed, 22 insertions(+), 12 deletions(-)
diff --git a/src/embed_tests/TestDomainReload.cs b/src/embed_tests/TestDomainReload.cs
index 1637a0b1a..c94c8599c 100644
--- a/src/embed_tests/TestDomainReload.cs
+++ b/src/embed_tests/TestDomainReload.cs
@@ -52,11 +52,20 @@ public static void DomainReloadAndGC()
{
Assert.IsFalse(PythonEngine.IsInitialized);
RunAssemblyAndUnload("test1");
-
Assert.That(Runtime.Runtime.Py_IsInitialized() != 0,
"On soft-shutdown mode, Python runtime should still running");
RunAssemblyAndUnload("test2");
+ Assert.That(Runtime.Runtime.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.
+ Runtime.Runtime.PyGILState_Ensure();
+ Runtime.Runtime.Py_Finalize();
+ }
}
[Test]
@@ -268,17 +277,12 @@ public static void ShutdownPythonCompletely()
// 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.
- if (PythonEngine.DefaultShutdownMode != ShutdownMode.Reload)
+ var defaultMode = PythonEngine.DefaultShutdownMode;
+ if (defaultMode != ShutdownMode.Reload)
{
- PythonEngine.ShutdownMode = ShutdownMode.Soft;
+ PythonEngine.ShutdownMode = defaultMode;
}
PythonEngine.Shutdown();
- if (PythonEngine.DefaultShutdownMode == ShutdownMode.Normal)
- {
- // Normal mode will shutdown the VM, so it needs to be shutdown
- // for avoiding influence with other tests.
- Runtime.Runtime.Shutdown();
- }
}
public static IntPtr GetTestObject()
diff --git a/src/runtime/runtime.cs b/src/runtime/runtime.cs
index 4b6f37ea8..8a9453dd3 100644
--- a/src/runtime/runtime.cs
+++ b/src/runtime/runtime.cs
@@ -6,8 +6,6 @@
using System.Collections.Generic;
using Python.Runtime.Platform;
using System.Linq;
-using System.IO;
-using System.Runtime.Serialization.Formatters.Binary;
namespace Python.Runtime
{
@@ -157,6 +155,14 @@ internal static void Initialize(bool initSigs = false, ShutdownMode mode = Shutd
{
RuntimeState.Save();
}
+#if !NETSTANDARD
+ // XXX: Reload mode may reduct to Soft mode,
+ // so even on Reload mode it still needs to save the RuntimeState
+ else if (mode == ShutdownMode.Reload)
+ {
+ RuntimeState.Save();
+ }
+#endif
MainManagedThreadId = Thread.CurrentThread.ManagedThreadId;
}
else
diff --git a/src/runtime/runtime_state.cs b/src/runtime/runtime_state.cs
index ae01169ab..0ee192843 100644
--- a/src/runtime/runtime_state.cs
+++ b/src/runtime/runtime_state.cs
@@ -98,7 +98,7 @@ public static void Restore()
private static void ResotreModules(IntPtr dummyGC)
{
var intialModules = PySys_GetObject("initial_modules");
- Debug.Assert(intialModules != null);
+ Debug.Assert(intialModules != IntPtr.Zero);
var modules = PyImport_GetModuleDict();
foreach (var name in GetModuleNames())
{
From f5548e36288622d9acdd4502e91019bdd972f894 Mon Sep 17 00:00:00 2001
From: Victor
Date: Thu, 13 Feb 2020 05:05:57 -0800
Subject: [PATCH 087/998] CI for performance tests (#992)
* attempted to add performance tests to CI
* attempt to fix PerformanceTests xplat CI build
* enabling building PerformanceTests for Mono
* fixed AppVeyor path to Python.PerformanceTests.dll
* fixed Mono deb sources to bionic
* slightly relaxed perf target for WriteInt64Property
* PerformanceTests: explicitly specify platform
* use framework-specific build of perf tests in xplat and generic otherwise
* added perf tests run to Travis CI
* better error message for a failure to run benchmarks
* appveyor: don't run perf tests in unsupported configurations
* fixed performance test Python version condition in AppVeyor
* explicitly notify when performance tests are skipped in AppVeyor
* relax performance targets to ~10%, improve perf failure message
* switch to the release of Microsoft.NETFramework.ReferenceAssemblies package
---
.travis.yml | 6 +-
ci/appveyor_run_tests.ps1 | 32 ++++-
pythonnet.15.sln | 120 +++++++++++-------
setup.py | 10 ++
src/perf_tests/BenchmarkTests.cs | 17 ++-
src/perf_tests/Python.PerformanceTests.csproj | 9 +-
6 files changed, 136 insertions(+), 58 deletions(-)
diff --git a/.travis.yml b/.travis.yml
index 9689c0422..c69ccbc5d 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -9,8 +9,8 @@ python:
env:
matrix:
- - BUILD_OPTS=--xplat NUNIT_PATH="~/.nuget/packages/nunit.consolerunner/3.*/tools/nunit3-console.exe" RUN_TESTS=dotnet EMBED_TESTS_PATH=netcoreapp2.0_publish/
- - BUILD_OPTS="" NUNIT_PATH="./packages/NUnit.*/tools/nunit3-console.exe" RUN_TESTS="mono $NUNIT_PATH" EMBED_TESTS_PATH=""
+ - BUILD_OPTS=--xplat NUNIT_PATH="~/.nuget/packages/nunit.consolerunner/3.*/tools/nunit3-console.exe" RUN_TESTS=dotnet EMBED_TESTS_PATH=netcoreapp2.0_publish/ PERF_TESTS_PATH=net461/
+ - BUILD_OPTS="" NUNIT_PATH="./packages/NUnit.*/tools/nunit3-console.exe" RUN_TESTS="mono $NUNIT_PATH" EMBED_TESTS_PATH="" PERF_TESTS_PATH=""
global:
- LD_PRELOAD=/lib/x86_64-linux-gnu/libSegFault.so
@@ -45,6 +45,8 @@ install:
script:
- python -m pytest
- $RUN_TESTS src/embed_tests/bin/$EMBED_TESTS_PATH/Python.EmbeddingTest.dll
+ # does not work on Linux, because NuGet package for 2.3 is Windows only
+ # - "if [[ $TRAVIS_PYTHON_VERSION == '3.5' && $PERF_TESTS_PATH != '' ]]; then mono $NUNIT_PATH src/perf_tests/bin/$PERF_TESTS_PATH/Python.PerformanceTests.dll; fi"
after_script:
# Waiting on mono-coverage, SharpCover or xr.Baboon
diff --git a/ci/appveyor_run_tests.ps1 b/ci/appveyor_run_tests.ps1
index b45440fbe..f94cfb11e 100644
--- a/ci/appveyor_run_tests.ps1
+++ b/ci/appveyor_run_tests.ps1
@@ -3,6 +3,8 @@
# Test Runner framework being used for embedded tests
$CS_RUNNER = "nunit3-console"
+$XPLAT = $env:BUILD_OPTS -eq "--xplat"
+
# Needed for ARCH specific runners(NUnit2/XUnit3). Skip for NUnit3
if ($FALSE -and $env:PLATFORM -eq "x86"){
$CS_RUNNER = $CS_RUNNER + "-x86"
@@ -11,7 +13,7 @@ if ($FALSE -and $env:PLATFORM -eq "x86"){
# Executable paths for OpenCover
# Note if OpenCover fails, it won't affect the exit codes.
$OPENCOVER = Resolve-Path .\packages\OpenCover.*\tools\OpenCover.Console.exe
-if ($env:BUILD_OPTS -eq "--xplat"){
+if ($XPLAT){
$CS_RUNNER = Resolve-Path $env:USERPROFILE\.nuget\packages\nunit.consolerunner\*\tools\"$CS_RUNNER".exe
}
else{
@@ -42,9 +44,31 @@ Write-Host ("Starting embedded tests") -ForegroundColor "Green"
$CS_STATUS = $LastExitCode
if ($CS_STATUS -ne 0) {
Write-Host "Embedded tests failed" -ForegroundColor "Red"
+} else {
+ # NuGet for pythonnet-2.3 only has 64-bit binary for Python 3.5
+ # the test is only built using modern stack
+ if (($env:PLATFORM -eq "x64") -and ($XPLAT) -and ($env:PYTHON_VERSION -eq "3.5")) {
+ # Run C# Performance tests
+ Write-Host ("Starting performance tests") -ForegroundColor "Green"
+ if ($XPLAT) {
+ $CS_PERF_TESTS = ".\src\perf_tests\bin\net461\Python.PerformanceTests.dll"
+ }
+ else {
+ $CS_PERF_TESTS = ".\src\perf_tests\bin\Python.PerformanceTests.dll"
+ }
+ &"$CS_RUNNER" "$CS_PERF_TESTS"
+ $CS_PERF_STATUS = $LastExitCode
+ if ($CS_PERF_STATUS -ne 0) {
+ Write-Host "Performance tests (C#) failed" -ForegroundColor "Red"
+ }
+ } else {
+ Write-Host ("Skipping performance tests for ", $env:PYTHON_VERSION) -ForegroundColor "Yellow"
+ Write-Host ("on platform ", $env:PLATFORM, " xplat: ", $XPLAT) -ForegroundColor "Yellow"
+ $CS_PERF_STATUS = 0
+ }
}
-if ($env:BUILD_OPTS -eq "--xplat"){
+if ($XPLAT){
if ($env:PLATFORM -eq "x64") {
$DOTNET_CMD = "dotnet"
}
@@ -54,7 +78,7 @@ if ($env:BUILD_OPTS -eq "--xplat"){
# Run Embedded tests for netcoreapp2.0 (OpenCover currently does not supports dotnet core)
Write-Host ("Starting embedded tests for netcoreapp2.0") -ForegroundColor "Green"
- &$DOTNET_CMD .\src\embed_tests\bin\netcoreapp2.0_publish\Python.EmbeddingTest.dll
+ &$DOTNET_CMD ".\src\embed_tests\bin\netcoreapp2.0_publish\Python.EmbeddingTest.dll"
$CS_STATUS = $LastExitCode
if ($CS_STATUS -ne 0) {
Write-Host "Embedded tests for netcoreapp2.0 failed" -ForegroundColor "Red"
@@ -62,7 +86,7 @@ if ($env:BUILD_OPTS -eq "--xplat"){
}
# Set exit code to fail if either Python or Embedded tests failed
-if ($PYTHON_STATUS -ne 0 -or $CS_STATUS -ne 0) {
+if ($PYTHON_STATUS -ne 0 -or $CS_STATUS -ne 0 -or $CS_PERF_STATUS -ne 0) {
Write-Host "Tests failed" -ForegroundColor "Red"
$host.SetShouldExit(1)
}
diff --git a/pythonnet.15.sln b/pythonnet.15.sln
index 096dfbe9a..6d1f4fcd9 100644
--- a/pythonnet.15.sln
+++ b/pythonnet.15.sln
@@ -19,6 +19,26 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Repo", "Repo", "{441A0123-F
.editorconfig = .editorconfig
EndProjectSection
EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "CI", "CI", "{D301657F-5EAF-4534-B280-B858D651B2E5}"
+ ProjectSection(SolutionItems) = preProject
+ .travis.yml = .travis.yml
+ appveyor.yml = appveyor.yml
+ ci\appveyor_build_recipe.ps1 = ci\appveyor_build_recipe.ps1
+ ci\appveyor_run_tests.ps1 = ci\appveyor_run_tests.ps1
+ EndProjectSection
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{57F5D701-F265-4736-A5A2-07249E7A4DA3}"
+ ProjectSection(SolutionItems) = preProject
+ setup.py = setup.py
+ EndProjectSection
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "conda.recipe", "conda.recipe", "{7FD2404D-0CE8-4645-8DFB-766470E2150E}"
+ ProjectSection(SolutionItems) = preProject
+ conda.recipe\bld.bat = conda.recipe\bld.bat
+ conda.recipe\meta.yaml = conda.recipe\meta.yaml
+ conda.recipe\README.md = conda.recipe\README.md
+ EndProjectSection
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -313,54 +333,58 @@ Global
{F94B547A-E97E-4500-8D53-B4D64D076E5F}.ReleaseWinPY3|x64.Build.0 = ReleaseWinPY3|x64
{F94B547A-E97E-4500-8D53-B4D64D076E5F}.ReleaseWinPY3|x86.ActiveCfg = ReleaseWinPY3|x86
{F94B547A-E97E-4500-8D53-B4D64D076E5F}.ReleaseWinPY3|x86.Build.0 = ReleaseWinPY3|x86
- {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.Debug|Any CPU.ActiveCfg = DebugMono|Any CPU
- {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.Debug|Any CPU.Build.0 = DebugMono|Any CPU
- {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.Debug|x64.ActiveCfg = DebugMono|Any CPU
- {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.Debug|x64.Build.0 = DebugMono|Any CPU
- {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.Debug|x86.ActiveCfg = DebugMono|Any CPU
- {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.Debug|x86.Build.0 = DebugMono|Any CPU
- {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.DebugMono|Any CPU.ActiveCfg = DebugMono|Any CPU
- {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.DebugMono|x64.ActiveCfg = DebugMono|Any CPU
- {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.DebugMono|x86.ActiveCfg = DebugMono|Any CPU
- {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.DebugMonoPY3|Any CPU.ActiveCfg = DebugMonoPY3|Any CPU
- {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.DebugMonoPY3|x64.ActiveCfg = DebugMonoPY3|Any CPU
- {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.DebugMonoPY3|x86.ActiveCfg = DebugMonoPY3|Any CPU
- {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.DebugWin|Any CPU.ActiveCfg = DebugWin|Any CPU
- {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.DebugWin|Any CPU.Build.0 = DebugWin|Any CPU
- {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.DebugWin|x64.ActiveCfg = DebugWin|Any CPU
- {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.DebugWin|x64.Build.0 = DebugWin|Any CPU
- {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.DebugWin|x86.ActiveCfg = DebugWin|Any CPU
- {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.DebugWin|x86.Build.0 = DebugWin|Any CPU
- {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.DebugWinPY3|Any CPU.ActiveCfg = DebugWinPY3|Any CPU
- {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.DebugWinPY3|Any CPU.Build.0 = DebugWinPY3|Any CPU
- {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.DebugWinPY3|x64.ActiveCfg = DebugWinPY3|Any CPU
- {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.DebugWinPY3|x64.Build.0 = DebugWinPY3|Any CPU
- {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.DebugWinPY3|x86.ActiveCfg = DebugWinPY3|Any CPU
- {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.DebugWinPY3|x86.Build.0 = DebugWinPY3|Any CPU
- {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.Release|Any CPU.ActiveCfg = ReleaseMono|Any CPU
- {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.Release|Any CPU.Build.0 = ReleaseMono|Any CPU
- {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.Release|x64.ActiveCfg = ReleaseMono|Any CPU
- {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.Release|x64.Build.0 = ReleaseMono|Any CPU
- {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.Release|x86.ActiveCfg = ReleaseMono|Any CPU
- {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.Release|x86.Build.0 = ReleaseMono|Any CPU
- {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.ReleaseMono|Any CPU.ActiveCfg = ReleaseMono|Any CPU
- {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.ReleaseMono|x64.ActiveCfg = ReleaseMono|Any CPU
- {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.ReleaseMono|x86.ActiveCfg = ReleaseMono|Any CPU
- {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.ReleaseMonoPY3|Any CPU.ActiveCfg = ReleaseMonoPY3|Any CPU
- {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.ReleaseMonoPY3|x64.ActiveCfg = ReleaseMonoPY3|Any CPU
- {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.ReleaseMonoPY3|x86.ActiveCfg = ReleaseMonoPY3|Any CPU
- {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.ReleaseWin|Any CPU.ActiveCfg = ReleaseWin|Any CPU
- {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.ReleaseWin|Any CPU.Build.0 = ReleaseWin|Any CPU
- {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.ReleaseWin|x64.ActiveCfg = ReleaseWin|Any CPU
- {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.ReleaseWin|x64.Build.0 = ReleaseWin|Any CPU
- {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.ReleaseWin|x86.ActiveCfg = ReleaseWin|Any CPU
- {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.ReleaseWin|x86.Build.0 = ReleaseWin|Any CPU
- {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.ReleaseWinPY3|Any CPU.ActiveCfg = ReleaseWinPY3|Any CPU
- {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.ReleaseWinPY3|Any CPU.Build.0 = ReleaseWinPY3|Any CPU
- {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.ReleaseWinPY3|x64.ActiveCfg = ReleaseWinPY3|Any CPU
- {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.ReleaseWinPY3|x64.Build.0 = ReleaseWinPY3|Any CPU
- {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.ReleaseWinPY3|x86.ActiveCfg = ReleaseWinPY3|Any CPU
- {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.ReleaseWinPY3|x86.Build.0 = ReleaseWinPY3|Any CPU
+ {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.Debug|Any CPU.ActiveCfg = ReleaseWin|x86
+ {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.Debug|Any CPU.Build.0 = ReleaseWin|x86
+ {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.Debug|x64.ActiveCfg = DebugWinPY3|x64
+ {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.Debug|x64.Build.0 = DebugWinPY3|x64
+ {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.Debug|x86.ActiveCfg = DebugWinPY3|x86
+ {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.Debug|x86.Build.0 = DebugWinPY3|x86
+ {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.DebugMono|Any CPU.ActiveCfg = DebugMono|x86
+ {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.DebugMono|x64.ActiveCfg = DebugMono|x64
+ {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.DebugMono|x64.Build.0 = DebugMono|x64
+ {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.DebugMono|x86.ActiveCfg = DebugMono|x86
+ {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.DebugMono|x86.Build.0 = DebugMono|x86
+ {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.DebugMonoPY3|Any CPU.ActiveCfg = DebugMonoPY3|x86
+ {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.DebugMonoPY3|x64.ActiveCfg = DebugMonoPY3|x64
+ {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.DebugMonoPY3|x64.Build.0 = DebugMonoPY3|x64
+ {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.DebugMonoPY3|x86.ActiveCfg = DebugMonoPY3|x86
+ {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.DebugMonoPY3|x86.Build.0 = DebugMonoPY3|x86
+ {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.DebugWin|Any CPU.ActiveCfg = DebugWin|x86
+ {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.DebugWin|x64.ActiveCfg = DebugWin|x64
+ {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.DebugWin|x64.Build.0 = DebugWin|x64
+ {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.DebugWin|x86.ActiveCfg = DebugWin|x86
+ {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.DebugWin|x86.Build.0 = DebugWin|x86
+ {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.DebugWinPY3|Any CPU.ActiveCfg = DebugWinPY3|x86
+ {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.DebugWinPY3|x64.ActiveCfg = DebugWinPY3|x64
+ {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.DebugWinPY3|x64.Build.0 = DebugWinPY3|x64
+ {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.DebugWinPY3|x86.ActiveCfg = DebugWinPY3|x86
+ {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.DebugWinPY3|x86.Build.0 = DebugWinPY3|x86
+ {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.Release|Any CPU.ActiveCfg = ReleaseWin|x86
+ {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.Release|Any CPU.Build.0 = ReleaseWin|x86
+ {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.Release|x64.ActiveCfg = ReleaseWinPY3|x64
+ {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.Release|x64.Build.0 = ReleaseWinPY3|x64
+ {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.Release|x86.ActiveCfg = ReleaseWinPY3|x86
+ {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.Release|x86.Build.0 = ReleaseWinPY3|x86
+ {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.ReleaseMono|Any CPU.ActiveCfg = ReleaseMono|x86
+ {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.ReleaseMono|x64.ActiveCfg = ReleaseMono|x64
+ {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.ReleaseMono|x64.Build.0 = ReleaseMono|x64
+ {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.ReleaseMono|x86.ActiveCfg = ReleaseMono|x86
+ {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.ReleaseMono|x86.Build.0 = ReleaseMono|x86
+ {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.ReleaseMonoPY3|Any CPU.ActiveCfg = ReleaseMonoPY3|x86
+ {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.ReleaseMonoPY3|x64.ActiveCfg = ReleaseMonoPY3|x64
+ {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.ReleaseMonoPY3|x64.Build.0 = ReleaseMonoPY3|x64
+ {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.ReleaseMonoPY3|x86.ActiveCfg = ReleaseMonoPY3|x86
+ {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.ReleaseMonoPY3|x86.Build.0 = ReleaseMonoPY3|x86
+ {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.ReleaseWin|Any CPU.ActiveCfg = ReleaseWin|x86
+ {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.ReleaseWin|x64.ActiveCfg = ReleaseWin|x64
+ {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.ReleaseWin|x64.Build.0 = ReleaseWin|x64
+ {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.ReleaseWin|x86.ActiveCfg = ReleaseWin|x86
+ {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.ReleaseWin|x86.Build.0 = ReleaseWin|x86
+ {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.ReleaseWinPY3|Any CPU.ActiveCfg = ReleaseWinPY3|x86
+ {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.ReleaseWinPY3|x64.ActiveCfg = ReleaseWinPY3|x64
+ {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.ReleaseWinPY3|x64.Build.0 = ReleaseWinPY3|x64
+ {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.ReleaseWinPY3|x86.ActiveCfg = ReleaseWinPY3|x86
+ {6FB0D091-9CEC-4DCC-8701-C40F9BFC9EDE}.ReleaseWinPY3|x86.Build.0 = ReleaseWinPY3|x86
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/setup.py b/setup.py
index c6e4007e6..db2b4ae68 100644
--- a/setup.py
+++ b/setup.py
@@ -362,6 +362,16 @@ def build_extension(self, ext):
),
shell=use_shell,
)
+ subprocess.check_call(
+ " ".join(
+ cmd
+ + [
+ '"/t:Python_PerformanceTests:publish"',
+ "/p:TargetFramework=net461",
+ ]
+ ),
+ shell=use_shell,
+ )
if DEVTOOLS == "Mono" or DEVTOOLS == "dotnet":
self._build_monoclr()
diff --git a/src/perf_tests/BenchmarkTests.cs b/src/perf_tests/BenchmarkTests.cs
index 12ba6c900..baa825ca8 100644
--- a/src/perf_tests/BenchmarkTests.cs
+++ b/src/perf_tests/BenchmarkTests.cs
@@ -21,21 +21,23 @@ public void SetUp()
Environment.CurrentDirectory = Path.Combine(DeploymentRoot, "new");
this.summary = BenchmarkRunner.Run();
Assert.IsNotEmpty(this.summary.Reports);
- Assert.IsTrue(this.summary.Reports.All(r => r.Success));
+ Assert.IsTrue(
+ condition: this.summary.Reports.All(r => r.Success),
+ message: "BenchmarkDotNet failed to execute or collect results of performance tests. See logs above.");
}
[Test]
public void ReadInt64Property()
{
double optimisticPerfRatio = GetOptimisticPerfRatio(this.summary.Reports);
- Assert.LessOrEqual(optimisticPerfRatio, 0.68);
+ AssertPerformanceIsBetterOrSame(optimisticPerfRatio, target: 0.66);
}
[Test]
public void WriteInt64Property()
{
double optimisticPerfRatio = GetOptimisticPerfRatio(this.summary.Reports);
- Assert.LessOrEqual(optimisticPerfRatio, 0.66);
+ AssertPerformanceIsBetterOrSame(optimisticPerfRatio, target: 0.64);
}
static double GetOptimisticPerfRatio(
@@ -59,5 +61,14 @@ static double GetOptimisticPerfRatio(
}
public static string DeploymentRoot => Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
+
+ public static void AssertPerformanceIsBetterOrSame(
+ double actual, double target,
+ double wiggleRoom = 1.1, [CallerMemberName] string testName = null) {
+ double threshold = target * wiggleRoom;
+ Assert.LessOrEqual(actual, threshold,
+ $"{testName}: {actual:F3} > {threshold:F3} (target: {target:F3})"
+ + ": perf result is higher than the failure threshold.");
+ }
}
}
diff --git a/src/perf_tests/Python.PerformanceTests.csproj b/src/perf_tests/Python.PerformanceTests.csproj
index 33949fdc1..1231cef69 100644
--- a/src/perf_tests/Python.PerformanceTests.csproj
+++ b/src/perf_tests/Python.PerformanceTests.csproj
@@ -1,14 +1,21 @@
-
+
net461
DebugMono;DebugMonoPY3;ReleaseMono;ReleaseMonoPY3;DebugWin;DebugWinPY3;ReleaseWin;ReleaseWinPY3
+ bin\
false
+
+ x64;x86
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
From 3c9a83c13a839ca66723ef14039f9184b0d0e532 Mon Sep 17 00:00:00 2001
From: amos402
Date: Thu, 13 Feb 2020 21:24:56 +0800
Subject: [PATCH 088/998] Skip non-serializable objects
---
src/runtime/runtime_state.cs | 17 ++++++++++++++---
1 file changed, 14 insertions(+), 3 deletions(-)
diff --git a/src/runtime/runtime_state.cs b/src/runtime/runtime_state.cs
index 0ee192843..bf7850d01 100644
--- a/src/runtime/runtime_state.cs
+++ b/src/runtime/runtime_state.cs
@@ -249,16 +249,27 @@ internal static void Stash()
TypeManager.StashPush(stack);
ClassManager.StashPush(stack);
var objs = ManagedType.GetManagedObjects();
- foreach (var obj in objs.Keys)
+ var saveObjs = new Dictionary();
+ foreach (var entry in objs)
{
+ var obj = entry.Key;
+ if (entry.Value == ManagedType.TrackTypes.Wrapper
+ && !obj.GetType().IsSerializable)
+ {
+ // XXX: Skip non-serializable objects,
+ // use them after next initialization will raise exceptions.
+ continue;
+ }
+ Debug.Assert(obj.GetType().IsSerializable);
obj.Save();
+ saveObjs.Add(entry.Key, entry.Value);
}
- stack.Push(objs);
+ stack.Push(saveObjs);
formatter.Serialize(ms, stack);
byte[] data = ms.GetBuffer();
// TODO: use buffer api instead
- System.Diagnostics.Debug.Assert(data.Length <= int.MaxValue);
+ Debug.Assert(data.Length <= int.MaxValue);
IntPtr mem = PyMem_Malloc(data.LongLength + IntPtr.Size);
Marshal.WriteIntPtr(mem, (IntPtr)data.LongLength);
Marshal.Copy(data, 0, mem + IntPtr.Size, data.Length);
From df84e290fef15f289149231fa35dd47eac511e47 Mon Sep 17 00:00:00 2001
From: amos402
Date: Mon, 17 Feb 2020 10:46:59 +0800
Subject: [PATCH 089/998] * Reset Exceptions to IntPtr.Zero * Cleanup the code
---
src/runtime/exceptions.cs | 6 ++++--
src/runtime/interop.cs | 13 +++----------
src/runtime/runtime.cs | 5 +++--
3 files changed, 10 insertions(+), 14 deletions(-)
diff --git a/src/runtime/exceptions.cs b/src/runtime/exceptions.cs
index c1edaced2..908de9745 100644
--- a/src/runtime/exceptions.cs
+++ b/src/runtime/exceptions.cs
@@ -140,10 +140,12 @@ internal static void Shutdown()
foreach (FieldInfo fi in type.GetFields(BindingFlags.Public | BindingFlags.Static))
{
var op = (IntPtr)fi.GetValue(type);
- if (op != IntPtr.Zero)
+ if (op == IntPtr.Zero)
{
- Runtime.XDecref(op);
+ continue;
}
+ Runtime.XDecref(op);
+ fi.SetValue(null, IntPtr.Zero);
}
Runtime.Py_CLEAR(ref exceptions_module);
Runtime.Py_CLEAR(ref warnings_module);
diff --git a/src/runtime/interop.cs b/src/runtime/interop.cs
index 58464ec7f..58ec82415 100644
--- a/src/runtime/interop.cs
+++ b/src/runtime/interop.cs
@@ -499,16 +499,9 @@ internal static ThunkInfo GetThunk(MethodInfo method, string funcType = null)
{
return ThunkInfo.Empty;
}
- try
- {
- Delegate d = Delegate.CreateDelegate(dt, method);
- var info = new ThunkInfo(d);
- return info;
- }
- catch (Exception)
- {
- throw;
- }
+ Delegate d = Delegate.CreateDelegate(dt, method);
+ var info = new ThunkInfo(d);
+ return info;
}
diff --git a/src/runtime/runtime.cs b/src/runtime/runtime.cs
index 8a9453dd3..d61ac7b34 100644
--- a/src/runtime/runtime.cs
+++ b/src/runtime/runtime.cs
@@ -358,9 +358,7 @@ internal static void Shutdown()
}
#endif
AssemblyManager.Shutdown();
- Exceptions.Shutdown();
ImportHook.Shutdown();
- Finalizer.Shutdown();
#if !NETSTANDARD
if (mode != ShutdownMode.Reload)
@@ -377,6 +375,9 @@ internal static void Shutdown()
MetaType.Release();
PyCLRMetaType = IntPtr.Zero;
+ Exceptions.Shutdown();
+ Finalizer.Shutdown();
+
if (mode != ShutdownMode.Normal)
{
PyGC_Collect();
From 8b516210a5088f730b8fa192dd19db5b0f03b9eb Mon Sep 17 00:00:00 2001
From: amos402
Date: Mon, 17 Feb 2020 11:30:27 +0800
Subject: [PATCH 090/998] Split RuntimeData into separate file
---
src/runtime/Python.Runtime.csproj | 1 +
src/runtime/runtime_data.cs | 99 +++++++++++++++++++++++++++++++
src/runtime/runtime_state.cs | 91 ----------------------------
3 files changed, 100 insertions(+), 91 deletions(-)
create mode 100644 src/runtime/runtime_data.cs
diff --git a/src/runtime/Python.Runtime.csproj b/src/runtime/Python.Runtime.csproj
index a00f37440..73f352076 100644
--- a/src/runtime/Python.Runtime.csproj
+++ b/src/runtime/Python.Runtime.csproj
@@ -137,6 +137,7 @@
+
diff --git a/src/runtime/runtime_data.cs b/src/runtime/runtime_data.cs
new file mode 100644
index 000000000..420837be8
--- /dev/null
+++ b/src/runtime/runtime_data.cs
@@ -0,0 +1,99 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Runtime.InteropServices;
+using System.Runtime.Serialization.Formatters.Binary;
+
+using static Python.Runtime.Runtime;
+
+namespace Python.Runtime
+{
+ class RuntimeData
+ {
+ internal static void Stash()
+ {
+ var formatter = new BinaryFormatter();
+ var ms = new MemoryStream();
+ var stack = new Stack();
+ MetaType.StashPush(stack);
+ TypeManager.StashPush(stack);
+ ClassManager.StashPush(stack);
+ var objs = ManagedType.GetManagedObjects();
+ var saveObjs = new Dictionary();
+ foreach (var entry in objs)
+ {
+ var obj = entry.Key;
+ if (entry.Value == ManagedType.TrackTypes.Wrapper
+ && !obj.GetType().IsSerializable)
+ {
+ // XXX: Skip non-serializable objects,
+ // use them after next initialization will raise exceptions.
+ continue;
+ }
+ Debug.Assert(obj.GetType().IsSerializable);
+ obj.Save();
+ saveObjs.Add(entry.Key, entry.Value);
+ }
+ stack.Push(saveObjs);
+ formatter.Serialize(ms, stack);
+
+ byte[] data = ms.GetBuffer();
+ // TODO: use buffer api instead
+ Debug.Assert(data.Length <= int.MaxValue);
+ IntPtr mem = PyMem_Malloc(data.LongLength + IntPtr.Size);
+ Marshal.WriteIntPtr(mem, (IntPtr)data.LongLength);
+ Marshal.Copy(data, 0, mem + IntPtr.Size, data.Length);
+
+ IntPtr capsule = PySys_GetObject("clr_data");
+ if (capsule != IntPtr.Zero)
+ {
+ IntPtr oldData = PyCapsule_GetPointer(capsule, null);
+ PyMem_Free(oldData);
+ PyCapsule_SetPointer(capsule, IntPtr.Zero);
+ }
+ capsule = PyCapsule_New(mem, null, IntPtr.Zero);
+ PySys_SetObject("clr_data", capsule);
+ XDecref(capsule);
+ }
+
+ internal static void StashPop()
+ {
+ IntPtr capsule = PySys_GetObject("clr_data");
+ if (capsule == IntPtr.Zero)
+ {
+ return;
+ }
+ IntPtr mem = PyCapsule_GetPointer(capsule, null);
+ int length = (int)Marshal.ReadIntPtr(mem);
+ byte[] data = new byte[length];
+ Marshal.Copy(mem + IntPtr.Size, data, 0, length);
+ var ms = new MemoryStream(data);
+ var formatter = new BinaryFormatter();
+
+ var stack = (Stack)formatter.Deserialize(ms);
+
+ var loadObjs = (IDictionary)stack.Pop();
+ foreach (var entry in loadObjs)
+ {
+ ManagedType obj = entry.Key;
+ obj.Load();
+ }
+ Debug.Assert(ManagedType.GetManagedObjects().Count == loadObjs.Count);
+ ClassManager.StashPop(stack);
+ TypeManager.StashPop(stack);
+ PyCLRMetaType = MetaType.StashPop(stack);
+ }
+
+ public static bool HasStashData()
+ {
+ return PySys_GetObject("clr_data") != IntPtr.Zero;
+ }
+
+ public static void ClearStash()
+ {
+ PySys_SetObject("clr_data", IntPtr.Zero);
+ }
+ }
+}
diff --git a/src/runtime/runtime_state.cs b/src/runtime/runtime_state.cs
index bf7850d01..ca3fecbb2 100644
--- a/src/runtime/runtime_state.cs
+++ b/src/runtime/runtime_state.cs
@@ -1,10 +1,7 @@
using System;
-using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
-using System.IO;
using System.Runtime.InteropServices;
-using System.Runtime.Serialization.Formatters.Binary;
using static Python.Runtime.Runtime;
@@ -236,92 +233,4 @@ private static unsafe IntPtr GetNextGCNode(IntPtr node)
return ((PyGC_Head*)node)->gc.gc_next;
}
}
-
-
- class RuntimeData
- {
- internal static void Stash()
- {
- var formatter = new BinaryFormatter();
- var ms = new MemoryStream();
- var stack = new Stack();
- MetaType.StashPush(stack);
- TypeManager.StashPush(stack);
- ClassManager.StashPush(stack);
- var objs = ManagedType.GetManagedObjects();
- var saveObjs = new Dictionary();
- foreach (var entry in objs)
- {
- var obj = entry.Key;
- if (entry.Value == ManagedType.TrackTypes.Wrapper
- && !obj.GetType().IsSerializable)
- {
- // XXX: Skip non-serializable objects,
- // use them after next initialization will raise exceptions.
- continue;
- }
- Debug.Assert(obj.GetType().IsSerializable);
- obj.Save();
- saveObjs.Add(entry.Key, entry.Value);
- }
- stack.Push(saveObjs);
- formatter.Serialize(ms, stack);
-
- byte[] data = ms.GetBuffer();
- // TODO: use buffer api instead
- Debug.Assert(data.Length <= int.MaxValue);
- IntPtr mem = PyMem_Malloc(data.LongLength + IntPtr.Size);
- Marshal.WriteIntPtr(mem, (IntPtr)data.LongLength);
- Marshal.Copy(data, 0, mem + IntPtr.Size, data.Length);
-
- IntPtr capsule = PySys_GetObject("clr_data");
- if (capsule != IntPtr.Zero)
- {
- IntPtr oldData = PyCapsule_GetPointer(capsule, null);
- PyMem_Free(oldData);
- PyCapsule_SetPointer(capsule, IntPtr.Zero);
- }
- capsule = PyCapsule_New(mem, null, IntPtr.Zero);
- PySys_SetObject("clr_data", capsule);
- XDecref(capsule);
- }
-
- internal static void StashPop()
- {
- IntPtr capsule = PySys_GetObject("clr_data");
- if (capsule == IntPtr.Zero)
- {
- return;
- }
- IntPtr mem = PyCapsule_GetPointer(capsule, null);
- int length = (int)Marshal.ReadIntPtr(mem);
- byte[] data = new byte[length];
- Marshal.Copy(mem + IntPtr.Size, data, 0, length);
- var ms = new MemoryStream(data);
- var formatter = new BinaryFormatter();
-
- var stack = (Stack)formatter.Deserialize(ms);
-
- var loadObjs = (IDictionary)stack.Pop();
- foreach (var entry in loadObjs)
- {
- ManagedType obj = entry.Key;
- obj.Load();
- }
- Debug.Assert(ManagedType.GetManagedObjects().Count == loadObjs.Count);
- ClassManager.StashPop(stack);
- TypeManager.StashPop(stack);
- PyCLRMetaType = MetaType.StashPop(stack);
- }
-
- public static bool HasStashData()
- {
- return PySys_GetObject("clr_data") != IntPtr.Zero;
- }
-
- public static void ClearStash()
- {
- PySys_SetObject("clr_data", IntPtr.Zero);
- }
- }
}
From 8ae5cf49fb075deda0c30bc59ea03ffdbaeda3d3 Mon Sep 17 00:00:00 2001
From: Andrey Santanna
Date: Thu, 20 Feb 2020 12:03:35 -0300
Subject: [PATCH 091/998] #1047 ModuleObject __getattribute__ doesn't treat
exceptions ocurred during internal GetAttribute
---
src/runtime/moduleobject.cs | 13 ++++++++++++-
1 file changed, 12 insertions(+), 1 deletion(-)
diff --git a/src/runtime/moduleobject.cs b/src/runtime/moduleobject.cs
index 544f69c81..5e7059b53 100644
--- a/src/runtime/moduleobject.cs
+++ b/src/runtime/moduleobject.cs
@@ -280,7 +280,18 @@ public static IntPtr tp_getattro(IntPtr ob, IntPtr key)
return self.dict;
}
- ManagedType attr = self.GetAttribute(name, true);
+ ManagedType attr = null;
+
+ try
+ {
+ attr = self.GetAttribute(name, true);
+ }
+ catch (Exception e)
+ {
+ Exceptions.SetError(e);
+ return IntPtr.Zero;
+ }
+
if (attr == null)
{
From f25694e47f5ce796d631dc6037d56a3d46df581a Mon Sep 17 00:00:00 2001
From: Andrey Santanna
Date: Thu, 20 Feb 2020 12:20:33 -0300
Subject: [PATCH 092/998] #1047 ModuleObject __getattribute__ doesn't treat
exceptions ocurred during internal GetAttribute
---
AUTHORS.md | 1 +
CHANGELOG.md | 1 +
2 files changed, 2 insertions(+)
diff --git a/AUTHORS.md b/AUTHORS.md
index 26285bf6a..b0d1f0c91 100644
--- a/AUTHORS.md
+++ b/AUTHORS.md
@@ -15,6 +15,7 @@
- Alex Earl ([@slide](https://github.com/slide))
- Alex Helms ([@alexhelms](https://github.com/alexhelms))
- Alexandre Catarino([@AlexCatarino](https://github.com/AlexCatarino))
+- Andrey Sant'Anna ([@andreydani](https://github.com/andreydani))
- Arvid JB ([@ArvidJB](https://github.com/ArvidJB))
- Benoît Hudson ([@benoithudson](https://github.com/benoithudson))
- Bradley Friedman ([@leith-bartrich](https://github.com/leith-bartrich))
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1fd2b1dcf..82fccbe35 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -29,6 +29,7 @@ This document follows the conventions laid out in [Keep a CHANGELOG][].
together with Nuitka
- Fixes bug where delegates get casts (dotnetcore)
- Determine size of interpreter longs at runtime
+- Handling exceptions ocurred in ModuleObject's getattribute
## [2.4.0][]
From 9e3ed3ae69c54dc26e9e7a15c5c9026ccfa7dfce Mon Sep 17 00:00:00 2001
From: Victor Milovanov
Date: Thu, 20 Feb 2020 12:33:03 -0800
Subject: [PATCH 093/998] report AppVeyor build timings
---
ci/appveyor_build_recipe.ps1 | 6 +++++-
ci/appveyor_run_tests.ps1 | 28 ++++++++++++++++++++++++++++
2 files changed, 33 insertions(+), 1 deletion(-)
diff --git a/ci/appveyor_build_recipe.ps1 b/ci/appveyor_build_recipe.ps1
index 84e0bc7c6..08eae8d5d 100644
--- a/ci/appveyor_build_recipe.ps1
+++ b/ci/appveyor_build_recipe.ps1
@@ -1,5 +1,7 @@
# Build `conda.recipe` only if this is a Pull_Request. Saves time for CI.
+$stopwatch = [Diagnostics.Stopwatch]::StartNew()
+
$env:CONDA_PY = "$env:PY_VER"
# Use pre-installed miniconda. Note that location differs if 64bit
$env:CONDA_BLD = "C:\miniconda36"
@@ -30,7 +32,9 @@ if ($env:APPVEYOR_PULL_REQUEST_NUMBER -or $env:APPVEYOR_REPO_TAG_NAME -or $env:F
$CONDA_PKG=(conda build conda.recipe --output)
Copy-Item $CONDA_PKG .\dist\
- Write-Host "Completed conda build recipe" -ForegroundColor "Green"
+
+ $timeSpent = $stopwatch.Elapsed
+ Write-Host "Completed conda build recipe in " $timeSpent -ForegroundColor "Green"
# Restore PATH back to original
$env:path = $old_path
diff --git a/ci/appveyor_run_tests.ps1 b/ci/appveyor_run_tests.ps1
index f94cfb11e..7bd632b19 100644
--- a/ci/appveyor_run_tests.ps1
+++ b/ci/appveyor_run_tests.ps1
@@ -1,5 +1,8 @@
# Script to simplify AppVeyor configuration and resolve path to tools
+$stopwatch = [Diagnostics.Stopwatch]::StartNew()
+[array]$timings = @()
+
# Test Runner framework being used for embedded tests
$CS_RUNNER = "nunit3-console"
@@ -25,6 +28,17 @@ $PY = Get-Command python
$CS_TESTS = ".\src\embed_tests\bin\Python.EmbeddingTest.dll"
$RUNTIME_DIR = ".\src\runtime\bin\"
+function ReportTime {
+ param([string] $action)
+
+ $timeSpent = $stopwatch.Elapsed
+ $timings += [pscustomobject]@{action=$action; timeSpent=$timeSpent}
+ Write-Host $action " in " $timeSpent -ForegroundColor "Green"
+ $stopwatch.Restart()
+}
+
+ReportTime "Preparation done"
+
# Run python tests with C# coverage
Write-Host ("Starting Python tests") -ForegroundColor "Green"
.$OPENCOVER -register:user -searchdirs:"$RUNTIME_DIR" -output:py.coverage `
@@ -33,6 +47,9 @@ Write-Host ("Starting Python tests") -ForegroundColor "Green"
$PYTHON_STATUS = $LastExitCode
if ($PYTHON_STATUS -ne 0) {
Write-Host "Python tests failed, continuing to embedded tests" -ForegroundColor "Red"
+ ReportTime ""
+} else {
+ ReportTime "Python tests completed"
}
# Run Embedded tests with C# coverage
@@ -44,7 +61,10 @@ Write-Host ("Starting embedded tests") -ForegroundColor "Green"
$CS_STATUS = $LastExitCode
if ($CS_STATUS -ne 0) {
Write-Host "Embedded tests failed" -ForegroundColor "Red"
+ ReportTime ""
} else {
+ ReportTime "Embedded tests completed"
+
# NuGet for pythonnet-2.3 only has 64-bit binary for Python 3.5
# the test is only built using modern stack
if (($env:PLATFORM -eq "x64") -and ($XPLAT) -and ($env:PYTHON_VERSION -eq "3.5")) {
@@ -60,6 +80,9 @@ if ($CS_STATUS -ne 0) {
$CS_PERF_STATUS = $LastExitCode
if ($CS_PERF_STATUS -ne 0) {
Write-Host "Performance tests (C#) failed" -ForegroundColor "Red"
+ ReportTime ""
+ } else {
+ ReportTime "Performance tests (C#) completed"
}
} else {
Write-Host ("Skipping performance tests for ", $env:PYTHON_VERSION) -ForegroundColor "Yellow"
@@ -82,9 +105,14 @@ if ($XPLAT){
$CS_STATUS = $LastExitCode
if ($CS_STATUS -ne 0) {
Write-Host "Embedded tests for netcoreapp2.0 failed" -ForegroundColor "Red"
+ ReportTime ""
+ } else {
+ ReportTime ".NET Core 2.0 tests completed"
}
}
+Write-Host ($timings | Format-Table | Out-String)
+
# Set exit code to fail if either Python or Embedded tests failed
if ($PYTHON_STATUS -ne 0 -or $CS_STATUS -ne 0 -or $CS_PERF_STATUS -ne 0) {
Write-Host "Tests failed" -ForegroundColor "Red"
From 103fa099015786b291bd8b009f6516099385bc2d Mon Sep 17 00:00:00 2001
From: Victor Milovanov
Date: Thu, 20 Feb 2020 14:40:29 -0800
Subject: [PATCH 094/998] reduced number of iterations in performance tests to
fit into AppVeyor time limit
---
src/perf_tests/PythonCallingNetBenchmark.cs | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/perf_tests/PythonCallingNetBenchmark.cs b/src/perf_tests/PythonCallingNetBenchmark.cs
index 4e9461d2e..ef668a911 100644
--- a/src/perf_tests/PythonCallingNetBenchmark.cs
+++ b/src/perf_tests/PythonCallingNetBenchmark.cs
@@ -19,7 +19,7 @@ public void ReadInt64Property()
locals.SetItem("a", new NetObject().ToPython());
PythonEngine.Exec($@"
s = 0
-for i in range(300000):
+for i in range(50000):
s += a.{nameof(NetObject.LongProperty)}
", locals: locals.Handle);
}
@@ -32,7 +32,7 @@ public void WriteInt64Property() {
locals.SetItem("a", new NetObject().ToPython());
PythonEngine.Exec($@"
s = 0
-for i in range(300000):
+for i in range(50000):
a.{nameof(NetObject.LongProperty)} += i
", locals: locals.Handle);
}
From c4bbc8cc1e3683f4a8df5ad18dcfcfa6717b5314 Mon Sep 17 00:00:00 2001
From: Victor Milovanov
Date: Thu, 20 Feb 2020 15:40:20 -0800
Subject: [PATCH 095/998] make timings output more prominent
---
ci/appveyor_run_tests.ps1 | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/ci/appveyor_run_tests.ps1 b/ci/appveyor_run_tests.ps1
index 7bd632b19..cb1c68eed 100644
--- a/ci/appveyor_run_tests.ps1
+++ b/ci/appveyor_run_tests.ps1
@@ -111,7 +111,7 @@ if ($XPLAT){
}
}
-Write-Host ($timings | Format-Table | Out-String)
+Write-Host "Timings:" ($timings | Format-Table | Out-String) -ForegroundColor "Green"
# Set exit code to fail if either Python or Embedded tests failed
if ($PYTHON_STATUS -ne 0 -or $CS_STATUS -ne 0 -or $CS_PERF_STATUS -ne 0) {
From 97c8c2a200bc1246f7ed0fa034300b81fa6dfe48 Mon Sep 17 00:00:00 2001
From: amos402
Date: Fri, 21 Feb 2020 11:51:10 +0800
Subject: [PATCH 096/998] Extract InitPyMembers method
---
src/runtime/runtime.cs | 64 ++++++++++++++++++++++--------------------
1 file changed, 34 insertions(+), 30 deletions(-)
diff --git a/src/runtime/runtime.cs b/src/runtime/runtime.cs
index d61ac7b34..dcf3df4b1 100644
--- a/src/runtime/runtime.cs
+++ b/src/runtime/runtime.cs
@@ -179,6 +179,40 @@ internal static void Initialize(bool initSigs = false, ShutdownMode mode = Shutd
ClassDerivedObject.Reset();
TypeManager.Initialize();
+ InitPyMembers();
+
+ // Initialize data about the platform we're running on. We need
+ // this for the type manager and potentially other details. Must
+ // happen after caching the python types, above.
+ NativeCodePageHelper.InitializePlatformData();
+
+ // Initialize modules that depend on the runtime class.
+ AssemblyManager.Initialize();
+#if !NETSTANDARD
+ if (mode == ShutdownMode.Reload && RuntimeData.HasStashData())
+ {
+ RuntimeData.StashPop();
+ }
+ else
+#endif
+ {
+ PyCLRMetaType = MetaType.Initialize(); // Steal a reference
+ }
+ Exceptions.Initialize();
+ ImportHook.Initialize();
+
+ // Need to add the runtime directory to sys.path so that we
+ // can find built-in assemblies like System.Data, et. al.
+ string rtdir = RuntimeEnvironment.GetRuntimeDirectory();
+ IntPtr path = PySys_GetObject("path");
+ IntPtr item = PyString_FromString(rtdir);
+ PyList_Append(path, item);
+ XDecref(item);
+ AssemblyManager.UpdatePath();
+ }
+
+ private static void InitPyMembers()
+ {
IntPtr op;
{
var builtins = GetBuiltins();
@@ -300,36 +334,6 @@ internal static void Initialize(bool initSigs = false, ShutdownMode mode = Shutd
PyModuleType = PyObject_Type(sys);
XDecref(sys);
}
-
- // Initialize data about the platform we're running on. We need
- // this for the type manager and potentially other details. Must
- // happen after caching the python types, above.
- NativeCodePageHelper.InitializePlatformData();
-
- // Initialize modules that depend on the runtime class.
- AssemblyManager.Initialize();
-#if !NETSTANDARD
- if (mode == ShutdownMode.Reload && RuntimeData.HasStashData())
- {
- RuntimeData.StashPop();
- RuntimeData.ClearStash();
- }
- else
-#endif
- {
- PyCLRMetaType = MetaType.Initialize(); // Steal a reference
- }
- Exceptions.Initialize();
- ImportHook.Initialize();
-
- // Need to add the runtime directory to sys.path so that we
- // can find built-in assemblies like System.Data, et. al.
- string rtdir = RuntimeEnvironment.GetRuntimeDirectory();
- IntPtr path = PySys_GetObject("path");
- IntPtr item = PyString_FromString(rtdir);
- PyList_Append(path, item);
- XDecref(item);
- AssemblyManager.UpdatePath();
}
private static IntPtr Get_PyObject_NextNotImplemented()
From ec982097e21ff7bddf8e78aa8b3d9b7662f8f70a Mon Sep 17 00:00:00 2001
From: Victor Milovanov
Date: Thu, 20 Feb 2020 23:25:31 -0800
Subject: [PATCH 097/998] fixed bad IncRef after PyTuple_New
---
src/runtime/Codecs/TupleCodecs.cs | 10 +++++++---
1 file changed, 7 insertions(+), 3 deletions(-)
diff --git a/src/runtime/Codecs/TupleCodecs.cs b/src/runtime/Codecs/TupleCodecs.cs
index f6bcc3fc9..51c08cd3d 100644
--- a/src/runtime/Codecs/TupleCodecs.cs
+++ b/src/runtime/Codecs/TupleCodecs.cs
@@ -12,8 +12,12 @@ public sealed class TupleCodec : IPyObjectEncoder, IPyObjectDecoder
public static TupleCodec Instance { get; } = new TupleCodec();
public bool CanEncode(Type type)
- => type.Namespace == typeof(TTuple).Namespace && type.Name.StartsWith(typeof(TTuple).Name + '`')
- || type == typeof(object) || type == typeof(TTuple);
+ {
+ if (type == typeof(object) || type == typeof(TTuple)) return true;
+ return type.Namespace == typeof(TTuple).Namespace
+ // generic versions of tuples are named Tuple`TYPE_ARG_COUNT
+ && type.Name.StartsWith(typeof(TTuple).Name + '`');
+ }
public PyObject TryEncode(object value)
{
@@ -36,7 +40,7 @@ public PyObject TryEncode(object value)
Runtime.PyTuple_SetItem(tuple, fieldIndex, pyItem);
fieldIndex++;
}
- return new PyTuple(Runtime.SelfIncRef(tuple));
+ return new PyTuple(tuple);
}
public bool CanDecode(PyObject objectType, Type targetType)
From 50a3822bf49f6e99a0b96a78b89af26fd32e0fcc Mon Sep 17 00:00:00 2001
From: Victor Milovanov
Date: Thu, 20 Feb 2020 23:31:54 -0800
Subject: [PATCH 098/998] corrected reference counting in Codecs
---
src/runtime/converter.cs | 6 ++++--
src/runtime/converterextensions.cs | 2 +-
2 files changed, 5 insertions(+), 3 deletions(-)
diff --git a/src/runtime/converter.cs b/src/runtime/converter.cs
index 85f4fecb5..a56fa88bb 100644
--- a/src/runtime/converter.cs
+++ b/src/runtime/converter.cs
@@ -138,8 +138,10 @@ internal static IntPtr ToPython(object value, Type type)
if (Type.GetTypeCode(type) == TypeCode.Object && value.GetType() != typeof(object)) {
var encoded = PyObjectConversions.TryEncode(value, type);
if (encoded != null) {
- Runtime.XIncref(encoded.Handle);
- return encoded.Handle;
+ result = encoded.Handle;
+ Runtime.XIncref(result);
+ encoded.Dispose();
+ return result;
}
}
diff --git a/src/runtime/converterextensions.cs b/src/runtime/converterextensions.cs
index 0d7f0aff2..ce8ec2679 100644
--- a/src/runtime/converterextensions.cs
+++ b/src/runtime/converterextensions.cs
@@ -124,7 +124,7 @@ internal static bool TryDecode(IntPtr pyHandle, IntPtr pyType, Type targetType,
static Converter.TryConvertFromPythonDelegate GetDecoder(IntPtr sourceType, Type targetType)
{
IPyObjectDecoder decoder;
- using (var pyType = new PyObject(sourceType))
+ using (var pyType = new PyObject(Runtime.SelfIncRef(sourceType)))
{
lock (decoders)
{
From 2e19f2c3d3a40502968a77149f4a62a590fdb9a3 Mon Sep 17 00:00:00 2001
From: Victor Milovanov
Date: Fri, 21 Feb 2020 09:29:59 -0800
Subject: [PATCH 099/998] don't dispose encoded object in case codec keeps a
cache of them
---
src/runtime/converter.cs | 1 -
1 file changed, 1 deletion(-)
diff --git a/src/runtime/converter.cs b/src/runtime/converter.cs
index a56fa88bb..7c53bdcb1 100644
--- a/src/runtime/converter.cs
+++ b/src/runtime/converter.cs
@@ -140,7 +140,6 @@ internal static IntPtr ToPython(object value, Type type)
if (encoded != null) {
result = encoded.Handle;
Runtime.XIncref(result);
- encoded.Dispose();
return result;
}
}
From 703439030a8e79f0d382b8a96f88ac7f72f16d8a Mon Sep 17 00:00:00 2001
From: Victor Milovanov
Date: Fri, 21 Feb 2020 16:44:49 -0800
Subject: [PATCH 100/998] limit benchmark time in config
---
src/perf_tests/BaselineComparisonConfig.cs | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/src/perf_tests/BaselineComparisonConfig.cs b/src/perf_tests/BaselineComparisonConfig.cs
index 06d529ff9..649bb56fd 100644
--- a/src/perf_tests/BaselineComparisonConfig.cs
+++ b/src/perf_tests/BaselineComparisonConfig.cs
@@ -5,6 +5,7 @@
using BenchmarkDotNet.Configs;
using BenchmarkDotNet.Jobs;
+using BenchmarkDotNet.Horology;
namespace Python.PerformanceTests
{
@@ -18,7 +19,11 @@ public BaselineComparisonConfig()
string deploymentRoot = BenchmarkTests.DeploymentRoot;
- var baseJob = Job.Default;
+ var baseJob = Job.Default
+ .WithLaunchCount(1)
+ .WithWarmupCount(3)
+ .WithMaxIterationCount(100)
+ .WithIterationTime(TimeInterval.FromMilliseconds(100));
this.Add(baseJob
.WithId("baseline")
.WithEnvironmentVariable(EnvironmentVariableName,
From a424998ec6d5452a79d8d0f035a18661666b8ac6 Mon Sep 17 00:00:00 2001
From: Victor
Date: Sun, 23 Feb 2020 09:01:19 -0800
Subject: [PATCH 101/998] ensure Py handles are inaccessible after
PythonException is disposed (#1055)
---
src/runtime/pythonexception.cs | 17 ++++++++++++++---
src/runtime/runtime.cs | 2 +-
2 files changed, 15 insertions(+), 4 deletions(-)
diff --git a/src/runtime/pythonexception.cs b/src/runtime/pythonexception.cs
index 8a6a24799..7ac922abc 100644
--- a/src/runtime/pythonexception.cs
+++ b/src/runtime/pythonexception.cs
@@ -21,7 +21,7 @@ public class PythonException : System.Exception, IPyDisposable
public PythonException()
{
IntPtr gs = PythonEngine.AcquireLock();
- Runtime.PyErr_Fetch(ref _pyType, ref _pyValue, ref _pyTB);
+ Runtime.PyErr_Fetch(out _pyType, out _pyValue, out _pyTB);
if (_pyType != IntPtr.Zero && _pyValue != IntPtr.Zero)
{
string type;
@@ -161,12 +161,23 @@ public void Dispose()
if (Runtime.Py_IsInitialized() > 0 && !Runtime.IsFinalizing)
{
IntPtr gs = PythonEngine.AcquireLock();
- Runtime.XDecref(_pyType);
- Runtime.XDecref(_pyValue);
+ if (_pyType != IntPtr.Zero)
+ {
+ Runtime.XDecref(_pyType);
+ _pyType= IntPtr.Zero;
+ }
+
+ if (_pyValue != IntPtr.Zero)
+ {
+ Runtime.XDecref(_pyValue);
+ _pyValue = IntPtr.Zero;
+ }
+
// XXX Do we ever get TraceBack? //
if (_pyTB != IntPtr.Zero)
{
Runtime.XDecref(_pyTB);
+ _pyTB = IntPtr.Zero;
}
PythonEngine.ReleaseLock(gs);
}
diff --git a/src/runtime/runtime.cs b/src/runtime/runtime.cs
index 7748bafa9..9c7cb42d2 100644
--- a/src/runtime/runtime.cs
+++ b/src/runtime/runtime.cs
@@ -1933,7 +1933,7 @@ internal static IntPtr PyMem_Realloc(IntPtr ptr, long size)
internal static extern IntPtr PyErr_Occurred();
[DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)]
- internal static extern void PyErr_Fetch(ref IntPtr ob, ref IntPtr val, ref IntPtr tb);
+ internal static extern void PyErr_Fetch(out IntPtr ob, out IntPtr val, out IntPtr tb);
[DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)]
internal static extern void PyErr_Restore(IntPtr ob, IntPtr val, IntPtr tb);
From 399ae545c9c6a4ab0a0f1713e41eb1c41c889ca4 Mon Sep 17 00:00:00 2001
From: Victor Milovanov
Date: Sun, 23 Feb 2020 15:10:22 -0800
Subject: [PATCH 102/998] do not dispose object, that might have been just
decoded succesfully, as the decoded object might store a reference to the
original PyObject
---
src/runtime/converterextensions.cs | 13 ++++++++-----
1 file changed, 8 insertions(+), 5 deletions(-)
diff --git a/src/runtime/converterextensions.cs b/src/runtime/converterextensions.cs
index ce8ec2679..fd012c6e4 100644
--- a/src/runtime/converterextensions.cs
+++ b/src/runtime/converterextensions.cs
@@ -137,13 +137,16 @@ static Converter.TryConvertFromPythonDelegate GetDecoder(IntPtr sourceType, Type
bool TryDecode(IntPtr pyHandle, out object result)
{
- using (var pyObj = new PyObject(Runtime.SelfIncRef(pyHandle)))
+ var pyObj = new PyObject(Runtime.SelfIncRef(pyHandle));
+ var @params = new object[] { pyObj, null };
+ bool success = (bool)decode.Invoke(decoder, @params);
+ if (!success)
{
- var @params = new object[] { pyObj, null };
- bool success = (bool)decode.Invoke(decoder, @params);
- result = @params[1];
- return success;
+ pyObj.Dispose();
}
+
+ result = @params[1];
+ return success;
}
return TryDecode;
From e2d333361c036243cec3c9aecab514d3888d8efa Mon Sep 17 00:00:00 2001
From: Victor Milovanov
Date: Sun, 23 Feb 2020 15:11:40 -0800
Subject: [PATCH 103/998] remove incref for tuple fields, as Converter.ToPython
is supposed to return a new reference
---
src/runtime/Codecs/TupleCodecs.cs | 1 -
1 file changed, 1 deletion(-)
diff --git a/src/runtime/Codecs/TupleCodecs.cs b/src/runtime/Codecs/TupleCodecs.cs
index 51c08cd3d..4c81cac0b 100644
--- a/src/runtime/Codecs/TupleCodecs.cs
+++ b/src/runtime/Codecs/TupleCodecs.cs
@@ -36,7 +36,6 @@ public PyObject TryEncode(object value)
{
var item = field.GetValue(value);
IntPtr pyItem = Converter.ToPython(item);
- Runtime.XIncref(pyItem);
Runtime.PyTuple_SetItem(tuple, fieldIndex, pyItem);
fieldIndex++;
}
From 39f47c81bbbafc501c98e7d59517484eca4e7a57 Mon Sep 17 00:00:00 2001
From: amos402
Date: Mon, 24 Feb 2020 13:02:08 +0800
Subject: [PATCH 104/998] * Classify runtime data * External interface for
custom storing wrapper objects
---
src/runtime/classmanager.cs | 8 +-
src/runtime/clrobject.cs | 16 ++
src/runtime/interop.cs | 4 +-
src/runtime/metatype.cs | 8 +-
src/runtime/runtime.cs | 17 +-
src/runtime/runtime_data.cs | 333 +++++++++++++++++++++++++++++++-----
src/runtime/typemanager.cs | 18 +-
7 files changed, 336 insertions(+), 68 deletions(-)
diff --git a/src/runtime/classmanager.cs b/src/runtime/classmanager.cs
index 3b9922a3d..0c7fff5af 100644
--- a/src/runtime/classmanager.cs
+++ b/src/runtime/classmanager.cs
@@ -82,14 +82,14 @@ private static int OnVisit(IntPtr ob, IntPtr arg)
}
- internal static void StashPush(Stack stack)
+ internal static void StashPush(RuntimeDataStorage storage)
{
- stack.Push(cache);
+ storage.PushValue(cache);
}
- internal static void StashPop(Stack stack)
+ internal static void StashPop(RuntimeDataStorage storage)
{
- cache = (Dictionary)stack.Pop();
+ cache = storage.PopValue>();
}
///
diff --git a/src/runtime/clrobject.cs b/src/runtime/clrobject.cs
index b004eb23b..a8af80f67 100644
--- a/src/runtime/clrobject.cs
+++ b/src/runtime/clrobject.cs
@@ -35,6 +35,10 @@ internal CLRObject(object ob, IntPtr tp)
Exceptions.SetArgsAndCause(py);
}
+ protected CLRObject()
+ {
+ }
+
internal static CLRObject GetInstance(object ob, IntPtr pyType)
{
@@ -70,6 +74,18 @@ internal static IntPtr GetInstHandle(object ob)
return co.pyHandle;
}
+ internal static CLRObject Restore(object ob, IntPtr pyHandle)
+ {
+ CLRObject co = new CLRObject()
+ {
+ inst = ob,
+ pyHandle = pyHandle,
+ tpHandle = Runtime.PyObject_TYPE(pyHandle)
+ };
+ co.Load();
+ return co;
+ }
+
protected override void OnSave()
{
base.OnSave();
diff --git a/src/runtime/interop.cs b/src/runtime/interop.cs
index 58ec82415..5a39a512d 100644
--- a/src/runtime/interop.cs
+++ b/src/runtime/interop.cs
@@ -564,7 +564,7 @@ public ThunkInfo(Delegate target)
}
}
- [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
+ [StructLayout(LayoutKind.Sequential)]
struct PyGC_Node
{
public IntPtr gc_next;
@@ -572,7 +572,7 @@ struct PyGC_Node
public IntPtr gc_refs;
}
- [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
+ [StructLayout(LayoutKind.Sequential)]
struct PyGC_Head
{
public PyGC_Node gc;
diff --git a/src/runtime/metatype.cs b/src/runtime/metatype.cs
index b54c2eb90..95dd8b9b4 100644
--- a/src/runtime/metatype.cs
+++ b/src/runtime/metatype.cs
@@ -40,15 +40,15 @@ public static void Release()
_metaSlotsHodler = null;
}
- internal static void StashPush(Stack stack)
+ internal static void StashPush(RuntimeDataStorage storage)
{
Runtime.XIncref(PyCLRMetaType);
- stack.Push(PyCLRMetaType);
+ storage.PushValue(PyCLRMetaType);
}
- internal static IntPtr StashPop(Stack stack)
+ internal static IntPtr StashPop(RuntimeDataStorage storage)
{
- PyCLRMetaType = (IntPtr)stack.Pop();
+ PyCLRMetaType = storage.PopValue();
_metaSlotsHodler = new SlotsHolder(PyCLRMetaType);
TypeManager.InitializeSlots(PyCLRMetaType, typeof(MetaType), _metaSlotsHodler);
diff --git a/src/runtime/runtime.cs b/src/runtime/runtime.cs
index dcf3df4b1..66c23c87b 100644
--- a/src/runtime/runtime.cs
+++ b/src/runtime/runtime.cs
@@ -206,7 +206,10 @@ internal static void Initialize(bool initSigs = false, ShutdownMode mode = Shutd
string rtdir = RuntimeEnvironment.GetRuntimeDirectory();
IntPtr path = PySys_GetObject("path");
IntPtr item = PyString_FromString(rtdir);
- PyList_Append(path, item);
+ if (PySequence_Contains(path, item) == 0)
+ {
+ PyList_Append(path, item);
+ }
XDecref(item);
AssemblyManager.UpdatePath();
}
@@ -364,12 +367,7 @@ internal static void Shutdown()
AssemblyManager.Shutdown();
ImportHook.Shutdown();
-#if !NETSTANDARD
- if (mode != ShutdownMode.Reload)
- {
- ClearClrModules();
- }
-#endif
+ ClearClrModules();
RemoveClrRootModule();
MoveClrInstancesOnwershipToPython();
@@ -454,8 +452,7 @@ private static void ClearClrModules()
var item = PyList_GetItem(items, i);
var name = PyTuple_GetItem(item, 0);
var module = PyTuple_GetItem(item, 1);
- var clrModule = ManagedType.GetManagedObject(module);
- if (clrModule != null)
+ if (ManagedType.IsManagedType(module))
{
PyDict_DelItem(modules, name);
}
@@ -486,8 +483,8 @@ private static void PyDictTryDelItem(IntPtr dict, string key)
private static void MoveClrInstancesOnwershipToPython()
{
- var copyObjs = ManagedType.GetManagedObjects().ToArray();
var objs = ManagedType.GetManagedObjects();
+ var copyObjs = objs.ToArray();
foreach (var entry in copyObjs)
{
ManagedType obj = entry.Key;
diff --git a/src/runtime/runtime_data.cs b/src/runtime/runtime_data.cs
index 420837be8..a4cab5a20 100644
--- a/src/runtime/runtime_data.cs
+++ b/src/runtime/runtime_data.cs
@@ -1,50 +1,70 @@
using System;
using System.Collections;
using System.Collections.Generic;
+using System.Collections.ObjectModel;
using System.Diagnostics;
using System.IO;
+using System.Linq;
using System.Runtime.InteropServices;
+using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using static Python.Runtime.Runtime;
namespace Python.Runtime
{
- class RuntimeData
+ public static class RuntimeData
{
- internal static void Stash()
+ private static Type _formatterType;
+ public static Type FormatterType
{
- var formatter = new BinaryFormatter();
- var ms = new MemoryStream();
- var stack = new Stack();
- MetaType.StashPush(stack);
- TypeManager.StashPush(stack);
- ClassManager.StashPush(stack);
- var objs = ManagedType.GetManagedObjects();
- var saveObjs = new Dictionary();
- foreach (var entry in objs)
+ get => _formatterType;
+ set
{
- var obj = entry.Key;
- if (entry.Value == ManagedType.TrackTypes.Wrapper
- && !obj.GetType().IsSerializable)
+ if (!typeof(IFormatter).IsAssignableFrom(value))
{
- // XXX: Skip non-serializable objects,
- // use them after next initialization will raise exceptions.
- continue;
+ throw new ArgumentException("Not a type implemented IFormatter");
}
- Debug.Assert(obj.GetType().IsSerializable);
- obj.Save();
- saveObjs.Add(entry.Key, entry.Value);
+ _formatterType = value;
}
- stack.Push(saveObjs);
- formatter.Serialize(ms, stack);
+ }
+
+ public static ICLRObjectStorer WrappersStorer { get; set; }
+
+ internal static void Stash()
+ {
+ var metaStorage = new RuntimeDataStorage();
+ MetaType.StashPush(metaStorage);
+
+ var typeStorage = new RuntimeDataStorage();
+ TypeManager.StashPush(typeStorage);
+
+ var clsStorage = new RuntimeDataStorage();
+ ClassManager.StashPush(clsStorage);
+ var moduleStorage = new RuntimeDataStorage();
+ StashPushModules(moduleStorage);
+
+ var objStorage = new RuntimeDataStorage();
+ StashPushObjects(objStorage);
+
+ var runtimeStorage = new RuntimeDataStorage();
+ runtimeStorage.AddValue("meta", metaStorage);
+ runtimeStorage.AddValue("types", typeStorage);
+ runtimeStorage.AddValue("classes", clsStorage);
+ runtimeStorage.AddValue("modules", moduleStorage);
+ runtimeStorage.AddValue("objs", objStorage);
+
+ IFormatter formatter = CreateFormatter();
+ var ms = new MemoryStream();
+ formatter.Serialize(ms, runtimeStorage);
+
+ Debug.Assert(ms.Length <= int.MaxValue);
byte[] data = ms.GetBuffer();
// TODO: use buffer api instead
- Debug.Assert(data.Length <= int.MaxValue);
- IntPtr mem = PyMem_Malloc(data.LongLength + IntPtr.Size);
- Marshal.WriteIntPtr(mem, (IntPtr)data.LongLength);
- Marshal.Copy(data, 0, mem + IntPtr.Size, data.Length);
+ IntPtr mem = PyMem_Malloc(ms.Length + IntPtr.Size);
+ Marshal.WriteIntPtr(mem, (IntPtr)ms.Length);
+ Marshal.Copy(data, 0, mem + IntPtr.Size, (int)ms.Length);
IntPtr capsule = PySys_GetObject("clr_data");
if (capsule != IntPtr.Zero)
@@ -58,7 +78,20 @@ internal static void Stash()
XDecref(capsule);
}
+
internal static void StashPop()
+ {
+ try
+ {
+ StashPopImpl();
+ }
+ finally
+ {
+ ClearStash();
+ }
+ }
+
+ private static void StashPopImpl()
{
IntPtr capsule = PySys_GetObject("clr_data");
if (capsule == IntPtr.Zero)
@@ -70,20 +103,14 @@ internal static void StashPop()
byte[] data = new byte[length];
Marshal.Copy(mem + IntPtr.Size, data, 0, length);
var ms = new MemoryStream(data);
- var formatter = new BinaryFormatter();
-
- var stack = (Stack)formatter.Deserialize(ms);
+ var formatter = CreateFormatter();
+ var storage = (RuntimeDataStorage)formatter.Deserialize(ms);
- var loadObjs = (IDictionary)stack.Pop();
- foreach (var entry in loadObjs)
- {
- ManagedType obj = entry.Key;
- obj.Load();
- }
- Debug.Assert(ManagedType.GetManagedObjects().Count == loadObjs.Count);
- ClassManager.StashPop(stack);
- TypeManager.StashPop(stack);
- PyCLRMetaType = MetaType.StashPop(stack);
+ StashPopModules(storage.GetStorage("modules"));
+ StashPopObjects(storage.GetStorage("objs"));
+ ClassManager.StashPop(storage.GetStorage("classes"));
+ TypeManager.StashPop(storage.GetStorage("types"));
+ PyCLRMetaType = MetaType.StashPop(storage.GetStorage("meta"));
}
public static bool HasStashData()
@@ -95,5 +122,233 @@ public static void ClearStash()
{
PySys_SetObject("clr_data", IntPtr.Zero);
}
+
+ private static void StashPushObjects(RuntimeDataStorage storage)
+ {
+ var objs = ManagedType.GetManagedObjects();
+ var extensionObjs = new List();
+ var wrappers = new Dictionary
diff --git a/src/embed_tests/packages.config b/src/embed_tests/packages.config
index 8c175f441..7a3fb37c4 100644
--- a/src/embed_tests/packages.config
+++ b/src/embed_tests/packages.config
@@ -1,5 +1,5 @@
-
-
+
+
diff --git a/src/perf_tests/Python.PerformanceTests.csproj b/src/perf_tests/Python.PerformanceTests.csproj
index 1231cef69..25af89db0 100644
--- a/src/perf_tests/Python.PerformanceTests.csproj
+++ b/src/perf_tests/Python.PerformanceTests.csproj
@@ -11,14 +11,17 @@
-
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
-
-
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
compile
From 6c0324525f9642c200d3d013d253e10d2ce0e44d Mon Sep 17 00:00:00 2001
From: Victor
Date: Wed, 26 Feb 2020 02:47:24 -0800
Subject: [PATCH 106/998] fixed reference counting for exception objects in
Py.With (#1062)
PyObject(s) constructed for __exit__ method referenced existing Python objects without increasing refcount appropriately, which could lead to double-free.
---
src/runtime/Util.cs | 11 +++++++++--
src/runtime/pythonengine.cs | 9 ++++++---
2 files changed, 15 insertions(+), 5 deletions(-)
diff --git a/src/runtime/Util.cs b/src/runtime/Util.cs
index dc5f78608..29a5170ab 100644
--- a/src/runtime/Util.cs
+++ b/src/runtime/Util.cs
@@ -3,7 +3,7 @@
namespace Python.Runtime
{
- internal class Util
+ internal static class Util
{
internal static Int64 ReadCLong(IntPtr tp, int offset)
{
@@ -29,5 +29,12 @@ internal static void WriteCLong(IntPtr type, int offset, Int64 flags)
Marshal.WriteInt64(type, offset, flags);
}
}
+
+ ///
+ /// Null-coalesce: if parameter is not
+ /// , return it. Otherwise return .
+ ///
+ internal static IntPtr Coalesce(this IntPtr primary, IntPtr fallback)
+ => primary == IntPtr.Zero ? fallback : primary;
}
-}
\ No newline at end of file
+}
diff --git a/src/runtime/pythonengine.cs b/src/runtime/pythonengine.cs
index aec2a412e..a2da04af3 100644
--- a/src/runtime/pythonengine.cs
+++ b/src/runtime/pythonengine.cs
@@ -758,11 +758,14 @@ public static void With(PyObject obj, Action Body)
catch (PythonException e)
{
ex = e;
- type = ex.PyType;
- val = ex.PyValue;
- traceBack = ex.PyTB;
+ type = ex.PyType.Coalesce(type);
+ val = ex.PyValue.Coalesce(val);
+ traceBack = ex.PyTB.Coalesce(traceBack);
}
+ Runtime.XIncref(type);
+ Runtime.XIncref(val);
+ Runtime.XIncref(traceBack);
var exitResult = obj.InvokeMethod("__exit__", new PyObject(type), new PyObject(val), new PyObject(traceBack));
if (ex != null && !exitResult.IsTrue()) throw ex;
From 4271e57967c75850c47f962a04bd036e987fee34 Mon Sep 17 00:00:00 2001
From: Victor
Date: Wed, 26 Feb 2020 02:48:17 -0800
Subject: [PATCH 107/998] added checks to
PyCheck_Iter_PyObject_IsIterable_ThreadingLock_Test in attempt to replace
segfault with a more meaningful exception (#1061)
related to https://github.com/pythonnet/pythonnet/issues/1060
---
src/embed_tests/TestRuntime.cs | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/src/embed_tests/TestRuntime.cs b/src/embed_tests/TestRuntime.cs
index 25b70fac5..157fe4cb7 100644
--- a/src/embed_tests/TestRuntime.cs
+++ b/src/embed_tests/TestRuntime.cs
@@ -1,4 +1,5 @@
using System;
+using System.Collections.Generic;
using NUnit.Framework;
using Python.Runtime;
using Python.Runtime.Platform;
@@ -110,9 +111,15 @@ public static void PyCheck_Iter_PyObject_IsIterable_ThreadingLock_Test()
// Create an instance of threading.Lock, which is one of the very few types that does not have the
// TypeFlags.HaveIter set in Python 2. This tests a different code path in PyObject_IsIterable and PyIter_Check.
var threading = Runtime.Runtime.PyImport_ImportModule("threading");
+ Exceptions.ErrorCheck(threading);
var threadingDict = Runtime.Runtime.PyModule_GetDict(threading);
+ Exceptions.ErrorCheck(threadingDict);
var lockType = Runtime.Runtime.PyDict_GetItemString(threadingDict, "Lock");
+ if (lockType == IntPtr.Zero)
+ throw new KeyNotFoundException("class 'Lock' was not found in 'threading'");
+
var lockInstance = Runtime.Runtime.PyObject_CallObject(lockType, Runtime.Runtime.PyTuple_New(0));
+ Exceptions.ErrorCheck(lockInstance);
Assert.IsFalse(Runtime.Runtime.PyObject_IsIterable(lockInstance));
Assert.IsFalse(Runtime.Runtime.PyIter_Check(lockInstance));
From 770fc01efc40cca298cf833a7556180c922c119b Mon Sep 17 00:00:00 2001
From: Victor
Date: Wed, 26 Feb 2020 03:21:20 -0800
Subject: [PATCH 108/998] Safe pointers (#1043)
* NewReference type and an example usage
* BorrowedReference + example, that exposes dangerous pattern
* make BorrowedReference readonly ref struct
* BorrowedReference.Pointer is a private readonly field
* renamed NewReference.ToPyObject to MoveToPyObject
* removed public property Pointer from NewReference and replaced with DangerousGetAddress
---
src/runtime/BorrowedReference.cs | 22 ++++++++++++++++
src/runtime/NewReference.cs | 39 ++++++++++++++++++++++++++++
src/runtime/NonCopyableAttribute.cs | 6 +++++
src/runtime/Python.Runtime.15.csproj | 7 +++++
src/runtime/Python.Runtime.csproj | 3 +++
src/runtime/assemblymanager.cs | 4 +--
src/runtime/methodbinder.cs | 2 +-
src/runtime/pydict.cs | 16 +++++++++---
src/runtime/runtime.cs | 8 +++---
9 files changed, 97 insertions(+), 10 deletions(-)
create mode 100644 src/runtime/BorrowedReference.cs
create mode 100644 src/runtime/NewReference.cs
create mode 100644 src/runtime/NonCopyableAttribute.cs
diff --git a/src/runtime/BorrowedReference.cs b/src/runtime/BorrowedReference.cs
new file mode 100644
index 000000000..7dbc7a811
--- /dev/null
+++ b/src/runtime/BorrowedReference.cs
@@ -0,0 +1,22 @@
+namespace Python.Runtime
+{
+ using System;
+ ///
+ /// Represents a reference to a Python object, that is being lent, and
+ /// can only be safely used until execution returns to the caller.
+ ///
+ readonly ref struct BorrowedReference
+ {
+ readonly IntPtr pointer;
+ public bool IsNull => this.pointer == IntPtr.Zero;
+
+ /// Gets a raw pointer to the Python object
+ public IntPtr DangerousGetAddress()
+ => this.IsNull ? throw new NullReferenceException() : this.pointer;
+
+ BorrowedReference(IntPtr pointer)
+ {
+ this.pointer = pointer;
+ }
+ }
+}
diff --git a/src/runtime/NewReference.cs b/src/runtime/NewReference.cs
new file mode 100644
index 000000000..3b45f821f
--- /dev/null
+++ b/src/runtime/NewReference.cs
@@ -0,0 +1,39 @@
+namespace Python.Runtime
+{
+ using System;
+ ///
+ /// Represents a reference to a Python object, that is tracked by Python's reference counting.
+ ///
+ [NonCopyable]
+ ref struct NewReference
+ {
+ IntPtr pointer;
+ public bool IsNull => this.pointer == IntPtr.Zero;
+
+ /// Gets a raw pointer to the Python object
+ public IntPtr DangerousGetAddress()
+ => this.IsNull ? throw new NullReferenceException() : this.pointer;
+
+ ///
+ /// Returns wrapper around this reference, which now owns
+ /// the pointer. Sets the original reference to null, as it no longer owns it.
+ ///
+ public PyObject MoveToPyObject()
+ {
+ if (this.IsNull) throw new NullReferenceException();
+
+ var result = new PyObject(this.pointer);
+ this.pointer = IntPtr.Zero;
+ return result;
+ }
+ ///
+ /// Removes this reference to a Python object, and sets it to null.
+ ///
+ public void Dispose()
+ {
+ if (!this.IsNull)
+ Runtime.XDecref(this.pointer);
+ this.pointer = IntPtr.Zero;
+ }
+ }
+}
diff --git a/src/runtime/NonCopyableAttribute.cs b/src/runtime/NonCopyableAttribute.cs
new file mode 100644
index 000000000..63d36ab42
--- /dev/null
+++ b/src/runtime/NonCopyableAttribute.cs
@@ -0,0 +1,6 @@
+namespace Python.Runtime
+{
+ using System;
+ [AttributeUsage(AttributeTargets.Struct)]
+ class NonCopyableAttribute : Attribute { }
+}
diff --git a/src/runtime/Python.Runtime.15.csproj b/src/runtime/Python.Runtime.15.csproj
index c31d4bf91..fd4f3416a 100644
--- a/src/runtime/Python.Runtime.15.csproj
+++ b/src/runtime/Python.Runtime.15.csproj
@@ -129,6 +129,13 @@
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
diff --git a/src/runtime/Python.Runtime.csproj b/src/runtime/Python.Runtime.csproj
index 0c2f912de..c79afee3e 100644
--- a/src/runtime/Python.Runtime.csproj
+++ b/src/runtime/Python.Runtime.csproj
@@ -83,6 +83,7 @@
+
@@ -119,6 +120,8 @@
+
+
diff --git a/src/runtime/assemblymanager.cs b/src/runtime/assemblymanager.cs
index 3085bb639..9d0296d47 100644
--- a/src/runtime/assemblymanager.cs
+++ b/src/runtime/assemblymanager.cs
@@ -145,7 +145,7 @@ internal static void UpdatePath()
probed.Clear();
for (var i = 0; i < count; i++)
{
- IntPtr item = Runtime.PyList_GetItem(list, i);
+ BorrowedReference item = Runtime.PyList_GetItem(list, i);
string path = Runtime.GetManagedString(item);
if (path != null)
{
@@ -492,4 +492,4 @@ internal static Type[] GetTypes(Assembly a)
}
}
}
-}
\ No newline at end of file
+}
diff --git a/src/runtime/methodbinder.cs b/src/runtime/methodbinder.cs
index 8a7fc1930..4e8698da1 100644
--- a/src/runtime/methodbinder.cs
+++ b/src/runtime/methodbinder.cs
@@ -292,7 +292,7 @@ internal Binding Bind(IntPtr inst, IntPtr args, IntPtr kw, MethodBase info, Meth
for (int i = 0; i < pynkwargs; ++i)
{
var keyStr = Runtime.GetManagedString(Runtime.PyList_GetItem(keylist, i));
- kwargDict[keyStr] = Runtime.PyList_GetItem(valueList, i);
+ kwargDict[keyStr] = Runtime.PyList_GetItem(valueList, i).DangerousGetAddress();
}
Runtime.XDecref(keylist);
Runtime.XDecref(valueList);
diff --git a/src/runtime/pydict.cs b/src/runtime/pydict.cs
index 7237d1990..b396f4f3d 100644
--- a/src/runtime/pydict.cs
+++ b/src/runtime/pydict.cs
@@ -139,12 +139,20 @@ public PyObject Values()
///
public PyObject Items()
{
- IntPtr items = Runtime.PyDict_Items(obj);
- if (items == IntPtr.Zero)
+ var items = Runtime.PyDict_Items(this.obj);
+ try
{
- throw new PythonException();
+ if (items.IsNull)
+ {
+ throw new PythonException();
+ }
+
+ return items.MoveToPyObject();
+ }
+ finally
+ {
+ items.Dispose();
}
- return new PyObject(items);
}
diff --git a/src/runtime/runtime.cs b/src/runtime/runtime.cs
index 9c7cb42d2..6d75e4bef 100644
--- a/src/runtime/runtime.cs
+++ b/src/runtime/runtime.cs
@@ -1509,6 +1509,8 @@ internal static IntPtr PyUnicode_FromString(string s)
return PyUnicode_FromUnicode(s, s.Length);
}
+ internal static string GetManagedString(in BorrowedReference borrowedReference)
+ => GetManagedString(borrowedReference.DangerousGetAddress());
///
/// Function to access the internal PyUnicode/PyString object and
/// convert it to a managed string with the correct encoding.
@@ -1591,7 +1593,7 @@ internal static bool PyDict_Check(IntPtr ob)
internal static extern IntPtr PyDict_Values(IntPtr pointer);
[DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)]
- internal static extern IntPtr PyDict_Items(IntPtr pointer);
+ internal static extern NewReference PyDict_Items(IntPtr pointer);
[DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)]
internal static extern IntPtr PyDict_Copy(IntPtr pointer);
@@ -1631,13 +1633,13 @@ internal static IntPtr PyList_New(long size)
[DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)]
internal static extern IntPtr PyList_AsTuple(IntPtr pointer);
- internal static IntPtr PyList_GetItem(IntPtr pointer, long index)
+ internal static BorrowedReference PyList_GetItem(IntPtr pointer, long index)
{
return PyList_GetItem(pointer, new IntPtr(index));
}
[DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)]
- private static extern IntPtr PyList_GetItem(IntPtr pointer, IntPtr index);
+ private static extern BorrowedReference PyList_GetItem(IntPtr pointer, IntPtr index);
internal static int PyList_SetItem(IntPtr pointer, long index, IntPtr value)
{
From aa63f0bcbcc1af0aa0488c13a2a3d1adaf97f8c6 Mon Sep 17 00:00:00 2001
From: amos402
Date: Thu, 27 Feb 2020 17:03:00 +0800
Subject: [PATCH 109/998] * Stash for ImportHook * Refactor Save/Load * Fix
refcnt error for moduleobject.cs
---
src/embed_tests/TestDomainReload.cs | 39 +++++++++++++++++------------
src/runtime/classbase.cs | 7 ++++++
src/runtime/classmanager.cs | 14 ++++++++++-
src/runtime/eventbinding.cs | 6 -----
src/runtime/exceptions.cs | 6 +----
src/runtime/extensiontype.cs | 6 -----
src/runtime/importhook.cs | 18 ++++++++++++-
src/runtime/managedtype.cs | 2 ++
src/runtime/moduleobject.cs | 6 +++++
src/runtime/runtime.cs | 9 ++++---
src/runtime/runtime_data.cs | 18 ++++++++++++-
src/runtime/typemanager.cs | 13 +++++++---
12 files changed, 102 insertions(+), 42 deletions(-)
diff --git a/src/embed_tests/TestDomainReload.cs b/src/embed_tests/TestDomainReload.cs
index c94c8599c..68e92e22c 100644
--- a/src/embed_tests/TestDomainReload.cs
+++ b/src/embed_tests/TestDomainReload.cs
@@ -233,26 +233,33 @@ public static void RunPython()
mode = ShutdownMode.Soft;
}
PythonEngine.Initialize(mode: mode);
- using (Py.GIL())
+ try
{
- try
- {
- var pyScript = string.Format("import clr\n"
- + "print('[{0} in python] imported clr')\n"
- + "clr.AddReference('System')\n"
- + "print('[{0} in python] allocated a clr object')\n"
- + "import gc\n"
- + "gc.collect()\n"
- + "print('[{0} in python] collected garbage')\n",
- name);
- PythonEngine.Exec(pyScript);
- }
- catch (Exception e)
+ using (Py.GIL())
{
- Console.WriteLine(string.Format("[{0} in .NET] Caught exception: {1}", name, e));
+ try
+ {
+ var pyScript = string.Format("import clr\n"
+ + "print('[{0} in python] imported clr')\n"
+ + "clr.AddReference('System')\n"
+ + "print('[{0} in python] allocated a clr object')\n"
+ + "import gc\n"
+ + "gc.collect()\n"
+ + "print('[{0} in python] collected garbage')\n",
+ name);
+ PythonEngine.Exec(pyScript);
+ }
+ catch (Exception e)
+ {
+ Console.WriteLine(string.Format("[{0} in .NET] Caught exception: {1}", name, e));
+ throw;
+ }
}
}
- PythonEngine.BeginAllowThreads();
+ finally
+ {
+ PythonEngine.BeginAllowThreads();
+ }
}
diff --git a/src/runtime/classbase.cs b/src/runtime/classbase.cs
index 3f10849fd..9a8b0db74 100644
--- a/src/runtime/classbase.cs
+++ b/src/runtime/classbase.cs
@@ -303,5 +303,12 @@ public static int tp_clear(IntPtr ob)
self.tpHandle = IntPtr.Zero;
return 0;
}
+
+ protected override void OnLoad()
+ {
+ base.OnLoad();
+ gcHandle = AllocGCHandle();
+ Marshal.WriteIntPtr(pyHandle, TypeOffset.magic(), (IntPtr)gcHandle);
+ }
}
}
diff --git a/src/runtime/classmanager.cs b/src/runtime/classmanager.cs
index 0c7fff5af..ee9006d1e 100644
--- a/src/runtime/classmanager.cs
+++ b/src/runtime/classmanager.cs
@@ -56,12 +56,14 @@ internal static void RemoveClasses()
cls.CallTypeTraverse(OnVisit, visitedPtr);
// XXX: Force release instance resources but not dealloc itself.
cls.CallTypeClear();
+ cls.DecrRefCount();
}
}
finally
{
visitedHandle.Free();
}
+ cache.Clear();
}
private static int OnVisit(IntPtr ob, IntPtr arg)
@@ -81,15 +83,25 @@ private static int OnVisit(IntPtr ob, IntPtr arg)
return 0;
}
-
internal static void StashPush(RuntimeDataStorage storage)
{
storage.PushValue(cache);
+ foreach (var cls in cache.Values)
+ {
+ // This incref is for cache to hold the cls,
+ // thus no need for decreasing it at StashPop.
+ Runtime.XIncref(cls.pyHandle);
+ cls.Save();
+ }
}
internal static void StashPop(RuntimeDataStorage storage)
{
cache = storage.PopValue>();
+ foreach (var cls in cache.Values)
+ {
+ cls.Load();
+ }
}
///
diff --git a/src/runtime/eventbinding.cs b/src/runtime/eventbinding.cs
index 5dbace9d9..581095185 100644
--- a/src/runtime/eventbinding.cs
+++ b/src/runtime/eventbinding.cs
@@ -128,11 +128,5 @@ public static int tp_clear(IntPtr ob)
Runtime.Py_CLEAR(ref self.target);
return 0;
}
-
- protected override void OnSave()
- {
- base.OnSave();
- Runtime.XIncref(target);
- }
}
}
diff --git a/src/runtime/exceptions.cs b/src/runtime/exceptions.cs
index 908de9745..b0c540782 100644
--- a/src/runtime/exceptions.cs
+++ b/src/runtime/exceptions.cs
@@ -89,15 +89,11 @@ internal static Exception ToException(IntPtr ob)
///
/// Readability of the Exceptions class improvements as we look toward version 2.7 ...
///
- public class Exceptions
+ public static class Exceptions
{
internal static IntPtr warnings_module;
internal static IntPtr exceptions_module;
- private Exceptions()
- {
- }
-
///
/// Initialization performed on startup of the Python runtime.
///
diff --git a/src/runtime/extensiontype.cs b/src/runtime/extensiontype.cs
index fc839644e..75ca04205 100644
--- a/src/runtime/extensiontype.cs
+++ b/src/runtime/extensiontype.cs
@@ -98,12 +98,6 @@ public static void tp_dealloc(IntPtr ob)
self.Dealloc();
}
- protected override void OnSave()
- {
- base.OnSave();
- Runtime.XIncref(pyHandle);
- }
-
protected override void OnLoad()
{
base.OnLoad();
diff --git a/src/runtime/importhook.cs b/src/runtime/importhook.cs
index aa3bbab6d..e6e6c252a 100644
--- a/src/runtime/importhook.cs
+++ b/src/runtime/importhook.cs
@@ -6,7 +6,7 @@ namespace Python.Runtime
///
/// Implements the "import hook" used to integrate Python with the CLR.
///
- internal class ImportHook
+ internal static class ImportHook
{
private static IntPtr py_import;
private static CLRModule root;
@@ -130,6 +130,22 @@ internal static void Shutdown()
CLRModule.Reset();
}
+ internal static void StashPush(RuntimeDataStorage storage)
+ {
+ Runtime.XIncref(py_clr_module);
+ Runtime.XIncref(root.pyHandle);
+ storage.AddValue("py_clr_module", py_clr_module);
+ storage.AddValue("root", root.pyHandle);
+ }
+
+ internal static void StashPop(RuntimeDataStorage storage)
+ {
+ InitImport();
+ storage.GetValue("py_clr_module", out py_clr_module);
+ var rootHandle = storage.GetValue("root");
+ root = (CLRModule)ManagedType.GetManagedObject(rootHandle);
+ }
+
///
/// Return the clr python module (new reference)
///
diff --git a/src/runtime/managedtype.cs b/src/runtime/managedtype.cs
index 99e897570..9fc1a6424 100644
--- a/src/runtime/managedtype.cs
+++ b/src/runtime/managedtype.cs
@@ -202,12 +202,14 @@ protected void TypeClear()
internal void Save()
{
+ Runtime.XIncref(pyHandle);
OnSave();
}
internal void Load()
{
OnLoad();
+ Runtime.XDecref(pyHandle);
}
protected virtual void OnSave() { }
diff --git a/src/runtime/moduleobject.cs b/src/runtime/moduleobject.cs
index 8661d74ae..c6a4a5ca0 100644
--- a/src/runtime/moduleobject.cs
+++ b/src/runtime/moduleobject.cs
@@ -103,6 +103,7 @@ public ManagedType GetAttribute(string name, bool guess)
{
m = new ModuleObject(qname);
StoreAttribute(name, m);
+ m.DecrRefCount();
return m;
}
@@ -118,6 +119,7 @@ public ManagedType GetAttribute(string name, bool guess)
}
c = ClassManager.GetClass(type);
StoreAttribute(name, c);
+ c.DecrRefCount();
return c;
}
@@ -132,6 +134,7 @@ public ManagedType GetAttribute(string name, bool guess)
{
m = new ModuleObject(qname);
StoreAttribute(name, m);
+ m.DecrRefCount();
return m;
}
@@ -144,6 +147,7 @@ public ManagedType GetAttribute(string name, bool guess)
}
c = ClassManager.GetClass(type);
StoreAttribute(name, c);
+ c.DecrRefCount();
return c;
}
}
@@ -239,6 +243,7 @@ internal void InitializeModuleMembers()
mi[0] = method;
var m = new ModuleFunctionObject(type, name, mi, allow_threads);
StoreAttribute(name, m);
+ m.DecrRefCount();
}
}
@@ -251,6 +256,7 @@ internal void InitializeModuleMembers()
string name = property.Name;
var p = new ModulePropertyObject(property);
StoreAttribute(name, p);
+ p.DecrRefCount();
}
}
type = type.BaseType;
diff --git a/src/runtime/runtime.cs b/src/runtime/runtime.cs
index 66c23c87b..5124a46e7 100644
--- a/src/runtime/runtime.cs
+++ b/src/runtime/runtime.cs
@@ -197,9 +197,9 @@ internal static void Initialize(bool initSigs = false, ShutdownMode mode = Shutd
#endif
{
PyCLRMetaType = MetaType.Initialize(); // Steal a reference
+ ImportHook.Initialize();
}
Exceptions.Initialize();
- ImportHook.Initialize();
// Need to add the runtime directory to sys.path so that we
// can find built-in assemblies like System.Data, et. al.
@@ -497,13 +497,16 @@ private static void MoveClrInstancesOnwershipToPython()
obj.CallTypeClear();
// obj's tp_type will degenerate to a pure Python type after TypeManager.RemoveTypes(),
// thus just be safe to give it back to GC chain.
- PyObject_GC_Track(obj.pyHandle);
+ if (!_PyObject_GC_IS_TRACKED(obj.pyHandle))
+ {
+ PyObject_GC_Track(obj.pyHandle);
+ }
}
if (obj.gcHandle.IsAllocated)
{
obj.gcHandle.Free();
}
- obj.gcHandle = new GCHandle();
+ obj.gcHandle = default;
}
ManagedType.ClearTrackedObjects();
}
diff --git a/src/runtime/runtime_data.cs b/src/runtime/runtime_data.cs
index a4cab5a20..96e8ea85b 100644
--- a/src/runtime/runtime_data.cs
+++ b/src/runtime/runtime_data.cs
@@ -36,6 +36,9 @@ internal static void Stash()
var metaStorage = new RuntimeDataStorage();
MetaType.StashPush(metaStorage);
+ var importStorage = new RuntimeDataStorage();
+ ImportHook.StashPush(importStorage);
+
var typeStorage = new RuntimeDataStorage();
TypeManager.StashPush(typeStorage);
@@ -50,6 +53,7 @@ internal static void Stash()
var runtimeStorage = new RuntimeDataStorage();
runtimeStorage.AddValue("meta", metaStorage);
+ runtimeStorage.AddValue("import", importStorage);
runtimeStorage.AddValue("types", typeStorage);
runtimeStorage.AddValue("classes", clsStorage);
runtimeStorage.AddValue("modules", moduleStorage);
@@ -106,10 +110,11 @@ private static void StashPopImpl()
var formatter = CreateFormatter();
var storage = (RuntimeDataStorage)formatter.Deserialize(ms);
- StashPopModules(storage.GetStorage("modules"));
StashPopObjects(storage.GetStorage("objs"));
+ StashPopModules(storage.GetStorage("modules"));
ClassManager.StashPop(storage.GetStorage("classes"));
TypeManager.StashPop(storage.GetStorage("types"));
+ ImportHook.StashPop(storage.GetStorage("import"));
PyCLRMetaType = MetaType.StashPop(storage.GetStorage("meta"));
}
@@ -287,6 +292,12 @@ public T GetValue(string name)
return (T)GetValue(name);
}
+ public T GetValue(string name, out T value)
+ {
+ value = GetValue(name);
+ return value;
+ }
+
public RuntimeDataStorage GetStorage(string name)
{
return GetValue(name);
@@ -311,6 +322,11 @@ public T PopValue()
{
return (T)PopValue();
}
+
+ public T PopValue(out T value)
+ {
+ return value = (T)PopValue();
+ }
}
diff --git a/src/runtime/typemanager.cs b/src/runtime/typemanager.cs
index 48716a967..b552e2cae 100644
--- a/src/runtime/typemanager.cs
+++ b/src/runtime/typemanager.cs
@@ -22,6 +22,7 @@ internal class TypeManager
private const BindingFlags tbFlags = BindingFlags.Public | BindingFlags.Static;
private static Dictionary cache = new Dictionary();
private static readonly Dictionary _slotsHolders = new Dictionary();
+ private static Dictionary _slotsImpls = new Dictionary();
// Slots which must be set
private static readonly string[] _requiredSlots = new string[]
@@ -62,6 +63,7 @@ internal static void RemoveTypes()
Runtime.XDecref(tpHandle);
}
cache.Clear();
+ _slotsImpls.Clear();
_slotsHolders.Clear();
}
@@ -71,19 +73,21 @@ internal static void StashPush(RuntimeDataStorage storage)
{
Runtime.XIncref(tpHandle);
}
- storage.PushValue(cache);
+ storage.AddValue("cache", cache);
+ storage.AddValue("slots", _slotsImpls);
}
internal static void StashPop(RuntimeDataStorage storage)
{
Debug.Assert(cache == null || cache.Count == 0);
- cache = storage.PopValue>();
+ storage.GetValue("slots", out _slotsImpls);
+ storage.GetValue("cache", out cache);
foreach (var entry in cache)
{
Type type = entry.Key;
IntPtr handle = entry.Value;
SlotsHolder holder = CreateSolotsHolder(handle);
- InitializeSlots(handle, type, holder);
+ InitializeSlots(handle, _slotsImpls[type], holder);
// FIXME: mp_length_slot.CanAssgin(clrType)
}
}
@@ -107,6 +111,7 @@ internal static IntPtr GetTypeHandle(Type type)
}
handle = CreateType(type);
cache[type] = handle;
+ _slotsImpls.Add(type, type);
return handle;
}
@@ -127,6 +132,7 @@ internal static IntPtr GetTypeHandle(ManagedType obj, Type type)
}
handle = CreateType(obj, type);
cache[type] = handle;
+ _slotsImpls.Add(type, obj.GetType());
return handle;
}
@@ -711,6 +717,7 @@ private static void InitMethods(IntPtr pytype, Type type)
mi[0] = method;
MethodObject m = new TypeMethod(type, method_name, mi);
Runtime.PyDict_SetItemString(dict, method_name, m.pyHandle);
+ //m.DecrRefCount();
addedMethods.Add(method_name);
}
}
From 35cbe55b285923bc7253d545d3ae6f530703f4b3 Mon Sep 17 00:00:00 2001
From: amos402
Date: Thu, 27 Feb 2020 18:26:33 +0800
Subject: [PATCH 110/998] Apply Reference type usage
---
src/runtime/runtime.cs | 10 +++++-----
src/runtime/runtime_data.cs | 9 +++++----
src/runtime/runtime_state.cs | 4 ++--
3 files changed, 12 insertions(+), 11 deletions(-)
diff --git a/src/runtime/runtime.cs b/src/runtime/runtime.cs
index 2f255c995..d3faa5f24 100644
--- a/src/runtime/runtime.cs
+++ b/src/runtime/runtime.cs
@@ -446,18 +446,18 @@ private static void ClearClrModules()
{
var modules = PyImport_GetModuleDict();
var items = PyDict_Items(modules);
- long length = PyList_Size(items);
+ long length = PyList_Size(items.DangerousGetAddress());
for (long i = 0; i < length; i++)
{
- var item = PyList_GetItem(items, i);
- var name = PyTuple_GetItem(item, 0);
- var module = PyTuple_GetItem(item, 1);
+ var item = PyList_GetItem(items.DangerousGetAddress(), i);
+ var name = PyTuple_GetItem(item.DangerousGetAddress(), 0);
+ var module = PyTuple_GetItem(item.DangerousGetAddress(), 1);
if (ManagedType.IsManagedType(module))
{
PyDict_DelItem(modules, name);
}
}
- XDecref(items);
+ items.Dispose();
}
private static void RemoveClrRootModule()
diff --git a/src/runtime/runtime_data.cs b/src/runtime/runtime_data.cs
index 96e8ea85b..f8cc1f682 100644
--- a/src/runtime/runtime_data.cs
+++ b/src/runtime/runtime_data.cs
@@ -224,14 +224,15 @@ private static void StashPopObjects(RuntimeDataStorage storage)
private static void StashPushModules(RuntimeDataStorage storage)
{
var pyModules = PyImport_GetModuleDict();
- var items = PyDict_Items(pyModules);
+ var itemsRef = PyDict_Items(pyModules);
+ var items = itemsRef.DangerousGetAddress();
long length = PyList_Size(items);
var modules = new Dictionary(); ;
for (long i = 0; i < length; i++)
{
var item = PyList_GetItem(items, i);
- var name = PyTuple_GetItem(item, 0);
- var module = PyTuple_GetItem(item, 1);
+ var name = PyTuple_GetItem(item.DangerousGetAddress(), 0);
+ var module = PyTuple_GetItem(item.DangerousGetAddress(), 1);
if (ManagedType.IsManagedType(module))
{
XIncref(name);
@@ -239,7 +240,7 @@ private static void StashPushModules(RuntimeDataStorage storage)
modules.Add(name, module);
}
}
- XDecref(items);
+ itemsRef.Dispose();
storage.AddValue("modules", modules);
}
diff --git a/src/runtime/runtime_state.cs b/src/runtime/runtime_state.cs
index ca3fecbb2..9c3823315 100644
--- a/src/runtime/runtime_state.cs
+++ b/src/runtime/runtime_state.cs
@@ -152,7 +152,7 @@ public static IEnumerable PyGCGetObjects()
for (long i = 0; i < length; i++)
{
var obj = PyList_GetItem(objs, i);
- yield return obj;
+ yield return obj.DangerousGetAddress();
}
XDecref(objs);
XDecref(gc);
@@ -166,7 +166,7 @@ public static IEnumerable GetModuleNames()
for (int i = 0; i < length; i++)
{
var name = PyList_GetItem(names, i);
- yield return name;
+ yield return name.DangerousGetAddress();
}
}
From 8e108b4a09a4ce6a2a116162587d9587cb42f781 Mon Sep 17 00:00:00 2001
From: Victor
Date: Fri, 28 Feb 2020 23:55:05 -0800
Subject: [PATCH 111/998] Disable 3.8 tests in AppVeyor temporarily, as they
never pass yet anyway (#1058)
---
appveyor.yml | 6 ------
1 file changed, 6 deletions(-)
diff --git a/appveyor.yml b/appveyor.yml
index 20d8ed991..445f9bb5a 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -23,22 +23,16 @@ environment:
BUILD_OPTS: --xplat
- PYTHON_VERSION: 3.7
BUILD_OPTS: --xplat
- - PYTHON_VERSION: 3.8
- BUILD_OPTS: --xplat
- PYTHON_VERSION: 2.7
- PYTHON_VERSION: 3.5
- PYTHON_VERSION: 3.6
- PYTHON_VERSION: 3.7
- - PYTHON_VERSION: 3.8
matrix:
allow_failures:
- PYTHON_VERSION: 3.4
BUILD_OPTS: --xplat
- PYTHON_VERSION: 3.4
- - PYTHON_VERSION: 3.8
- BUILD_OPTS: --xplat
- - PYTHON_VERSION: 3.8
init:
# Update Environment Variables based on matrix/platform
From f23cae63b9944fd65a706456b9ad090ad1e94bb2 Mon Sep 17 00:00:00 2001
From: amos402
Date: Mon, 2 Mar 2020 12:07:00 +0800
Subject: [PATCH 112/998] * Manipulate refcnt in Push/Pop objects * Add several
Serializable mark
---
src/embed_tests/TestFinalizer.cs | 2 +-
src/runtime/arrayobject.cs | 1 +
src/runtime/classderived.cs | 1 +
src/runtime/classmanager.cs | 3 +--
src/runtime/exceptions.cs | 1 +
src/runtime/generictype.cs | 1 +
src/runtime/interfaceobject.cs | 1 +
src/runtime/managedtype.cs | 2 --
src/runtime/pyobject.cs | 4 ++--
src/runtime/runtime_data.cs | 17 ++++++++++++++---
10 files changed, 23 insertions(+), 10 deletions(-)
diff --git a/src/embed_tests/TestFinalizer.cs b/src/embed_tests/TestFinalizer.cs
index 650ee5686..53497d7cd 100644
--- a/src/embed_tests/TestFinalizer.cs
+++ b/src/embed_tests/TestFinalizer.cs
@@ -214,7 +214,7 @@ public void ValidateRefCount()
{
if (!Finalizer.Instance.RefCountValidationEnabled)
{
- Assert.Pass("Only run with FINALIZER_CHECK");
+ Assert.Ignore("Only run with FINALIZER_CHECK");
}
IntPtr ptr = IntPtr.Zero;
bool called = false;
diff --git a/src/runtime/arrayobject.cs b/src/runtime/arrayobject.cs
index 1ef318473..cf7067ace 100644
--- a/src/runtime/arrayobject.cs
+++ b/src/runtime/arrayobject.cs
@@ -8,6 +8,7 @@ namespace Python.Runtime
/// the same as a ClassObject, except that it provides sequence semantics
/// to support natural array usage (indexing) from Python.
///
+ [Serializable]
internal class ArrayObject : ClassBase
{
internal ArrayObject(Type tp) : base(tp)
diff --git a/src/runtime/classderived.cs b/src/runtime/classderived.cs
index f9c019cfe..ab2bda3bf 100644
--- a/src/runtime/classderived.cs
+++ b/src/runtime/classderived.cs
@@ -22,6 +22,7 @@ public interface IPythonDerivedType
{
}
+ [Serializable]
internal class ClassDerivedObject : ClassObject
{
private static Dictionary assemblyBuilders;
diff --git a/src/runtime/classmanager.cs b/src/runtime/classmanager.cs
index ee9006d1e..0ef6f5e42 100644
--- a/src/runtime/classmanager.cs
+++ b/src/runtime/classmanager.cs
@@ -69,11 +69,10 @@ internal static void RemoveClasses()
private static int OnVisit(IntPtr ob, IntPtr arg)
{
var visited = (HashSet)GCHandle.FromIntPtr(arg).Target;
- if (visited.Contains(ob))
+ if (!visited.Add(ob))
{
return 0;
}
- visited.Add(ob);
var clrObj = ManagedType.GetManagedObject(ob);
if (clrObj != null)
{
diff --git a/src/runtime/exceptions.cs b/src/runtime/exceptions.cs
index b0c540782..1af91d1b8 100644
--- a/src/runtime/exceptions.cs
+++ b/src/runtime/exceptions.cs
@@ -15,6 +15,7 @@ namespace Python.Runtime
/// 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)
diff --git a/src/runtime/generictype.cs b/src/runtime/generictype.cs
index eeae801d2..76d2e9a5d 100644
--- a/src/runtime/generictype.cs
+++ b/src/runtime/generictype.cs
@@ -8,6 +8,7 @@ namespace Python.Runtime
/// generic types. Both are essentially factories for creating closed
/// types based on the required generic type parameters.
///
+ [Serializable]
internal class GenericType : ClassBase
{
internal GenericType(Type tp) : base(tp)
diff --git a/src/runtime/interfaceobject.cs b/src/runtime/interfaceobject.cs
index 616ced6bd..536c8796f 100644
--- a/src/runtime/interfaceobject.cs
+++ b/src/runtime/interfaceobject.cs
@@ -10,6 +10,7 @@ namespace Python.Runtime
/// Each of those type objects is associated with an instance of this
/// class, which provides the implementation for the Python type.
///
+ [Serializable]
internal class InterfaceObject : ClassBase
{
internal ConstructorInfo ctor;
diff --git a/src/runtime/managedtype.cs b/src/runtime/managedtype.cs
index 9fc1a6424..99e897570 100644
--- a/src/runtime/managedtype.cs
+++ b/src/runtime/managedtype.cs
@@ -202,14 +202,12 @@ protected void TypeClear()
internal void Save()
{
- Runtime.XIncref(pyHandle);
OnSave();
}
internal void Load()
{
OnLoad();
- Runtime.XDecref(pyHandle);
}
protected virtual void OnSave() { }
diff --git a/src/runtime/pyobject.cs b/src/runtime/pyobject.cs
index 58359e5c5..da6c73b18 100644
--- a/src/runtime/pyobject.cs
+++ b/src/runtime/pyobject.cs
@@ -20,8 +20,8 @@ public interface IPyDisposable : IDisposable
/// PY3: https://docs.python.org/3/c-api/object.html
/// for details.
///
- //[Serializable]
- public class PyObject : DynamicObject, IEnumerable, IPyDisposable
+ [Serializable]
+ public partial class PyObject : DynamicObject, IEnumerable, IPyDisposable
{
#if TRACE_ALLOC
///
diff --git a/src/runtime/runtime_data.cs b/src/runtime/runtime_data.cs
index f8cc1f682..83bdb23ba 100644
--- a/src/runtime/runtime_data.cs
+++ b/src/runtime/runtime_data.cs
@@ -110,12 +110,17 @@ private static void StashPopImpl()
var formatter = CreateFormatter();
var storage = (RuntimeDataStorage)formatter.Deserialize(ms);
- StashPopObjects(storage.GetStorage("objs"));
+ var objs = StashPopObjects(storage.GetStorage("objs"));
StashPopModules(storage.GetStorage("modules"));
ClassManager.StashPop(storage.GetStorage("classes"));
TypeManager.StashPop(storage.GetStorage("types"));
ImportHook.StashPop(storage.GetStorage("import"));
PyCLRMetaType = MetaType.StashPop(storage.GetStorage("meta"));
+
+ foreach (var item in objs)
+ {
+ XDecref(item.pyHandle);
+ }
}
public static bool HasStashData()
@@ -137,6 +142,7 @@ private static void StashPushObjects(RuntimeDataStorage storage)
foreach (var entry in objs)
{
var obj = entry.Key;
+ XIncref(obj.pyHandle);
switch (entry.Value)
{
case ManagedType.TrackTypes.Extension:
@@ -190,6 +196,7 @@ private static void StashPushObjects(RuntimeDataStorage storage)
}
foreach (var clrObj in wrappers[item.Instance])
{
+ XIncref(clrObj.pyHandle);
clrObj.Save();
}
}
@@ -198,13 +205,15 @@ private static void StashPushObjects(RuntimeDataStorage storage)
storage.AddValue("wrappers", wrapperStorage);
}
- private static void StashPopObjects(RuntimeDataStorage storage)
+ private static IEnumerable StashPopObjects(RuntimeDataStorage storage)
{
var extensions = storage.GetValue>("extensions");
var internalStores = storage.GetValue>("internalStores");
+ var storedObjs = new List();
foreach (var obj in Enumerable.Union(extensions, internalStores))
{
obj.Load();
+ storedObjs.Add(obj);
}
if (WrappersStorer != null)
{
@@ -215,10 +224,12 @@ private static void StashPopObjects(RuntimeDataStorage storage)
object obj = item.Instance;
foreach (var handle in item.Handles)
{
- CLRObject.Restore(obj, handle);
+ var co = CLRObject.Restore(obj, handle);
+ storedObjs.Add(co);
}
}
}
+ return storedObjs;
}
private static void StashPushModules(RuntimeDataStorage storage)
From d93217621991064eeccef9f2e4a9fdd9261d2ec5 Mon Sep 17 00:00:00 2001
From: Victor
Date: Mon, 2 Mar 2020 00:17:59 -0800
Subject: [PATCH 113/998] correctly dispose the result of PyRun_String (#1071)
this also changes a few members of NewReference type to make them work in PyRun_String scenario
---
src/embed_tests/Python.EmbeddingTest.csproj | 1 +
src/embed_tests/References.cs | 40 +++++++++++++++++++++
src/runtime/NewReference.cs | 34 ++++++++++++++----
src/runtime/Python.Runtime.csproj | 1 +
src/runtime/ReferenceExtensions.cs | 20 +++++++++++
src/runtime/pydict.cs | 2 +-
src/runtime/pyscope.cs | 31 +++++++++++-----
src/runtime/pythonengine.cs | 22 ++++++++----
src/runtime/runtime.cs | 2 +-
9 files changed, 129 insertions(+), 24 deletions(-)
create mode 100644 src/embed_tests/References.cs
create mode 100644 src/runtime/ReferenceExtensions.cs
diff --git a/src/embed_tests/Python.EmbeddingTest.csproj b/src/embed_tests/Python.EmbeddingTest.csproj
index 7a0964a8a..a191290ef 100644
--- a/src/embed_tests/Python.EmbeddingTest.csproj
+++ b/src/embed_tests/Python.EmbeddingTest.csproj
@@ -88,6 +88,7 @@
+
diff --git a/src/embed_tests/References.cs b/src/embed_tests/References.cs
new file mode 100644
index 000000000..4c7124907
--- /dev/null
+++ b/src/embed_tests/References.cs
@@ -0,0 +1,40 @@
+namespace Python.EmbeddingTest
+{
+ using NUnit.Framework;
+ using Python.Runtime;
+
+ public class References
+ {
+ private Py.GILState _gs;
+
+ [SetUp]
+ public void SetUp()
+ {
+ _gs = Py.GIL();
+ }
+
+ [TearDown]
+ public void Dispose()
+ {
+ _gs.Dispose();
+ }
+
+ [Test]
+ public void MoveToPyObject_SetsNull()
+ {
+ var dict = new PyDict();
+ NewReference reference = Runtime.PyDict_Items(dict.Handle);
+ try
+ {
+ Assert.IsFalse(reference.IsNull());
+
+ using (reference.MoveToPyObject())
+ Assert.IsTrue(reference.IsNull());
+ }
+ finally
+ {
+ reference.Dispose();
+ }
+ }
+ }
+}
diff --git a/src/runtime/NewReference.cs b/src/runtime/NewReference.cs
index 3b45f821f..bbeb86dc4 100644
--- a/src/runtime/NewReference.cs
+++ b/src/runtime/NewReference.cs
@@ -1,6 +1,8 @@
namespace Python.Runtime
{
using System;
+ using System.Diagnostics.Contracts;
+
///
/// Represents a reference to a Python object, that is tracked by Python's reference counting.
///
@@ -8,11 +10,6 @@ namespace Python.Runtime
ref struct NewReference
{
IntPtr pointer;
- public bool IsNull => this.pointer == IntPtr.Zero;
-
- /// Gets a raw pointer to the Python object
- public IntPtr DangerousGetAddress()
- => this.IsNull ? throw new NullReferenceException() : this.pointer;
///
/// Returns wrapper around this reference, which now owns
@@ -20,7 +17,7 @@ public IntPtr DangerousGetAddress()
///
public PyObject MoveToPyObject()
{
- if (this.IsNull) throw new NullReferenceException();
+ if (this.IsNull()) throw new NullReferenceException();
var result = new PyObject(this.pointer);
this.pointer = IntPtr.Zero;
@@ -31,9 +28,32 @@ public PyObject MoveToPyObject()
///
public void Dispose()
{
- if (!this.IsNull)
+ if (!this.IsNull())
Runtime.XDecref(this.pointer);
this.pointer = IntPtr.Zero;
}
+
+ [Pure]
+ internal static IntPtr DangerousGetAddress(in NewReference reference)
+ => IsNull(reference) ? throw new NullReferenceException() : reference.pointer;
+ [Pure]
+ internal static bool IsNull(in NewReference reference)
+ => reference.pointer == IntPtr.Zero;
+ }
+
+ ///
+ /// These members can not be directly in type,
+ /// because this is always passed by value, which we need to avoid.
+ /// (note this in NewReference vs the usual this NewReference)
+ ///
+ static class NewReferenceExtensions
+ {
+ /// Gets a raw pointer to the Python object
+ [Pure]
+ public static IntPtr DangerousGetAddress(this in NewReference reference)
+ => NewReference.DangerousGetAddress(reference);
+ [Pure]
+ public static bool IsNull(this in NewReference reference)
+ => NewReference.IsNull(reference);
}
}
diff --git a/src/runtime/Python.Runtime.csproj b/src/runtime/Python.Runtime.csproj
index 1d40c2a38..fd2d35bde 100644
--- a/src/runtime/Python.Runtime.csproj
+++ b/src/runtime/Python.Runtime.csproj
@@ -141,6 +141,7 @@
+
diff --git a/src/runtime/ReferenceExtensions.cs b/src/runtime/ReferenceExtensions.cs
new file mode 100644
index 000000000..8fa2731b7
--- /dev/null
+++ b/src/runtime/ReferenceExtensions.cs
@@ -0,0 +1,20 @@
+namespace Python.Runtime
+{
+ using System.Diagnostics.Contracts;
+
+ static class ReferenceExtensions
+ {
+ ///
+ /// Checks if the reference points to Python object None.
+ ///
+ [Pure]
+ public static bool IsNone(this in NewReference reference)
+ => reference.DangerousGetAddress() == Runtime.PyNone;
+ ///
+ /// Checks if the reference points to Python object None.
+ ///
+ [Pure]
+ public static bool IsNone(this BorrowedReference reference)
+ => reference.DangerousGetAddress() == Runtime.PyNone;
+ }
+}
diff --git a/src/runtime/pydict.cs b/src/runtime/pydict.cs
index b396f4f3d..7ff7a83c8 100644
--- a/src/runtime/pydict.cs
+++ b/src/runtime/pydict.cs
@@ -142,7 +142,7 @@ public PyObject Items()
var items = Runtime.PyDict_Items(this.obj);
try
{
- if (items.IsNull)
+ if (items.IsNull())
{
throw new PythonException();
}
diff --git a/src/runtime/pyscope.cs b/src/runtime/pyscope.cs
index 4008ce29a..8738824f5 100644
--- a/src/runtime/pyscope.cs
+++ b/src/runtime/pyscope.cs
@@ -278,11 +278,19 @@ public PyObject Eval(string code, PyDict locals = null)
Check();
IntPtr _locals = locals == null ? variables : locals.obj;
var flag = (IntPtr)Runtime.Py_eval_input;
- IntPtr ptr = Runtime.PyRun_String(
+
+ NewReference reference = Runtime.PyRun_String(
code, flag, variables, _locals
);
- Runtime.CheckExceptionOccurred();
- return new PyObject(ptr);
+ try
+ {
+ Runtime.CheckExceptionOccurred();
+ return reference.MoveToPyObject();
+ }
+ finally
+ {
+ reference.Dispose();
+ }
}
///
@@ -316,15 +324,22 @@ public void Exec(string code, PyDict locals = null)
private void Exec(string code, IntPtr _globals, IntPtr _locals)
{
var flag = (IntPtr)Runtime.Py_file_input;
- IntPtr ptr = Runtime.PyRun_String(
+ NewReference reference = Runtime.PyRun_String(
code, flag, _globals, _locals
);
- Runtime.CheckExceptionOccurred();
- if (ptr != Runtime.PyNone)
+
+ try
{
- throw new PythonException();
+ Runtime.CheckExceptionOccurred();
+ if (!reference.IsNone())
+ {
+ throw new PythonException();
+ }
+ }
+ finally
+ {
+ reference.Dispose();
}
- Runtime.XDecref(ptr);
}
///
diff --git a/src/runtime/pythonengine.cs b/src/runtime/pythonengine.cs
index d5492ebb9..df2d98641 100644
--- a/src/runtime/pythonengine.cs
+++ b/src/runtime/pythonengine.cs
@@ -543,12 +543,13 @@ public static PyObject Eval(string code, IntPtr? globals = null, IntPtr? locals
///
public static void Exec(string code, IntPtr? globals = null, IntPtr? locals = null)
{
- PyObject result = RunString(code, globals, locals, RunFlagType.File);
- if (result.obj != Runtime.PyNone)
+ using (PyObject result = RunString(code, globals, locals, RunFlagType.File))
{
- throw new PythonException();
+ if (result.obj != Runtime.PyNone)
+ {
+ throw new PythonException();
+ }
}
- result.Dispose();
}
@@ -594,13 +595,20 @@ internal static PyObject RunString(string code, IntPtr? globals, IntPtr? locals,
try
{
- IntPtr result = Runtime.PyRun_String(
+ NewReference result = Runtime.PyRun_String(
code, (IntPtr)flag, globals.Value, locals.Value
);
- Runtime.CheckExceptionOccurred();
+ try
+ {
+ Runtime.CheckExceptionOccurred();
- return new PyObject(result);
+ return result.MoveToPyObject();
+ }
+ finally
+ {
+ result.Dispose();
+ }
}
finally
{
diff --git a/src/runtime/runtime.cs b/src/runtime/runtime.cs
index 963c9f475..9c9d674a6 100644
--- a/src/runtime/runtime.cs
+++ b/src/runtime/runtime.cs
@@ -802,7 +802,7 @@ public static extern int Py_Main(
internal static extern int PyRun_SimpleString(string code);
[DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)]
- internal static extern IntPtr PyRun_String(string code, IntPtr st, IntPtr globals, IntPtr locals);
+ internal static extern NewReference PyRun_String(string code, IntPtr st, IntPtr globals, IntPtr locals);
[DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)]
internal static extern IntPtr PyEval_EvalCode(IntPtr co, IntPtr globals, IntPtr locals);
From 8ad10620a5a76b6bb326c45c9af86a2672106d95 Mon Sep 17 00:00:00 2001
From: Victor
Date: Tue, 3 Mar 2020 00:27:46 -0800
Subject: [PATCH 114/998] Enable C# parameters of type `object` accept any
argument, passed from Python (#889)
* added a regression test for #881
* when converting to object, wrap values of unknown type into PyObject instead of failing
This enables overload resolution with object parameters to behave the same way PyObject parameters behave - e.g. allow any Python object to be passed as PyObject as fallback.
Resolves #811
* fixed ObjectField conversion test
* fixed test_object_indexer to pass on custom class key
* use object() instance in OverloadResolution_UnknownToObject test
---
CHANGELOG.md | 1 +
src/embed_tests/Python.EmbeddingTest.csproj | 1 +
src/embed_tests/TestInstanceWrapping.cs | 58 +++++++++++++++++++++
src/runtime/converter.cs | 9 ++--
src/tests/test_conversion.py | 9 ++--
src/tests/test_indexer.py | 12 ++---
6 files changed, 73 insertions(+), 17 deletions(-)
create mode 100644 src/embed_tests/TestInstanceWrapping.cs
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 82fccbe35..5bc0c9981 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -58,6 +58,7 @@ This document follows the conventions laid out in [Keep a CHANGELOG][].
- Reattach python exception traceback information (#545)
- PythonEngine.Intialize will now call `Py_InitializeEx` with a default value of 0, so signals will not be configured by default on embedding. This is different from the previous behaviour, where `Py_Initialize` was called instead, which sets initSigs to 1. ([#449][i449])
- Refactored MethodBinder.Bind in preparation to make it extensible (#829)
+- When calling C# from Python, enable passing argument of any type to a parameter of C# type `object` by wrapping it into `PyObject` instance. ([#881][i881])
- Look for installed Windows 10 sdk's during installation instead of relying on specific versions.
### Fixed
diff --git a/src/embed_tests/Python.EmbeddingTest.csproj b/src/embed_tests/Python.EmbeddingTest.csproj
index a191290ef..9c5f97711 100644
--- a/src/embed_tests/Python.EmbeddingTest.csproj
+++ b/src/embed_tests/Python.EmbeddingTest.csproj
@@ -94,6 +94,7 @@
+
diff --git a/src/embed_tests/TestInstanceWrapping.cs b/src/embed_tests/TestInstanceWrapping.cs
new file mode 100644
index 000000000..8be207c00
--- /dev/null
+++ b/src/embed_tests/TestInstanceWrapping.cs
@@ -0,0 +1,58 @@
+using System;
+using System.Globalization;
+using NUnit.Framework;
+using Python.Runtime;
+
+namespace Python.EmbeddingTest
+{
+ public class TestInstanceWrapping
+ {
+ [OneTimeSetUp]
+ public void SetUp()
+ {
+ PythonEngine.Initialize();
+ }
+
+ [OneTimeTearDown]
+ public void Dispose()
+ {
+ PythonEngine.Shutdown();
+ }
+
+ // regression test for https://github.com/pythonnet/pythonnet/issues/811
+ [Test]
+ public void OverloadResolution_UnknownToObject()
+ {
+ var overloaded = new Overloaded();
+ using (Py.GIL())
+ {
+ var o = overloaded.ToPython();
+
+ dynamic callWithSelf = PythonEngine.Eval("lambda o: o.ObjOrClass(object())");
+ callWithSelf(o);
+ Assert.AreEqual(Overloaded.Object, overloaded.Value);
+ }
+ }
+
+ class Base {}
+ class Derived: Base { }
+
+ class Overloaded: Derived
+ {
+ public int Value { get; set; }
+ public void IntOrStr(int arg) => this.Value = arg;
+ public void IntOrStr(string arg) =>
+ this.Value = int.Parse(arg, NumberStyles.Integer, CultureInfo.InvariantCulture);
+
+ public const int Object = 1;
+ public const int ConcreteClass = 2;
+ public void ObjOrClass(object _) => this.Value = Object;
+ public void ObjOrClass(Overloaded _) => this.Value = ConcreteClass;
+
+ public const int Base = ConcreteClass + 1;
+ public const int Derived = Base + 1;
+ public void BaseOrDerived(Base _) => this.Value = Base;
+ public void BaseOrDerived(Derived _) => this.Value = Derived;
+ }
+ }
+}
diff --git a/src/runtime/converter.cs b/src/runtime/converter.cs
index 7c53bdcb1..3add8aba0 100644
--- a/src/runtime/converter.cs
+++ b/src/runtime/converter.cs
@@ -398,12 +398,9 @@ internal static bool ToManagedValue(IntPtr value, Type obType,
return ToArray(value, typeof(object[]), out result, setError);
}
- if (setError)
- {
- Exceptions.SetError(Exceptions.TypeError, "value cannot be converted to Object");
- }
-
- return false;
+ Runtime.XIncref(value); // PyObject() assumes ownership
+ result = new PyObject(value);
+ return true;
}
// Conversion to 'Type' is done using the same mappings as above for objects.
diff --git a/src/tests/test_conversion.py b/src/tests/test_conversion.py
index 0ba10a80e..e61eda26c 100644
--- a/src/tests/test_conversion.py
+++ b/src/tests/test_conversion.py
@@ -595,11 +595,10 @@ def test_object_conversion():
# need to test subclass here
- with pytest.raises(TypeError):
- class Foo(object):
- pass
- ob = ConversionTest()
- ob.ObjectField = Foo
+ class Foo(object):
+ pass
+ ob.ObjectField = Foo
+ assert ob.ObjectField == Foo
def test_enum_conversion():
diff --git a/src/tests/test_indexer.py b/src/tests/test_indexer.py
index 6f18550d9..ca4fd3b89 100644
--- a/src/tests/test_indexer.py
+++ b/src/tests/test_indexer.py
@@ -438,13 +438,13 @@ def test_object_indexer():
ob[long(1)] = "long"
assert ob[long(1)] == "long"
- with pytest.raises(TypeError):
- class Eggs(object):
- pass
+ class Eggs(object):
+ pass
- key = Eggs()
- ob = Test.ObjectIndexerTest()
- ob[key] = "wrong"
+ key = Eggs()
+ ob = Test.ObjectIndexerTest()
+ ob[key] = "eggs_key"
+ assert ob[key] == "eggs_key"
def test_interface_indexer():
From 9fd877e555b2469c848a2726140377a87e039cf5 Mon Sep 17 00:00:00 2001
From: Victor
Date: Tue, 3 Mar 2020 10:52:02 -0800
Subject: [PATCH 115/998] reimplemented some of the PyList members using
BorrowedReference (#1068)
---
src/embed_tests/pyimport.cs | 2 +-
src/runtime/BorrowedReference.cs | 5 ++++-
src/runtime/pylist.cs | 8 ++++----
src/runtime/pyobject.cs | 19 ++++++++++++++++++-
src/runtime/runtime.cs | 12 ++++++------
5 files changed, 33 insertions(+), 13 deletions(-)
diff --git a/src/embed_tests/pyimport.cs b/src/embed_tests/pyimport.cs
index acb3433de..6b2408745 100644
--- a/src/embed_tests/pyimport.cs
+++ b/src/embed_tests/pyimport.cs
@@ -39,7 +39,7 @@ public void SetUp()
IntPtr str = Runtime.Runtime.PyString_FromString(testPath);
IntPtr path = Runtime.Runtime.PySys_GetObject("path");
- Runtime.Runtime.PyList_Append(path, str);
+ Runtime.Runtime.PyList_Append(new BorrowedReference(path), str);
}
[TearDown]
diff --git a/src/runtime/BorrowedReference.cs b/src/runtime/BorrowedReference.cs
index 7dbc7a811..a3bf29056 100644
--- a/src/runtime/BorrowedReference.cs
+++ b/src/runtime/BorrowedReference.cs
@@ -14,7 +14,10 @@ readonly ref struct BorrowedReference
public IntPtr DangerousGetAddress()
=> this.IsNull ? throw new NullReferenceException() : this.pointer;
- BorrowedReference(IntPtr pointer)
+ ///
+ /// Creates new instance of from raw pointer. Unsafe.
+ ///
+ public BorrowedReference(IntPtr pointer)
{
this.pointer = pointer;
}
diff --git a/src/runtime/pylist.cs b/src/runtime/pylist.cs
index b22d9d51f..347cc3000 100644
--- a/src/runtime/pylist.cs
+++ b/src/runtime/pylist.cs
@@ -120,7 +120,7 @@ public static PyList AsList(PyObject value)
///
public void Append(PyObject item)
{
- int r = Runtime.PyList_Append(obj, item.obj);
+ int r = Runtime.PyList_Append(this.Reference, item.obj);
if (r < 0)
{
throw new PythonException();
@@ -135,7 +135,7 @@ public void Append(PyObject item)
///
public void Insert(int index, PyObject item)
{
- int r = Runtime.PyList_Insert(obj, index, item.obj);
+ int r = Runtime.PyList_Insert(this.Reference, index, item.obj);
if (r < 0)
{
throw new PythonException();
@@ -151,7 +151,7 @@ public void Insert(int index, PyObject item)
///
public void Reverse()
{
- int r = Runtime.PyList_Reverse(obj);
+ int r = Runtime.PyList_Reverse(this.Reference);
if (r < 0)
{
throw new PythonException();
@@ -167,7 +167,7 @@ public void Reverse()
///
public void Sort()
{
- int r = Runtime.PyList_Sort(obj);
+ int r = Runtime.PyList_Sort(this.Reference);
if (r < 0)
{
throw new PythonException();
diff --git a/src/runtime/pyobject.cs b/src/runtime/pyobject.cs
index 8ae99ecd0..37d53eeec 100644
--- a/src/runtime/pyobject.cs
+++ b/src/runtime/pyobject.cs
@@ -33,6 +33,8 @@ public class PyObject : DynamicObject, IEnumerable, IPyDisposable
private bool disposed = false;
private bool _finalized = false;
+ internal BorrowedReference Reference => new BorrowedReference(obj);
+
///
/// PyObject Constructor
///
@@ -52,9 +54,24 @@ public PyObject(IntPtr ptr)
#endif
}
+ ///
+ /// Creates new pointing to the same object as
+ /// the . Increments refcount, allowing
+ /// to have ownership over its own reference.
+ ///
+ internal PyObject(BorrowedReference reference)
+ {
+ if (reference.IsNull) throw new ArgumentNullException(nameof(reference));
+
+ obj = Runtime.SelfIncRef(reference.DangerousGetAddress());
+#if TRACE_ALLOC
+ Traceback = new StackTrace(1);
+#endif
+ }
+
// Protected default constructor to allow subclasses to manage
// initialization in different ways as appropriate.
- [Obsolete("Please, always use PyObject(IntPtr)")]
+ [Obsolete("Please, always use PyObject(*Reference)")]
protected PyObject()
{
#if TRACE_ALLOC
diff --git a/src/runtime/runtime.cs b/src/runtime/runtime.cs
index 9c9d674a6..bae3daa15 100644
--- a/src/runtime/runtime.cs
+++ b/src/runtime/runtime.cs
@@ -341,7 +341,7 @@ internal static void Initialize(bool initSigs = false)
string rtdir = RuntimeEnvironment.GetRuntimeDirectory();
IntPtr path = PySys_GetObject("path");
IntPtr item = PyString_FromString(rtdir);
- PyList_Append(path, item);
+ PyList_Append(new BorrowedReference(path), item);
XDecref(item);
AssemblyManager.UpdatePath();
}
@@ -1658,22 +1658,22 @@ internal static int PyList_SetItem(IntPtr pointer, long index, IntPtr value)
[DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)]
private static extern int PyList_SetItem(IntPtr pointer, IntPtr index, IntPtr value);
- internal static int PyList_Insert(IntPtr pointer, long index, IntPtr value)
+ internal static int PyList_Insert(BorrowedReference pointer, long index, IntPtr value)
{
return PyList_Insert(pointer, new IntPtr(index), value);
}
[DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)]
- private static extern int PyList_Insert(IntPtr pointer, IntPtr index, IntPtr value);
+ private static extern int PyList_Insert(BorrowedReference pointer, IntPtr index, IntPtr value);
[DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)]
- internal static extern int PyList_Append(IntPtr pointer, IntPtr value);
+ internal static extern int PyList_Append(BorrowedReference pointer, IntPtr value);
[DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)]
- internal static extern int PyList_Reverse(IntPtr pointer);
+ internal static extern int PyList_Reverse(BorrowedReference pointer);
[DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)]
- internal static extern int PyList_Sort(IntPtr pointer);
+ internal static extern int PyList_Sort(BorrowedReference pointer);
internal static IntPtr PyList_GetSlice(IntPtr pointer, long start, long end)
{
From 653a9329e5360bc683a852172d66c6e587ad2f07 Mon Sep 17 00:00:00 2001
From: Victor
Date: Thu, 5 Mar 2020 22:44:45 -0800
Subject: [PATCH 116/998] adds extension object.GetRawPythonProxy() (#1078)
GetRawPythonProxy creates a PyObject pointing to the specified object without performing any coversions, which lets .NET code to pass CLR objects as-is, if it needs Python to have direct access to them.
This enables codecs to create arbitrary proxy objects, bypassing default conversions or other registered codecs.
---
CHANGELOG.md | 3 ++-
pythonnet.15.sln | 3 +++
src/embed_tests/TestConverter.cs | 23 +++++++++++++++++++++++
src/runtime/NewReference.cs | 6 ++++++
src/runtime/clrobject.cs | 12 ++++++++++++
src/runtime/converter.cs | 11 +++++++++++
6 files changed, 57 insertions(+), 1 deletion(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5bc0c9981..db126bd1c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -13,6 +13,7 @@ This document follows the conventions laid out in [Keep a CHANGELOG][].
- Added function that sets Py_NoSiteFlag to 1.
- Added support for Jetson Nano.
- Added support for __len__ for .NET classes that implement ICollection
+- Added `object.GetRawPythonProxy() -> PyObject` extension method, that bypasses any conversions
### Changed
@@ -21,6 +22,7 @@ This document follows the conventions laid out in [Keep a CHANGELOG][].
- Removes PyLong_GetMax and PyClass_New when targetting Python3
- Added support for converting python iterators to C# arrays
- Changed usage of obselete function GetDelegateForFunctionPointer(IntPtr, Type) to GetDelegateForFunctionPointer(IntPtr)
+- When calling C# from Python, enable passing argument of any type to a parameter of C# type `object` by wrapping it into `PyObject` instance. ([#881][i881])
- Added support for kwarg parameters when calling .NET methods from Python
### Fixed
@@ -58,7 +60,6 @@ This document follows the conventions laid out in [Keep a CHANGELOG][].
- Reattach python exception traceback information (#545)
- PythonEngine.Intialize will now call `Py_InitializeEx` with a default value of 0, so signals will not be configured by default on embedding. This is different from the previous behaviour, where `Py_Initialize` was called instead, which sets initSigs to 1. ([#449][i449])
- Refactored MethodBinder.Bind in preparation to make it extensible (#829)
-- When calling C# from Python, enable passing argument of any type to a parameter of C# type `object` by wrapping it into `PyObject` instance. ([#881][i881])
- Look for installed Windows 10 sdk's during installation instead of relying on specific versions.
### Fixed
diff --git a/pythonnet.15.sln b/pythonnet.15.sln
index 6d1f4fcd9..ce863817f 100644
--- a/pythonnet.15.sln
+++ b/pythonnet.15.sln
@@ -17,6 +17,9 @@ EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Repo", "Repo", "{441A0123-F4C6-4EE4-9AEE-315FD79BE2D5}"
ProjectSection(SolutionItems) = preProject
.editorconfig = .editorconfig
+ .gitignore = .gitignore
+ CHANGELOG.md = CHANGELOG.md
+ README.rst = README.rst
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "CI", "CI", "{D301657F-5EAF-4534-B280-B858D651B2E5}"
diff --git a/src/embed_tests/TestConverter.cs b/src/embed_tests/TestConverter.cs
index caaec311b..078f4c0f8 100644
--- a/src/embed_tests/TestConverter.cs
+++ b/src/embed_tests/TestConverter.cs
@@ -1,3 +1,5 @@
+using System;
+using System.Collections.Generic;
using NUnit.Framework;
using Python.Runtime;
@@ -44,5 +46,26 @@ public void TestConvertDoubleToManaged(
Assert.IsTrue(converted);
Assert.IsTrue(((double) convertedValue).Equals(testValue));
}
+
+ [Test]
+ public void RawListProxy()
+ {
+ var list = new List {"hello", "world"};
+ var listProxy = list.GetRawPythonProxy();
+ var clrObject = (CLRObject)ManagedType.GetManagedObject(listProxy.Handle);
+ Assert.AreSame(list, clrObject.inst);
+ }
+
+ [Test]
+ public void RawPyObjectProxy()
+ {
+ var pyObject = "hello world!".ToPython();
+ var pyObjectProxy = pyObject.GetRawPythonProxy();
+ var clrObject = (CLRObject)ManagedType.GetManagedObject(pyObjectProxy.Handle);
+ Assert.AreSame(pyObject, clrObject.inst);
+
+ var proxiedHandle = pyObjectProxy.GetAttr("Handle").As();
+ Assert.AreEqual(pyObject.Handle, proxiedHandle);
+ }
}
}
diff --git a/src/runtime/NewReference.cs b/src/runtime/NewReference.cs
index bbeb86dc4..3ab4b6530 100644
--- a/src/runtime/NewReference.cs
+++ b/src/runtime/NewReference.cs
@@ -33,6 +33,12 @@ public void Dispose()
this.pointer = IntPtr.Zero;
}
+ ///
+ /// Creates from a raw pointer
+ ///
+ public static NewReference DangerousFromPointer(IntPtr pointer)
+ => new NewReference {pointer = pointer};
+
[Pure]
internal static IntPtr DangerousGetAddress(in NewReference reference)
=> IsNull(reference) ? throw new NullReferenceException() : reference.pointer;
diff --git a/src/runtime/clrobject.cs b/src/runtime/clrobject.cs
index 502677655..13c15f862 100644
--- a/src/runtime/clrobject.cs
+++ b/src/runtime/clrobject.cs
@@ -68,5 +68,17 @@ internal static IntPtr GetInstHandle(object ob)
CLRObject co = GetInstance(ob);
return co.pyHandle;
}
+
+ ///
+ /// Creates proxy for the given object,
+ /// and returns a to it.
+ ///
+ internal static NewReference MakeNewReference(object obj)
+ {
+ if (obj is null) throw new ArgumentNullException(nameof(obj));
+
+ // TODO: CLRObject currently does not have Dispose or finalizer which might change in the future
+ return NewReference.DangerousFromPointer(GetInstHandle(obj));
+ }
}
}
diff --git a/src/runtime/converter.cs b/src/runtime/converter.cs
index 3add8aba0..a7b7b5c48 100644
--- a/src/runtime/converter.cs
+++ b/src/runtime/converter.cs
@@ -967,5 +967,16 @@ public static PyObject ToPython(this object o)
{
return new PyObject(Converter.ToPython(o, o?.GetType()));
}
+
+ ///
+ /// Gets raw Python proxy for this object (bypasses all conversions,
+ /// except null <==> None)
+ ///
+ public static PyObject GetRawPythonProxy(this object o)
+ {
+ if (o is null) return new PyObject(new BorrowedReference(Runtime.PyNone));
+
+ return CLRObject.MakeNewReference(o).MoveToPyObject();
+ }
}
}
From 925c1660d73883b9636c27d3732c328a321cebb8 Mon Sep 17 00:00:00 2001
From: Victor
Date: Thu, 5 Mar 2020 22:45:42 -0800
Subject: [PATCH 117/998] Fix synchronization in PyScopeTest.TestThread as
suggested in da97502006791bb0597446766ad00a6f9d291895 (#1070)
Fixes #1067.
---
src/embed_tests/TestPyScope.cs | 13 ++++++-------
1 file changed, 6 insertions(+), 7 deletions(-)
diff --git a/src/embed_tests/TestPyScope.cs b/src/embed_tests/TestPyScope.cs
index 7a4aa0228..701e698ec 100644
--- a/src/embed_tests/TestPyScope.cs
+++ b/src/embed_tests/TestPyScope.cs
@@ -338,16 +338,16 @@ public void TestThread()
//add function to the scope
//can be call many times, more efficient than ast
ps.Exec(
- "import clr\n" +
- "from System.Threading import Thread\n" +
+ "import threading\n" +
+ "lock = threading.Lock()\n" +
"def update():\n" +
- " global res, th_cnt\n" +
+ " global res, th_cnt\n" +
+ " with lock:\n" +
" res += bb + 1\n" +
- " Thread.MemoryBarrier()\n" +
" th_cnt += 1\n"
);
}
- int th_cnt = 3;
+ int th_cnt = 100;
for (int i = 0; i < th_cnt; i++)
{
System.Threading.Thread th = new System.Threading.Thread(() =>
@@ -368,9 +368,8 @@ public void TestThread()
{
cnt = ps.Get("th_cnt");
}
- Thread.Sleep(10);
+ Thread.Yield();
}
- Thread.MemoryBarrier();
using (Py.GIL())
{
var result = ps.Get("res");
From 183f9d822c17b1cba7f3538b8d4394b7ef1cae44 Mon Sep 17 00:00:00 2001
From: amos402
Date: Sun, 8 Mar 2020 15:51:00 +0800
Subject: [PATCH 118/998] Load cache of ModuleObject after reload
---
src/runtime/fieldobject.cs | 1 +
src/runtime/moduleobject.cs | 37 +++++++++++++++++++++++++++++++++++--
src/runtime/runtime.cs | 6 +++++-
3 files changed, 41 insertions(+), 3 deletions(-)
diff --git a/src/runtime/fieldobject.cs b/src/runtime/fieldobject.cs
index c4675d723..86b93dd1b 100644
--- a/src/runtime/fieldobject.cs
+++ b/src/runtime/fieldobject.cs
@@ -6,6 +6,7 @@ namespace Python.Runtime
///
/// Implements a Python descriptor type that provides access to CLR fields.
///
+ [Serializable]
internal class FieldObject : ExtensionType
{
private FieldInfo info;
diff --git a/src/runtime/moduleobject.cs b/src/runtime/moduleobject.cs
index ca6abd05d..4a1a15ca8 100644
--- a/src/runtime/moduleobject.cs
+++ b/src/runtime/moduleobject.cs
@@ -17,6 +17,10 @@ internal class ModuleObject : ExtensionType
[NonSerialized]
private Dictionary cache;
+ [NonSerialized]
+ // FIXME: Used by reload mode, remove it after implement a delay load handler.
+ private bool _cacheInited;
+
internal string moduleName;
internal IntPtr dict;
protected string _namespace;
@@ -29,6 +33,7 @@ public ModuleObject(string name)
}
moduleName = name;
cache = new Dictionary();
+ _cacheInited = true;
_namespace = name;
// Use the filename from any of the assemblies just so there's something for
@@ -72,6 +77,11 @@ public ModuleObject(string name)
///
public ManagedType GetAttribute(string name, bool guess)
{
+ if (!_cacheInited)
+ {
+ // XXX: Used by reload mode.
+ SetupCacheByDict();
+ }
ManagedType cached = null;
cache.TryGetValue(name, out cached);
if (cached != null)
@@ -116,7 +126,6 @@ public ManagedType GetAttribute(string name, bool guess)
{
c = ClassManager.GetClass(type);
StoreAttribute(name, c);
- c.DecrRefCount();
return c;
}
@@ -140,7 +149,6 @@ public ManagedType GetAttribute(string name, bool guess)
{
c = ClassManager.GetClass(type);
StoreAttribute(name, c);
- c.DecrRefCount();
return c;
}
}
@@ -364,9 +372,34 @@ protected override void OnSave()
protected override void OnLoad()
{
base.OnLoad();
+ // XXX: Set the cache after all objects loaded.
cache = new Dictionary();
+ _cacheInited = false;
SetObjectDict(pyHandle, dict);
}
+
+ private void SetupCacheByDict()
+ {
+ System.Diagnostics.Debug.Assert(!_cacheInited);
+ _cacheInited = true;
+ IntPtr key, value;
+ IntPtr pos;
+ while (Runtime.PyDict_Next(dict, out pos, out key, out value) != 0)
+ {
+ ManagedType obj = GetManagedObject(value);
+ if (obj == null)
+ {
+ continue;
+ }
+ string name = Runtime.GetManagedString(key);
+ if (cache.ContainsKey(name))
+ {
+ continue;
+ }
+ Runtime.XIncref(value);
+ cache.Add(name, obj);
+ }
+ }
}
///
diff --git a/src/runtime/runtime.cs b/src/runtime/runtime.cs
index d3faa5f24..4cedabbf9 100644
--- a/src/runtime/runtime.cs
+++ b/src/runtime/runtime.cs
@@ -490,6 +490,7 @@ private static void MoveClrInstancesOnwershipToPython()
ManagedType obj = entry.Key;
if (!objs.ContainsKey(obj))
{
+ System.Diagnostics.Debug.Assert(obj.gcHandle == default);
continue;
}
if (entry.Value == ManagedType.TrackTypes.Extension)
@@ -1675,6 +1676,9 @@ internal static bool PyDict_Check(IntPtr ob)
[DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)]
internal static extern IntPtr PyDict_New();
+ [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern int PyDict_Next(IntPtr p, out IntPtr ppos, out IntPtr pkey, out IntPtr pvalue);
+
[DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)]
internal static extern IntPtr PyDictProxy_New(IntPtr dict);
@@ -2004,7 +2008,7 @@ internal static IntPtr PyType_GenericAlloc(IntPtr type, long n)
private static extern IntPtr PyType_GenericAlloc(IntPtr type, IntPtr n);
///
- /// Finalize a type object. This should be called on all type objects to finish their initialization. This function is responsible for adding inherited slots from a types base class. Return 0 on success, or return -1 and sets an exception on error.
+ /// Finalize a type object. This should be called on all type objects to finish their initialization. This function is responsible for adding inherited slots from a type’s base class. Return 0 on success, or return -1 and sets an exception on error.
///
[DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)]
internal static extern int PyType_Ready(IntPtr type);
From 9d57a82732053f408b8d115d1035177d5c077bef Mon Sep 17 00:00:00 2001
From: amos402
Date: Sun, 8 Mar 2020 16:21:24 +0800
Subject: [PATCH 119/998] Add temp tests reference by
https://github.com/pythonnet/pythonnet/pull/1074#issuecomment-596139665
---
src/embed_tests/TestDomainReload.cs | 86 +++++++++++++++++++++++++++++
1 file changed, 86 insertions(+)
diff --git a/src/embed_tests/TestDomainReload.cs b/src/embed_tests/TestDomainReload.cs
index 68e92e22c..9e5f67d82 100644
--- a/src/embed_tests/TestDomainReload.cs
+++ b/src/embed_tests/TestDomainReload.cs
@@ -1,6 +1,7 @@
using System;
using System.Diagnostics;
using System.Reflection;
+using System.Runtime.InteropServices;
using NUnit.Framework;
using Python.Runtime;
@@ -115,6 +116,71 @@ public static void CrossDomainObject()
}
}
+
+ #region Tempary tests
+ // https://github.com/pythonnet/pythonnet/pull/1074#issuecomment-596139665
+ [Test]
+ public void CrossReleaseBuiltinType()
+ {
+ try
+ {
+ var numRef = CreateNumReference();
+ GC.Collect();
+ GC.WaitForPendingFinalizers(); // <- this will put former `num` into Finalizer queue
+ Finalizer.Instance.Collect(forceDispose: true);
+ // ^- this will call PyObject.Dispose, which will call XDecref on `num.Handle`,
+ // but Python interpreter from "run" 1 is long gone, so it will corrupt memory instead.
+ Assert.False(numRef.IsAlive);
+ }
+ finally
+ {
+ PythonEngine.Shutdown();
+ }
+ }
+
+ [Test]
+ public void CrossReleaseCustomType()
+ {
+ try
+ {
+ var objRef = CreateConcreateObject();
+ GC.Collect();
+ GC.WaitForPendingFinalizers();
+ Finalizer.Instance.Collect(forceDispose: true);
+ Assert.False(objRef.IsAlive);
+ }
+ finally
+ {
+ PythonEngine.Shutdown();
+ }
+ }
+
+ private static WeakReference CreateNumReference()
+ {
+ PythonEngine.Initialize();
+ var num = 3216757418.ToPython();
+ Assert.AreEqual(num.Refcount, 1);
+ WeakReference numRef = new WeakReference(num, false);
+ PythonEngine.Shutdown(); // <- "run" 1 ends
+ PythonEngine.Initialize(); // <- "run" 2 starts
+ num = null;
+ return numRef;
+ }
+
+ private static WeakReference CreateConcreateObject()
+ {
+ PythonEngine.Initialize();
+ var obj = new Domain.MyClass().ToPython();
+ Assert.AreEqual(obj.Refcount, 1);
+ WeakReference numRef = new WeakReference(obj, false);
+ PythonEngine.Shutdown();
+ PythonEngine.Initialize();
+ obj = null;
+ return numRef;
+ }
+
+ #endregion Tempary tests
+
///
/// This is a magic incantation required to run code in an application
/// domain other than the current one.
@@ -305,6 +371,8 @@ from Python.EmbeddingTest.Domain import MyClass
obj = MyClass()
obj.Method()
obj.StaticMethod()
+obj.Property = 1
+obj.Field = 10
", Assembly.GetExecutingAssembly().FullName);
using (Py.GIL())
@@ -333,11 +401,27 @@ public static void RunTestObject(IntPtr handle)
{
using (Py.GIL())
{
+ IntPtr tp = Runtime.Runtime.PyObject_TYPE(handle);
+ IntPtr tp_clear = Marshal.ReadIntPtr(tp, TypeOffset.tp_clear);
using (PyObject obj = new PyObject(handle))
{
obj.InvokeMethod("Method");
obj.InvokeMethod("StaticMethod");
+
+ using (var scope = Py.CreateScope())
+ {
+ scope.Set("obj", obj);
+ scope.Exec(@"
+obj.Method()
+obj.StaticMethod()
+obj.Property += 1
+obj.Field += 10
+");
+ }
+ var clrObj = obj.As();
+ Assert.AreEqual(clrObj.Property, 2);
+ Assert.AreEqual(clrObj.Field, 20);
}
}
}
@@ -370,6 +454,8 @@ namespace Python.EmbeddingTest.Domain
[Serializable]
public class MyClass
{
+ public int Property { get; set; }
+ public int Field;
public void Method() { }
public static void StaticMethod() { }
}
From 08fad26dda7fb52d98dce548fddf2302eb99fd3a Mon Sep 17 00:00:00 2001
From: amos402
Date: Mon, 9 Mar 2020 23:44:59 +0800
Subject: [PATCH 120/998] * Fix refcnt error of MethodBinding * Custom storage
context
---
src/runtime/classbase.cs | 21 ++++++++++--
src/runtime/classmanager.cs | 18 +++++++---
src/runtime/clrobject.cs | 13 ++++---
src/runtime/extensiontype.cs | 4 +--
src/runtime/managedtype.cs | 12 +++----
src/runtime/methodbinding.cs | 7 ++++
src/runtime/methodobject.cs | 12 ++++++-
src/runtime/moduleobject.cs | 51 +++++-----------------------
src/runtime/runtime_data.cs | 66 ++++++++++++++++++++++++++++++------
9 files changed, 128 insertions(+), 76 deletions(-)
diff --git a/src/runtime/classbase.cs b/src/runtime/classbase.cs
index 9a8b0db74..5bae9b350 100644
--- a/src/runtime/classbase.cs
+++ b/src/runtime/classbase.cs
@@ -2,6 +2,7 @@
using System.Collections;
using System.Diagnostics;
using System.Runtime.InteropServices;
+using System.Runtime.Serialization;
namespace Python.Runtime
{
@@ -304,9 +305,25 @@ public static int tp_clear(IntPtr ob)
return 0;
}
- protected override void OnLoad()
+ protected override void OnSave(PyObjectSerializeContext context)
{
- base.OnLoad();
+ base.OnSave(context);
+ if (pyHandle != tpHandle)
+ {
+ IntPtr dict = GetObjectDict(pyHandle);
+ Runtime.XIncref(dict);
+ context.Storage.AddValue("dict", dict);
+ }
+ }
+
+ protected override void OnLoad(PyObjectSerializeContext context)
+ {
+ base.OnLoad(context);
+ if (pyHandle != tpHandle)
+ {
+ IntPtr dict = context.Storage.GetValue("dict");
+ SetObjectDict(pyHandle, dict);
+ }
gcHandle = AllocGCHandle();
Marshal.WriteIntPtr(pyHandle, TypeOffset.magic(), (IntPtr)gcHandle);
}
diff --git a/src/runtime/classmanager.cs b/src/runtime/classmanager.cs
index 0ef6f5e42..a7d2d74e2 100644
--- a/src/runtime/classmanager.cs
+++ b/src/runtime/classmanager.cs
@@ -84,23 +84,31 @@ private static int OnVisit(IntPtr ob, IntPtr arg)
internal static void StashPush(RuntimeDataStorage storage)
{
- storage.PushValue(cache);
+ var contexts = storage.AddValue("contexts",
+ new Dictionary());
+ storage.AddValue("cache", cache);
foreach (var cls in cache.Values)
{
// This incref is for cache to hold the cls,
// thus no need for decreasing it at StashPop.
Runtime.XIncref(cls.pyHandle);
- cls.Save();
+ var context = contexts[cls.pyHandle] = new PyObjectSerializeContext();
+ cls.Save(context);
}
}
- internal static void StashPop(RuntimeDataStorage storage)
+ internal static Dictionary StashPop(RuntimeDataStorage storage)
{
- cache = storage.PopValue>();
+ cache = storage.GetValue>("cache");
+ var contexts = storage.GetValue >("contexts");
+ var loadedObjs = new Dictionary();
foreach (var cls in cache.Values)
{
- cls.Load();
+ var context = contexts[cls.pyHandle];
+ cls.Load(context);
+ loadedObjs.Add(cls, context);
}
+ return loadedObjs;
}
///
diff --git a/src/runtime/clrobject.cs b/src/runtime/clrobject.cs
index a8af80f67..f5fe45f5a 100644
--- a/src/runtime/clrobject.cs
+++ b/src/runtime/clrobject.cs
@@ -74,7 +74,7 @@ internal static IntPtr GetInstHandle(object ob)
return co.pyHandle;
}
- internal static CLRObject Restore(object ob, IntPtr pyHandle)
+ internal static CLRObject Restore(object ob, IntPtr pyHandle, PyObjectSerializeContext context)
{
CLRObject co = new CLRObject()
{
@@ -82,22 +82,21 @@ internal static CLRObject Restore(object ob, IntPtr pyHandle)
pyHandle = pyHandle,
tpHandle = Runtime.PyObject_TYPE(pyHandle)
};
- co.Load();
+ co.Load(context);
return co;
}
- protected override void OnSave()
+ protected override void OnSave(PyObjectSerializeContext context)
{
- base.OnSave();
+ base.OnSave(context);
Runtime.XIncref(pyHandle);
}
- protected override void OnLoad()
+ protected override void OnLoad(PyObjectSerializeContext context)
{
- base.OnLoad();
+ base.OnLoad(context);
GCHandle gc = AllocGCHandle(TrackTypes.Wrapper);
Marshal.WriteIntPtr(pyHandle, ObjectOffset.magic(tpHandle), (IntPtr)gc);
- Runtime.XDecref(pyHandle);
}
}
}
diff --git a/src/runtime/extensiontype.cs b/src/runtime/extensiontype.cs
index 75ca04205..79d78268b 100644
--- a/src/runtime/extensiontype.cs
+++ b/src/runtime/extensiontype.cs
@@ -98,9 +98,9 @@ public static void tp_dealloc(IntPtr ob)
self.Dealloc();
}
- protected override void OnLoad()
+ protected override void OnLoad(PyObjectSerializeContext context)
{
- base.OnLoad();
+ base.OnLoad(context);
GCHandle gc = AllocGCHandle(TrackTypes.Extension);
Marshal.WriteIntPtr(pyHandle, ObjectOffset.magic(tpHandle), (IntPtr)gc);
Runtime.PyObject_GC_UnTrack(pyHandle);
diff --git a/src/runtime/managedtype.cs b/src/runtime/managedtype.cs
index 99e897570..dae7ebee7 100644
--- a/src/runtime/managedtype.cs
+++ b/src/runtime/managedtype.cs
@@ -200,18 +200,18 @@ protected void TypeClear()
ClearObjectDict(pyHandle);
}
- internal void Save()
+ internal void Save(PyObjectSerializeContext context)
{
- OnSave();
+ OnSave(context);
}
- internal void Load()
+ internal void Load(PyObjectSerializeContext context)
{
- OnLoad();
+ OnLoad(context);
}
- protected virtual void OnSave() { }
- protected virtual void OnLoad() { }
+ protected virtual void OnSave(PyObjectSerializeContext context) { }
+ protected virtual void OnLoad(PyObjectSerializeContext context) { }
protected static void ClearObjectDict(IntPtr ob)
{
diff --git a/src/runtime/methodbinding.cs b/src/runtime/methodbinding.cs
index c14e592d5..1a87c4d31 100644
--- a/src/runtime/methodbinding.cs
+++ b/src/runtime/methodbinding.cs
@@ -254,5 +254,12 @@ public static int tp_clear(IntPtr ob)
self.ClearMembers();
return 0;
}
+
+ protected override void OnSave(PyObjectSerializeContext context)
+ {
+ base.OnSave(context);
+ Runtime.XIncref(target);
+ Runtime.XIncref(targetType);
+ }
}
}
diff --git a/src/runtime/methodobject.cs b/src/runtime/methodobject.cs
index 14fb0cd19..1e44f3270 100644
--- a/src/runtime/methodobject.cs
+++ b/src/runtime/methodobject.cs
@@ -18,7 +18,7 @@ internal class MethodObject : ExtensionType
internal MethodBinding unbound;
internal MethodBinder binder;
internal bool is_static = false;
- [NonSerialized]
+
internal IntPtr doc;
internal Type type;
@@ -221,5 +221,15 @@ public static int tp_clear(IntPtr ob)
ClearObjectDict(ob);
return 0;
}
+
+ protected override void OnSave(PyObjectSerializeContext context)
+ {
+ base.OnSave(context);
+ if (unbound != null)
+ {
+ Runtime.XIncref(unbound.pyHandle);
+ }
+ Runtime.XIncref(doc);
+ }
}
}
diff --git a/src/runtime/moduleobject.cs b/src/runtime/moduleobject.cs
index 4a1a15ca8..1001cde13 100644
--- a/src/runtime/moduleobject.cs
+++ b/src/runtime/moduleobject.cs
@@ -14,13 +14,8 @@ namespace Python.Runtime
[Serializable]
internal class ModuleObject : ExtensionType
{
- [NonSerialized]
private Dictionary cache;
- [NonSerialized]
- // FIXME: Used by reload mode, remove it after implement a delay load handler.
- private bool _cacheInited;
-
internal string moduleName;
internal IntPtr dict;
protected string _namespace;
@@ -33,7 +28,6 @@ public ModuleObject(string name)
}
moduleName = name;
cache = new Dictionary();
- _cacheInited = true;
_namespace = name;
// Use the filename from any of the assemblies just so there's something for
@@ -63,7 +57,7 @@ public ModuleObject(string name)
Runtime.XDecref(pydocstring);
Runtime.XIncref(dict);
- Marshal.WriteIntPtr(pyHandle, ObjectOffset.DictOffset(pyHandle), dict);
+ SetObjectDict(pyHandle, dict);
InitializeModuleMembers();
}
@@ -77,11 +71,6 @@ public ModuleObject(string name)
///
public ManagedType GetAttribute(string name, bool guess)
{
- if (!_cacheInited)
- {
- // XXX: Used by reload mode.
- SetupCacheByDict();
- }
ManagedType cached = null;
cache.TryGetValue(name, out cached);
if (cached != null)
@@ -360,46 +349,24 @@ public static int tp_clear(IntPtr ob)
return 0;
}
- protected override void OnSave()
+ protected override void OnSave(PyObjectSerializeContext context)
{
- base.OnSave();
+ base.OnSave(context);
System.Diagnostics.Debug.Assert(dict == GetObjectDict(pyHandle));
+ foreach (var attr in cache.Values)
+ {
+ Runtime.XIncref(attr.pyHandle);
+ }
// Decref twice in tp_clear, equilibrate them.
Runtime.XIncref(dict);
Runtime.XIncref(dict);
}
- protected override void OnLoad()
+ protected override void OnLoad(PyObjectSerializeContext context)
{
- base.OnLoad();
- // XXX: Set the cache after all objects loaded.
- cache = new Dictionary();
- _cacheInited = false;
+ base.OnLoad(context);
SetObjectDict(pyHandle, dict);
}
-
- private void SetupCacheByDict()
- {
- System.Diagnostics.Debug.Assert(!_cacheInited);
- _cacheInited = true;
- IntPtr key, value;
- IntPtr pos;
- while (Runtime.PyDict_Next(dict, out pos, out key, out value) != 0)
- {
- ManagedType obj = GetManagedObject(value);
- if (obj == null)
- {
- continue;
- }
- string name = Runtime.GetManagedString(key);
- if (cache.ContainsKey(name))
- {
- continue;
- }
- Runtime.XIncref(value);
- cache.Add(name, obj);
- }
- }
}
///
diff --git a/src/runtime/runtime_data.cs b/src/runtime/runtime_data.cs
index 83bdb23ba..19eb80a18 100644
--- a/src/runtime/runtime_data.cs
+++ b/src/runtime/runtime_data.cs
@@ -82,7 +82,6 @@ internal static void Stash()
XDecref(capsule);
}
-
internal static void StashPop()
{
try
@@ -112,14 +111,19 @@ private static void StashPopImpl()
var objs = StashPopObjects(storage.GetStorage("objs"));
StashPopModules(storage.GetStorage("modules"));
- ClassManager.StashPop(storage.GetStorage("classes"));
+ var clsObjs = ClassManager.StashPop(storage.GetStorage("classes"));
TypeManager.StashPop(storage.GetStorage("types"));
ImportHook.StashPop(storage.GetStorage("import"));
PyCLRMetaType = MetaType.StashPop(storage.GetStorage("meta"));
foreach (var item in objs)
{
- XDecref(item.pyHandle);
+ item.Value.ExecutePostActions();
+ XDecref(item.Key.pyHandle);
+ }
+ foreach (var item in clsObjs)
+ {
+ item.Value.ExecutePostActions();
}
}
@@ -139,6 +143,7 @@ private static void StashPushObjects(RuntimeDataStorage storage)
var extensionObjs = new List();
var wrappers = new Dictionary>();
var serializeObjs = new CLRWrapperCollection();
+ var contexts = new Dictionary();
foreach (var entry in objs)
{
var obj = entry.Key;
@@ -147,7 +152,9 @@ private static void StashPushObjects(RuntimeDataStorage storage)
{
case ManagedType.TrackTypes.Extension:
Debug.Assert(obj.GetType().IsSerializable);
- obj.Save();
+ var context = new PyObjectSerializeContext();
+ contexts[obj.pyHandle] = context;
+ obj.Save(context);
extensionObjs.Add(obj);
break;
case ManagedType.TrackTypes.Wrapper:
@@ -197,23 +204,28 @@ private static void StashPushObjects(RuntimeDataStorage storage)
foreach (var clrObj in wrappers[item.Instance])
{
XIncref(clrObj.pyHandle);
- clrObj.Save();
+ var context = new PyObjectSerializeContext();
+ contexts[clrObj.pyHandle] = context;
+ clrObj.Save(context);
}
}
storage.AddValue("internalStores", internalStores);
storage.AddValue("extensions", extensionObjs);
storage.AddValue("wrappers", wrapperStorage);
+ storage.AddValue("contexts", contexts);
}
- private static IEnumerable StashPopObjects(RuntimeDataStorage storage)
+ private static Dictionary StashPopObjects(RuntimeDataStorage storage)
{
var extensions = storage.GetValue>("extensions");
var internalStores = storage.GetValue>("internalStores");
- var storedObjs = new List();
+ var contexts = storage.GetValue >("contexts");
+ var storedObjs = new Dictionary();
foreach (var obj in Enumerable.Union(extensions, internalStores))
{
- obj.Load();
- storedObjs.Add(obj);
+ var context = contexts[obj.pyHandle];
+ obj.Load(context);
+ storedObjs.Add(obj, context);
}
if (WrappersStorer != null)
{
@@ -224,8 +236,9 @@ private static IEnumerable StashPopObjects(RuntimeDataStorage stora
object obj = item.Instance;
foreach (var handle in item.Handles)
{
- var co = CLRObject.Restore(obj, handle);
- storedObjs.Add(co);
+ var context = contexts[handle];
+ var co = CLRObject.Restore(obj, handle, context);
+ storedObjs.Add(co, context);
}
}
}
@@ -342,6 +355,37 @@ public T PopValue(out T value)
}
+ [Serializable]
+ class PyObjectSerializeContext
+ {
+ private RuntimeDataStorage _storage;
+ public RuntimeDataStorage Storage => _storage ?? (_storage = new RuntimeDataStorage());
+
+ ///
+ /// Actions after loaded.
+ ///
+ [NonSerialized]
+ private List _postActions;
+ public List PostActions => _postActions ?? (_postActions = new List());
+
+ public void AddPostAction(Action action)
+ {
+ PostActions.Add(action);
+ }
+
+ public void ExecutePostActions()
+ {
+ if (_postActions == null)
+ {
+ return;
+ }
+ foreach (var action in _postActions)
+ {
+ action();
+ }
+ }
+ }
+
public class CLRMappedItem
{
public object Instance { get; private set; }
From bcfdcc7001cc87817b92005e6ac373214bc42d04 Mon Sep 17 00:00:00 2001
From: amos402
Date: Tue, 10 Mar 2020 12:05:13 +0800
Subject: [PATCH 121/998] Test for class object on crossed domain
---
src/embed_tests/TestDomainReload.cs | 328 ++++++++++++++++++----------
1 file changed, 216 insertions(+), 112 deletions(-)
diff --git a/src/embed_tests/TestDomainReload.cs b/src/embed_tests/TestDomainReload.cs
index 9e5f67d82..5f3c189d2 100644
--- a/src/embed_tests/TestDomainReload.cs
+++ b/src/embed_tests/TestDomainReload.cs
@@ -5,6 +5,7 @@
using NUnit.Framework;
using Python.Runtime;
+using PyRuntime = Python.Runtime.Runtime;
//
// This test case is disabled on .NET Standard because it doesn't have all the
// APIs we use. We could work around that, but .NET Core doesn't implement
@@ -17,6 +18,12 @@ namespace Python.EmbeddingTest
{
class TestDomainReload
{
+ abstract class CrossCaller : MarshalByRefObject
+ {
+ public abstract ValueType Execte(ValueType arg);
+ }
+
+
///
/// Test that the python runtime can survive a C# domain reload without crashing.
///
@@ -53,71 +60,198 @@ public static void DomainReloadAndGC()
{
Assert.IsFalse(PythonEngine.IsInitialized);
RunAssemblyAndUnload("test1");
- Assert.That(Runtime.Runtime.Py_IsInitialized() != 0,
+ Assert.That(PyRuntime.Py_IsInitialized() != 0,
"On soft-shutdown mode, Python runtime should still running");
RunAssemblyAndUnload("test2");
- Assert.That(Runtime.Runtime.Py_IsInitialized() != 0,
+ 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.
- Runtime.Runtime.PyGILState_Ensure();
- Runtime.Runtime.Py_Finalize();
+ PyRuntime.PyGILState_Ensure();
+ PyRuntime.Py_Finalize();
}
}
- [Test]
- public static void CrossDomainObject()
+ #region CrossDomainObject
+
+ class CrossDomianObjectStep1 : CrossCaller
{
- IntPtr handle = IntPtr.Zero;
- Type type = typeof(Proxy);
+ public override ValueType Execte(ValueType arg)
{
- AppDomain domain = CreateDomain("test_domain_reload");
try
{
- var theProxy = (Proxy)domain.CreateInstanceAndUnwrap(
- type.Assembly.FullName,
- type.FullName);
- theProxy.Call("InitPython", ShutdownMode.Reload);
- handle = (IntPtr)theProxy.Call("GetTestObject");
- theProxy.Call("ShutdownPython");
+ Type type = typeof(Python.EmbeddingTest.Domain.MyClass);
+ string code = string.Format(@"
+import clr
+clr.AddReference('{0}')
+
+from Python.EmbeddingTest.Domain import MyClass
+obj = MyClass()
+obj.Method()
+obj.StaticMethod()
+obj.Property = 1
+obj.Field = 10
+", Assembly.GetExecutingAssembly().FullName);
+
+ using (Py.GIL())
+ using (var scope = Py.CreateScope())
+ {
+ scope.Exec(code);
+ using (PyObject obj = scope.Get("obj"))
+ {
+ Debug.Assert(obj.AsManagedObject(type).GetType() == type);
+ // We only needs its Python handle
+ PyRuntime.XIncref(obj.Handle);
+ return obj.Handle;
+ }
+ }
}
- finally
+ catch (Exception e)
{
- AppDomain.Unload(domain);
+ Debug.WriteLine(e);
+ throw;
}
}
+ }
+
+ class CrossDomianObjectStep2 : CrossCaller
+ {
+ public override ValueType Execte(ValueType arg)
{
- AppDomain domain = CreateDomain("test_domain_reload");
+ // handle refering a clr object created in previous domain,
+ // it should had been deserialized and became callable agian.
+ IntPtr handle = (IntPtr)arg;
try
{
- var theProxy = (Proxy)domain.CreateInstanceAndUnwrap(
- type.Assembly.FullName,
- type.FullName);
- theProxy.Call("InitPython", ShutdownMode.Reload);
+ using (Py.GIL())
+ {
+ IntPtr tp = Runtime.Runtime.PyObject_TYPE(handle);
+ IntPtr tp_clear = Marshal.ReadIntPtr(tp, TypeOffset.tp_clear);
- // handle refering a clr object created in previous domain,
- // it should had been deserialized and became callable agian.
- theProxy.Call("RunTestObject", handle);
- theProxy.Call("ShutdownPythonCompletely");
+ using (PyObject obj = new PyObject(handle))
+ {
+ obj.InvokeMethod("Method");
+ obj.InvokeMethod("StaticMethod");
+
+ using (var scope = Py.CreateScope())
+ {
+ scope.Set("obj", obj);
+ scope.Exec(@"
+obj.Method()
+obj.StaticMethod()
+obj.Property += 1
+obj.Field += 10
+");
+ }
+ var clrObj = obj.As();
+ Assert.AreEqual(clrObj.Property, 2);
+ Assert.AreEqual(clrObj.Field, 20);
+ }
+ }
}
- finally
+ catch (Exception e)
{
- AppDomain.Unload(domain);
+ Debug.WriteLine(e);
+ throw;
}
+ return 0;
}
- if (PythonEngine.DefaultShutdownMode == ShutdownMode.Normal)
+ }
+
+ [Test]
+ public static void CrossDomainObject()
+ {
+ RunDomainReloadSteps();
+ }
+
+ #endregion
+
+ #region TestClassReference
+
+ class ReloadClassRefStep1 : CrossCaller
+ {
+ public override ValueType Execte(ValueType arg)
+ {
+ const string code = @"
+from Python.EmbeddingTest.Domain import MyClass
+
+def test_obj_call():
+ obj = MyClass()
+ obj.Method()
+ obj.StaticMethod()
+ obj.Property = 1
+ obj.Field = 10
+
+test_obj_call()
+";
+ const string name = "test_domain_reload_mod";
+ using (Py.GIL())
+ {
+ IntPtr module = PyRuntime.PyModule_New(name);
+ Assert.That(module != IntPtr.Zero);
+ IntPtr globals = PyRuntime.PyObject_GetAttrString(module, "__dict__");
+ Assert.That(globals != IntPtr.Zero);
+ try
+ {
+ int res = PyRuntime.PyDict_SetItemString(globals, "__builtins__",
+ PyRuntime.PyEval_GetBuiltins());
+ PythonException.ThrowIfIsNotZero(res);
+
+ PythonEngine.Exec(code, globals);
+ IntPtr modules = PyRuntime.PyImport_GetModuleDict();
+ res = PyRuntime.PyDict_SetItemString(modules, name, modules);
+ PythonException.ThrowIfIsNotZero(res);
+ }
+ catch
+ {
+ PyRuntime.XDecref(module);
+ throw;
+ }
+ finally
+ {
+ PyRuntime.XDecref(globals);
+ }
+ return module;
+ }
+ }
+ }
+
+ class ReloadClassRefStep2 : CrossCaller
+ {
+ public override ValueType Execte(ValueType arg)
{
- Assert.IsTrue(Runtime.Runtime.Py_IsInitialized() == 0);
+ var module = (IntPtr)arg;
+ using (Py.GIL())
+ {
+ var test_obj_call = PyRuntime.PyObject_GetAttrString(module, "test_obj_call");
+ PythonException.ThrowIfIsNull(test_obj_call);
+ var args = PyRuntime.PyTuple_New(0);
+ var res = PyRuntime.PyObject_CallObject(test_obj_call, args);
+ PythonException.ThrowIfIsNull(res);
+
+ PyRuntime.XDecref(args);
+ PyRuntime.XDecref(res);
+ }
+ return 0;
}
}
+ [Test]
+ public void TestClassReference()
+ {
+ RunDomainReloadSteps();
+ }
+
+ #endregion
+
#region Tempary tests
+
// https://github.com/pythonnet/pythonnet/pull/1074#issuecomment-596139665
[Test]
public void CrossReleaseBuiltinType()
@@ -274,6 +408,58 @@ static Assembly ResolveAssembly(object sender, ResolveEventArgs args)
return null;
}
+
+ static void RunDomainReloadSteps() where T1 : CrossCaller where T2 : CrossCaller
+ {
+ ValueType arg = null;
+ Type type = typeof(Proxy);
+ {
+ AppDomain domain = CreateDomain("test_domain_reload");
+ try
+ {
+ var theProxy = (Proxy)domain.CreateInstanceAndUnwrap(
+ type.Assembly.FullName,
+ type.FullName);
+ theProxy.Call("InitPython", ShutdownMode.Reload);
+
+ var caller = (T1)domain.CreateInstanceAndUnwrap(
+ typeof(T1).Assembly.FullName,
+ typeof(T1).FullName);
+ arg = caller.Execte(arg);
+
+ theProxy.Call("ShutdownPython");
+ }
+ finally
+ {
+ AppDomain.Unload(domain);
+ }
+ }
+
+ {
+ AppDomain domain = CreateDomain("test_domain_reload");
+ try
+ {
+ var theProxy = (Proxy)domain.CreateInstanceAndUnwrap(
+ type.Assembly.FullName,
+ type.FullName);
+ theProxy.Call("InitPython", ShutdownMode.Reload);
+
+ var caller = (T2)domain.CreateInstanceAndUnwrap(
+ typeof(T2).Assembly.FullName,
+ typeof(T2).FullName);
+ caller.Execte(arg);
+ theProxy.Call("ShutdownPythonCompletely");
+ }
+ finally
+ {
+ AppDomain.Unload(domain);
+ }
+ }
+ if (PythonEngine.DefaultShutdownMode == ShutdownMode.Normal)
+ {
+ Assert.IsTrue(PyRuntime.Py_IsInitialized() == 0);
+ }
+ }
}
@@ -358,88 +544,6 @@ public static void ShutdownPythonCompletely()
PythonEngine.Shutdown();
}
- public static IntPtr GetTestObject()
- {
- try
- {
- Type type = typeof(Python.EmbeddingTest.Domain.MyClass);
- string code = string.Format(@"
-import clr
-clr.AddReference('{0}')
-
-from Python.EmbeddingTest.Domain import MyClass
-obj = MyClass()
-obj.Method()
-obj.StaticMethod()
-obj.Property = 1
-obj.Field = 10
-", Assembly.GetExecutingAssembly().FullName);
-
- using (Py.GIL())
- using (var scope = Py.CreateScope())
- {
- scope.Exec(code);
- using (PyObject obj = scope.Get("obj"))
- {
- Debug.Assert(obj.AsManagedObject(type).GetType() == type);
- // We only needs its Python handle
- Runtime.Runtime.XIncref(obj.Handle);
- return obj.Handle;
- }
- }
- }
- catch (Exception e)
- {
- Debug.WriteLine(e);
- throw;
- }
- }
-
- public static void RunTestObject(IntPtr handle)
- {
- try
- {
- using (Py.GIL())
- {
- IntPtr tp = Runtime.Runtime.PyObject_TYPE(handle);
- IntPtr tp_clear = Marshal.ReadIntPtr(tp, TypeOffset.tp_clear);
-
- using (PyObject obj = new PyObject(handle))
- {
- obj.InvokeMethod("Method");
- obj.InvokeMethod("StaticMethod");
-
- using (var scope = Py.CreateScope())
- {
- scope.Set("obj", obj);
- scope.Exec(@"
-obj.Method()
-obj.StaticMethod()
-obj.Property += 1
-obj.Field += 10
-");
- }
- var clrObj = obj.As();
- Assert.AreEqual(clrObj.Property, 2);
- Assert.AreEqual(clrObj.Field, 20);
- }
- }
- }
- catch (Exception e)
- {
- Debug.WriteLine(e);
- throw;
- }
- }
-
- public static void ReleaseTestObject(IntPtr handle)
- {
- using (Py.GIL())
- {
- Runtime.Runtime.XDecref(handle);
- }
- }
-
static void OnDomainUnload(object sender, EventArgs e)
{
Console.WriteLine(string.Format("[{0} in .NET] unloading", AppDomain.CurrentDomain.FriendlyName));
From 66ab7192222e382faf6cff808a23964eb846fc34 Mon Sep 17 00:00:00 2001
From: amos402
Date: Tue, 10 Mar 2020 13:14:13 +0800
Subject: [PATCH 122/998] Multi times for running cross dispose
---
src/embed_tests/TestDomainReload.cs | 73 +++++++++++++++++++++++------
1 file changed, 59 insertions(+), 14 deletions(-)
diff --git a/src/embed_tests/TestDomainReload.cs b/src/embed_tests/TestDomainReload.cs
index 5f3c189d2..05b449237 100644
--- a/src/embed_tests/TestDomainReload.cs
+++ b/src/embed_tests/TestDomainReload.cs
@@ -1,4 +1,5 @@
using System;
+using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.InteropServices;
@@ -256,37 +257,81 @@ public void TestClassReference()
[Test]
public void CrossReleaseBuiltinType()
{
+ void ExecTest()
+ {
+ try
+ {
+ var numRef = CreateNumReference();
+ GC.Collect();
+ GC.WaitForPendingFinalizers(); // <- this will put former `num` into Finalizer queue
+ Finalizer.Instance.Collect(forceDispose: true);
+ // ^- this will call PyObject.Dispose, which will call XDecref on `num.Handle`,
+ // but Python interpreter from "run" 1 is long gone, so it will corrupt memory instead.
+ Assert.False(numRef.IsAlive);
+ }
+ finally
+ {
+ PythonEngine.Shutdown();
+ }
+ }
+
+ var errorArgs = new List();
+ void ErrorHandler(object sender, Finalizer.ErrorArgs e)
+ {
+ errorArgs.Add(e);
+ }
+ Finalizer.Instance.ErrorHandler += ErrorHandler;
try
{
- var numRef = CreateNumReference();
- GC.Collect();
- GC.WaitForPendingFinalizers(); // <- this will put former `num` into Finalizer queue
- Finalizer.Instance.Collect(forceDispose: true);
- // ^- this will call PyObject.Dispose, which will call XDecref on `num.Handle`,
- // but Python interpreter from "run" 1 is long gone, so it will corrupt memory instead.
- Assert.False(numRef.IsAlive);
+ for (int i = 0; i < 10; i++)
+ {
+ ExecTest();
+ }
}
finally
{
- PythonEngine.Shutdown();
+ Finalizer.Instance.ErrorHandler -= ErrorHandler;
}
+ Assert.AreEqual(errorArgs.Count, 0);
}
[Test]
public void CrossReleaseCustomType()
{
+ void ExecTest()
+ {
+ try
+ {
+ var objRef = CreateConcreateObject();
+ GC.Collect();
+ GC.WaitForPendingFinalizers();
+ Finalizer.Instance.Collect(forceDispose: true);
+ Assert.False(objRef.IsAlive);
+ }
+ finally
+ {
+ PythonEngine.Shutdown();
+ }
+ }
+
+ var errorArgs = new List();
+ void ErrorHandler(object sender, Finalizer.ErrorArgs e)
+ {
+ errorArgs.Add(e);
+ }
+ Finalizer.Instance.ErrorHandler += ErrorHandler;
try
{
- var objRef = CreateConcreateObject();
- GC.Collect();
- GC.WaitForPendingFinalizers();
- Finalizer.Instance.Collect(forceDispose: true);
- Assert.False(objRef.IsAlive);
+ for (int i = 0; i < 10; i++)
+ {
+ ExecTest();
+ }
}
finally
{
- PythonEngine.Shutdown();
+ Finalizer.Instance.ErrorHandler -= ErrorHandler;
}
+ Assert.AreEqual(errorArgs.Count, 0);
}
private static WeakReference CreateNumReference()
From 8e3c0284d77805194abb3908fe9046e75baf6e9b Mon Sep 17 00:00:00 2001
From: amos402
Date: Tue, 10 Mar 2020 15:44:47 +0800
Subject: [PATCH 123/998] Apply Reference type usage
---
src/runtime/typemanager.cs | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/src/runtime/typemanager.cs b/src/runtime/typemanager.cs
index b552e2cae..43181735b 100644
--- a/src/runtime/typemanager.cs
+++ b/src/runtime/typemanager.cs
@@ -891,7 +891,8 @@ public static IntPtr CreateObjectType()
throw new PythonException();
}
const string code = "class A(object): pass";
- IntPtr res = Runtime.PyRun_String(code, RunFlagType.File, globals, globals);
+ var resRef = Runtime.PyRun_String(code, RunFlagType.File, globals, globals);
+ IntPtr res = resRef.DangerousGetAddress();
if (res == IntPtr.Zero)
{
try
@@ -903,7 +904,7 @@ public static IntPtr CreateObjectType()
Runtime.XDecref(globals);
}
}
- Runtime.XDecref(res);
+ resRef.Dispose();
IntPtr A = Runtime.PyDict_GetItemString(globals, "A");
Debug.Assert(A != IntPtr.Zero);
Runtime.XIncref(A);
From a320d49b0b102190d98b6bab706041b3c5422d02 Mon Sep 17 00:00:00 2001
From: Victor
Date: Tue, 10 Mar 2020 01:35:15 -0700
Subject: [PATCH 124/998] fixed tuple codec not clearing Python exception after
unsuccessful element decoding attempt (#1083)
---
src/runtime/Codecs/TupleCodecs.cs | 2 ++
1 file changed, 2 insertions(+)
diff --git a/src/runtime/Codecs/TupleCodecs.cs b/src/runtime/Codecs/TupleCodecs.cs
index 4c81cac0b..a9ae33fe0 100644
--- a/src/runtime/Codecs/TupleCodecs.cs
+++ b/src/runtime/Codecs/TupleCodecs.cs
@@ -81,6 +81,7 @@ public bool TryDecode(PyObject pyObj, out T value)
IntPtr pyItem = Runtime.PyTuple_GetItem(pyObj.Handle, itemIndex);
if (!Converter.ToManaged(pyItem, itemTypes[itemIndex], out elements[itemIndex], setError: false))
{
+ Exceptions.Clear();
return false;
}
}
@@ -105,6 +106,7 @@ static bool Decode(PyObject tuple, out object value)
var pyItem = Runtime.PyTuple_GetItem(tuple.Handle, itemIndex);
if (!Converter.ToManaged(pyItem, typeof(object), out elements[itemIndex], setError: false))
{
+ Exceptions.Clear();
return false;
}
From d145ab1c4835664928871474e62eb9a06dca7e6c Mon Sep 17 00:00:00 2001
From: Victor
Date: Tue, 10 Mar 2020 01:37:32 -0700
Subject: [PATCH 125/998] Python runtime must be initialized before trying to
acquire GIL (#1086)
---
src/embed_tests/TestGILState.cs | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/src/embed_tests/TestGILState.cs b/src/embed_tests/TestGILState.cs
index ba2ab500f..bf6f02dc6 100644
--- a/src/embed_tests/TestGILState.cs
+++ b/src/embed_tests/TestGILState.cs
@@ -17,5 +17,17 @@ public void CanDisposeMultipleTimes()
gilState.Dispose();
}
}
+
+ [OneTimeSetUp]
+ public void SetUp()
+ {
+ PythonEngine.Initialize();
+ }
+
+ [OneTimeTearDown]
+ public void Dispose()
+ {
+ PythonEngine.Shutdown();
+ }
}
}
From 638dc1c7772f7b08a593c89b58635c7bb970661f Mon Sep 17 00:00:00 2001
From: Victor Milovanov
Date: Tue, 10 Mar 2020 23:30:47 -0700
Subject: [PATCH 126/998] allow borrowing from NewReference
implemented as an implicit conversion
---
src/embed_tests/References.cs | 15 +++++++++++++++
src/runtime/NewReference.cs | 5 +++++
2 files changed, 20 insertions(+)
diff --git a/src/embed_tests/References.cs b/src/embed_tests/References.cs
index 4c7124907..1d29e85c7 100644
--- a/src/embed_tests/References.cs
+++ b/src/embed_tests/References.cs
@@ -36,5 +36,20 @@ public void MoveToPyObject_SetsNull()
reference.Dispose();
}
}
+
+ [Test]
+ public void CanBorrowFromNewReference()
+ {
+ var dict = new PyDict();
+ NewReference reference = Runtime.PyDict_Items(dict.Handle);
+ try
+ {
+ PythonException.ThrowIfIsNotZero(Runtime.PyList_Reverse(reference));
+ }
+ finally
+ {
+ reference.Dispose();
+ }
+ }
}
}
diff --git a/src/runtime/NewReference.cs b/src/runtime/NewReference.cs
index 3ab4b6530..6e66232d0 100644
--- a/src/runtime/NewReference.cs
+++ b/src/runtime/NewReference.cs
@@ -11,6 +11,10 @@ ref struct NewReference
{
IntPtr pointer;
+ [Pure]
+ public static implicit operator BorrowedReference(in NewReference reference)
+ => new BorrowedReference(reference.pointer);
+
///
/// Returns wrapper around this reference, which now owns
/// the pointer. Sets the original reference to null, as it no longer owns it.
@@ -36,6 +40,7 @@ public void Dispose()
///
/// Creates from a raw pointer
///
+ [Pure]
public static NewReference DangerousFromPointer(IntPtr pointer)
=> new NewReference {pointer = pointer};
From 5d28a8d9244e58e61a5c295b1b5a326ecdb88479 Mon Sep 17 00:00:00 2001
From: Alex Earl
Date: Wed, 8 Apr 2020 16:50:33 -0700
Subject: [PATCH 127/998] Add Format method to pythonexception (#1031)
Allows formatting a PythonException using traceback.format_exception
---
CHANGELOG.md | 1 +
src/embed_tests/TestPythonException.cs | 36 ++++++++++++++++++++++
src/runtime/pythonexception.cs | 42 ++++++++++++++++++++++++++
3 files changed, 79 insertions(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index db126bd1c..625f12de1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -14,6 +14,7 @@ This document follows the conventions laid out in [Keep a CHANGELOG][].
- Added support for Jetson Nano.
- Added support for __len__ for .NET classes that implement ICollection
- Added `object.GetRawPythonProxy() -> PyObject` extension method, that bypasses any conversions
+- Added PythonException.Format method to format exceptions the same as traceback.format_exception
### Changed
diff --git a/src/embed_tests/TestPythonException.cs b/src/embed_tests/TestPythonException.cs
index 57a8d54af..000c32ca3 100644
--- a/src/embed_tests/TestPythonException.cs
+++ b/src/embed_tests/TestPythonException.cs
@@ -54,5 +54,41 @@ public void TestPythonErrorTypeName()
Assert.That(ex.PythonTypeName, Is.EqualTo("ModuleNotFoundError").Or.EqualTo("ImportError"));
}
}
+
+ [Test]
+ public void TestPythonExceptionFormat()
+ {
+ try
+ {
+ PythonEngine.Exec("raise ValueError('Error!')");
+ Assert.Fail("Exception should have been raised");
+ }
+ catch (PythonException ex)
+ {
+ Assert.That(ex.Format(), Does.Contain("Traceback").And.Contains("(most recent call last):").And.Contains("ValueError: Error!"));
+ }
+ }
+
+ [Test]
+ public void TestPythonExceptionFormatNoError()
+ {
+ var ex = new PythonException();
+ Assert.AreEqual(ex.StackTrace, ex.Format());
+ }
+
+ [Test]
+ public void TestPythonExceptionFormatNoTraceback()
+ {
+ try
+ {
+ var module = PythonEngine.ImportModule("really____unknown___module");
+ Assert.Fail("Unknown module should not be loaded");
+ }
+ catch (PythonException ex)
+ {
+ // ImportError/ModuleNotFoundError do not have a traceback when not running in a script
+ Assert.AreEqual(ex.StackTrace, ex.Format());
+ }
+ }
}
}
diff --git a/src/runtime/pythonexception.cs b/src/runtime/pythonexception.cs
index 7ac922abc..8efdccc91 100644
--- a/src/runtime/pythonexception.cs
+++ b/src/runtime/pythonexception.cs
@@ -1,5 +1,6 @@
using System;
using System.Runtime.CompilerServices;
+using System.Text;
namespace Python.Runtime
{
@@ -145,6 +146,47 @@ public string PythonTypeName
get { return _pythonTypeName; }
}
+ ///
+ /// Formats this PythonException object into a message as would be printed
+ /// out via the Python console. See traceback.format_exception
+ ///
+ public string Format()
+ {
+ string res;
+ IntPtr gs = PythonEngine.AcquireLock();
+ try
+ {
+ if (_pyTB != IntPtr.Zero && _pyType != IntPtr.Zero && _pyValue != IntPtr.Zero)
+ {
+ Runtime.XIncref(_pyType);
+ Runtime.XIncref(_pyValue);
+ Runtime.XIncref(_pyTB);
+ using (PyObject pyType = new PyObject(_pyType))
+ using (PyObject pyValue = new PyObject(_pyValue))
+ using (PyObject pyTB = new PyObject(_pyTB))
+ using (PyObject tb_mod = PythonEngine.ImportModule("traceback"))
+ {
+ var buffer = new StringBuilder();
+ var values = tb_mod.InvokeMethod("format_exception", pyType, pyValue, pyTB);
+ foreach (PyObject val in values)
+ {
+ buffer.Append(val.ToString());
+ }
+ res = buffer.ToString();
+ }
+ }
+ else
+ {
+ res = StackTrace;
+ }
+ }
+ finally
+ {
+ PythonEngine.ReleaseLock(gs);
+ }
+ return res;
+ }
+
///
/// Dispose Method
///
From 2a83fe5981aa81a7ca32b34ad17b21d7aaab7a8c Mon Sep 17 00:00:00 2001
From: Alex Earl
Date: Wed, 8 Apr 2020 17:58:17 -0700
Subject: [PATCH 128/998] Update vswhere usage (#1105)
Updates to newer version of vswhere.exe
Follows the recommended way of finding MSBuild.exe
from https://github.com/microsoft/vswhere#example
Also, updates to allow finding newer versions of VS.
---
CHANGELOG.md | 1 +
setup.py | 36 ++++++++++++------------------------
tools/vswhere/vswhere.exe | Bin 402040 -> 458872 bytes
3 files changed, 13 insertions(+), 24 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 625f12de1..3cc571ad4 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -25,6 +25,7 @@ This document follows the conventions laid out in [Keep a CHANGELOG][].
- Changed usage of obselete function GetDelegateForFunctionPointer(IntPtr, Type) to GetDelegateForFunctionPointer(IntPtr)
- When calling C# from Python, enable passing argument of any type to a parameter of C# type `object` by wrapping it into `PyObject` instance. ([#881][i881])
- Added support for kwarg parameters when calling .NET methods from Python
+- Changed method for finding MSBuild using vswhere
### Fixed
diff --git a/setup.py b/setup.py
index db2b4ae68..cabb176af 100644
--- a/setup.py
+++ b/setup.py
@@ -457,26 +457,20 @@ def _find_msbuild_tool(self, tool="msbuild.exe", use_windows_sdk=False):
# trying to search path with help of vswhere when MSBuild 15.0 and higher installed.
if tool == "msbuild.exe" and use_windows_sdk == False:
try:
- basePathes = subprocess.check_output(
+ basePaths = subprocess.check_output(
[
"tools\\vswhere\\vswhere.exe",
"-latest",
"-version",
- "[15.0, 16.0)",
+ "[15.0,)",
"-requires",
"Microsoft.Component.MSBuild",
- "-property",
- "InstallationPath",
+ "-find",
+ "MSBuild\**\Bin\MSBuild.exe",
]
).splitlines()
- if len(basePathes):
- return os.path.join(
- basePathes[0].decode(sys.stdout.encoding or "utf-8"),
- "MSBuild",
- "15.0",
- "Bin",
- "MSBuild.exe",
- )
+ if len(basePaths):
+ return basePaths[0].decode(sys.stdout.encoding or "utf-8")
except:
pass # keep trying to search by old method.
@@ -528,26 +522,20 @@ def _find_msbuild_tool(self, tool="msbuild.exe", use_windows_sdk=False):
def _find_msbuild_tool_15(self):
"""Return full path to one of the Microsoft build tools"""
try:
- basePathes = subprocess.check_output(
+ basePaths = subprocess.check_output(
[
"tools\\vswhere\\vswhere.exe",
"-latest",
"-version",
- "[15.0, 16.0)",
+ "[15.0,)",
"-requires",
"Microsoft.Component.MSBuild",
- "-property",
- "InstallationPath",
+ "-find",
+ "MSBuild\**\Bin\MSBuild.exe",
]
).splitlines()
- if len(basePathes):
- return os.path.join(
- basePathes[0].decode(sys.stdout.encoding or "utf-8"),
- "MSBuild",
- "15.0",
- "Bin",
- "MSBuild.exe",
- )
+ if len(basePaths):
+ return basePaths[0].decode(sys.stdout.encoding or "utf-8")
else:
raise RuntimeError("MSBuild >=15.0 could not be found.")
except subprocess.CalledProcessError as e:
diff --git a/tools/vswhere/vswhere.exe b/tools/vswhere/vswhere.exe
index 3eb2df00987641952f6cb94bbe9127958256b9d8..582e82868dddc97bec4a8c8353f729348be55f56 100644
GIT binary patch
literal 458872
zcmeFadwi7DweUZa49Nf^Gf2>=Q9+}nhAJ8=(D4#36Yw%PA!cIK1Z@?k(N+pGfR{jU
z5}SwZW9@nEIe3aKJ*BM`+e51@QBe~@6K--*jz*ypD{Xh2RHLE>O_}$*_A?1V?YX?~
z@BQx=KFmDNzO23W+H0@9_F8N2319z^E8FFAdHA1ByIjqD%U_B4{LeqUZkKD+na_-J
zJvIEr@y+h5UmSmJ?3;_nEnIZxZHsRH*0^un{Oxbw86WrcTgNR*e0$tCzddf&HRa>J
zb>}U&o`1p#BMLL1cmLa%pR2aI4+sAD{bc#!`+4vC$+E)*=KHS0kMjN6`sIgzWWJXj
z-fF(@I{Y}__dR>`!&Q8%wtGL!G4Jnu*ueMckH<~<(;vStlmGUImPuc6Sjx})<~L%}
z=0Fc4A(!iF_b}I?8@^UOkk;kOa*uS63b^(`olod2fA2h)<#Gjh=oN~UIL{;vb2<3~
z->yKeA?jIMa-na@N>fnkNoDgr_$7(1Sy$$`9*~EJa$Kv&%R}aGbB=4-Wjwd!xE`MF
za&`TAm}}uc-jMI!zYKHb^B&>5WJtQ;xyeCoXzJke`SDwq#d-VSW%?s{NnfRFL;l9O
zTsNJ+=$4z~H&f&mfJxsM@ZEWQ!4e2^zEjM#xG;L;gzW&H0NKFZu>)
zrmytbbrvr*pDR~#{-Rs&xRZj?ccFo62H*9cD<`S{|KI-s1!}Kup787}mtNPH<*LoC
zDQ>Y6n~Kv5tk7ezNu42=9e&KJYt~Z>T`sGx-wwA>pl(mBi7YFzk76ylfV}olzuOLX
zN~Vr=namzCJ9YYOm#ZVxp9N-is5hPHwJsg^y;4`i4n1aFS}0G}r4#tl&rEW;lHvX|
z8L<~9mAGoNYx5&Ix;)Djt9c9jcCXBGRq2j~ELU4>@@pa2(=9P-tp$86Ci(v^`E9lN
z&C{Oa?G?%u?=RPR6>hq4;HQ&l!m94+2=)5Gq^bHqho`XN^^i+He45KuR%TWA>-%9L
zit2V!Vv7Xiji$+KrO67_lkAxkjomBhvq(>#GY60^z{q#ZO$TdTF%8HVkM$kHW|7>*
zYkI7!ca}@_HhvTzmOPr3IH66wz+f4)4*wJp}|NT~P|b
z_C)m3hqGKeV?UC1ch*Z6W3!le&1*K3(3{MRSx2bnu3wb6iqmv(Hyw=S|B<1yHyll;
zt>;?g^LJbLTwpaylCC{VMx!TU+gs&z!M)Dl+I_qgw{(OKt@(M0%TT!^q;~<&dd`~`
zAU-|;BIWcs2FM|R1W4860b;noF5y+69Jo-RTn!C6LS1_dl!$?Htpi2M>4W11l>~=?u*#rwR#=Z;IeEfap_b@
zYh8K?pSoh13({L(l_Ze!*w0D-EA@T%BPf$Cfk@QQxQEwd#~hx6eQ~cc#Z#{tZZrgo
zowx{w^wiFY=s(;8Lp~;>aoGxC$cyh2hMWk7PxlH#`oSvpE6G1Y^4n@*fLmaIpO%0^
zTdY-bJSHc$L!SF!@T#__r`A*V-rS6^OMbm&X?JogVwdV7WhnSvsN9W1q?-EgPdk+O9Y>FE)Xq5#>#f
zR=p(2Q<=B%BY)H5R8gTrjV2GL}k
z{zdGHJ(&_eV&$lkLC?JZ;Y)_$vK`<#${V4@^w`J1Ecu)?5i
zGu5_*B)Zx>8p1x~f!;0MOZb8aAZ>kv5-X2h>-N<98!CAoGTARXSth%EiHykRv9OGp
z?~U@_eUpLE1r3KeSdFnOx~%Fh<((IsBlJm+PJOA?tDj!NaQ9YqlS-*b0RlUWk-fC6v1Zqz5IW#>e)VQ7+R<(8pEEg&r%1~tr
zC{)#YtI`I-bJtP)gY{(&V+~BzHJ2O8dF@a$gXzrQi2m^mnQ?Y#ORzmYPHolSh2AR1
z3T;uP9xJp}m3pnvPU{S-#$(lZ?QpYRKmm}-RrW$!IflF-3JWme-*N^eb*ue@Ug%?o
zpU~GD5(%4)FeLIfT*%x{*0p%;iYM%Zrc?fz6;CK{bROfG*VwXbq-wXrTO#@vc$8|x
z9nhKa2Bb8nzlY>aK35>bu^UC!JtK>ZReiu8+9ONDhu53RBUSi-a;F0gE%B2+$6%KV
z>5@G@NRWK)Y5}EoM&hUHvuT)mhV{$@jp&UxxLm1xJ^b@IUm-^t+%-bJx9I5()*cn=
zf*={JLHU2gw!c9=%&_gxdF*~&_~m!2A*ovKOQtImpYO~RHkn9J{ethC?w^oc
zmgilRrCgP);(KBby#h<>H-vOs0^sD6q9GbU_{8prvp6>KQ6AOr|HwlT1|vQu*^)N^
zBoccJ0I3sHj@p%uAT>7dDpb-x0;DSSA-b%PZvIt%iL31&PRajZK4tp0oks;zJL0a8
zS)tmxyXeATKW=-7v|dmhm0WIma7BaX60a+4a&8AZYPN$?paiBK;(<$Ikd;9ga
zeG>z#&opohSb+JZn2#i$0|S5udKopO+G(J+LmNmX#m_Kk8(?Dw8~BHv>^*6)lYcgk
zM(!}-T+PEuT=jYnYtvAT;y>@D|=V=zBCXi9D
zt727$0FU2Ue^J#;zmFOwnFKb}0RTJna&0|aFT43d{exhiJyp?fQ
z?M#N81-ZH&uYYE!GZO29@yhHFO}v+7m{0A2OWmOK4H?RCOA`w4_w$$|JER(?yEV7OWJkdgh#Hczs{6YNX;nRRBqYOlFr8}Or*>WG>GmrW~I^F6^2;$zjQ
zL5#e~c9If*Q&ZGzZ&7=)t3MLeUmYV$dXiPq@?d`b`{8caPP4hlqt^*|
z39zQCR;X{F*Ov-Zf5WR(Q`dOy>)b{DW=U#2lBM=q)ra@KU9>s4b?FGZU_`L`uzyDN
zVbzx0mTv9JTKTD|I=eNZSF`b{QsKVVH?tsGcB>WcQ(>PPZg~f3;8B$x{avI-{p5ywS?!_S_ff#fspoOHWhZ
z$x|I_*WT`;)?n{apY6FYxW_+ZkJ_BdQXLK#NTcBZv_B2J{Ts<^{Zl}Ae&pu8-5;zU
zvu(i#>*s@3wV+q7s%b&h1+vpURFk5ZBJ!H
z@1_!ZKyk|gOXlT5kzqG*0
zo1XGft7v*4qVJ)2rgT|U7KiuLPME^%#FIA%TpiQ%#8A-gcIibU>HpXX2y+qPW6{Vu
zayBvg1FTbr-;Z3Ek;y7YUNZo5_Yj!30dtq$GK!Xufu0ZS
z!MQa#Gq(biQxGEAUV`X*e^B(bFo<8H{%1v>0kG7A1icstUL@#fAY3s7!cvc#?Gx!K
zAU>v70%Ep@|6XghPsC?bT}XYYCoEeVi0amFvIDNFX&V?S)hZkh74{sX!slslMgE}J
z@($^znas?VK(w+rtv+ttpU7>_M#)KC!~9pft)TL(dR8`3L64$T!aF^A#FKbCm9KV7
zPUQVr134p|9At88*b7;J0Bx@t6-}3UtYVqOOfMr@;I?FoCz2jJ4i=Gh4D3bBRNAGI
zT0pAmYh(riAW-X#I{P)f9XnA~)qpfepCda?XR>BGik4=P@mhL>*>h3
z1sp{t-|TUfMf5bW2_e_}ReSM^HToMsm9@^91=K_V4T!GJ&J22YeflieaUc9Hu?fpYAd2a7KwY16nR#-*PiUQ
z^W3OWRz-hg(L|(0nLZUW1ybyAI?NS)5>&9ZW)(4t-MV4VrY|+
zQT-R5D2IG=iuMi_DR+M2EFO%zPuBR^dFmQOT(EQLd1z&<1gBWD1F8cuo
zROfxySVh`BXSh^HO=^r}w1<2yS)dTG2a!1Q8Dg)ic3ktPqK_p}lNna}B6BZUSLllS
zYqMhYFq0k5(_#)lpxYJus6S7(5`e=f8rqkv(545-ooEI}tOPWA;Bk5r%5`hUjX{M1C%X9h00
z+aK)n&+fBlyCeFS0x8@--O4UQK>q?RG2{zaGXvH=d1l!)T$1b=cTE2EA>l>(C2!7i
znGVqvJJbbbBKkukf(r%PAOBrC(52VN-ah2DN`12QmIhRNN6xtH5Oz&xW_%R#x?#ci
z2)JIqeyd?rhxM8ppqlAnS!QWFVpNqCORE{Za+VmKW*_}oKHEow(7^wEC{S)K%|lCx
zM6LM&y%oI~_L2Q3!wGp7jVy_zr|1NHSyg3CG1FC$(zE(9dWL#W*4p6myrt&~am&>k
zQ1%QU`zj;l*7AUk1Cu2O&4m%UA;scn)*EP1sb?~RvWlD4M&=O9t4y8GEU*6ie*W3n
z^W`t-)lv2U$ITx9^JckaxtF;U?q&9WZW8?m_CKaZ53@sWv&`rYnMC(`k4>WWQW7oU
zu>Oft?u#eUc^?l>BJc5&XyWH45fex#RnBw`1JjTxGmSE2QoCh7mFok8)5wuXvM!Hy
z)@2b(!`rc19b1*_Afiy!EHq5nDF}p0{WgjPTp_|?9bAnGp<}jLomo!6RIpnF8_f?S
zKMgF-yYZMeo)Xm$3hZn0j?k2kPKc!NU?Fb4
z;29)E)g9t|cp<$)tZV&j9%T~mDi^(mJ*25~hF;1f5<6_WuwQ032}N=$Bal#E#jXgl
zs$GpO31931W{Z9s^~xw!zlEl(pKf}ItoYsSSuRM{ye2cFq;~t?MYRnxYhR%foyf2%
z)3n1il^NPm;z8oDI+w^I7JdHkgQc!$tozF)u2?^hdcM;Ld{KP|&8xmD);ynnUS>epYh9Qcp&u5Ed7frX_o&t(t2Ww+GndqKkCAv$
zPjH4OF;e9OL%xJ(gFBUVQOm%5W3o}kr)GH6T#qX81gCovBUDx}=UBmkp{wv&0c(yY
z)B7?}Czac-K+JjPVJe8|Ux86o)j;DyoogNHzzWBxcq$e`75ih2fw#vry
zD$(Wwvxqx83;zoM`5){oMCC_DvTHzG%52I=*XteWiYNfyXQk^B8fVt_i)8JMikgpT
z{gzlELWe&mT2B=-=W(L7`~9J!)hD90aENF{Sb34F<@$$sKaLlyX5D3MD$RajP_W8C
z+l$<*vSu{2B#solG7kv)haf(ny&%-QXgCW{*PUtAp5xMO6v!wztUc^629|@1^#ZYG
zRvllyrmg!ws6kOiL_a9&aA`)#is;LivS!t^+0#88Zc)1;`YVzm>_r9LK}A{PSl}i`
z^^uw(3!Ec6lH1evoU?;l{iWN>^oaT7tF5T5Bkk#DC%d|=IsMgb-IxD5E2FJlD(?41
z!z20y;8s<)!C%bamjRo?9Afr?qHAtXUrPQ8+voDn25gHd?5KQ0^#!(8$MglS(HC+Z
zec>YX1w_b-O0MJvaiX59XoPm_rw}tP0zDS^CD(7RUNu#0ZgCKp5GfX2+t(4
zTtek4$eb6||5SZEnH^NL?4UYwCNg`EX}3lTkE$bjp=9QYYJxqPjfjv&G(usuFKBVj
zkQNbV`7Lp5h6<0T$6%QBZ=
zB%|DzY^wsbxz}4|c?&G}0;?+Dsw%Lm##vQ`R@DTnYNAy&$*P)Tm0i8Snm(2Mh*foq
zRTksz+NP>+S&!7ykoElrK1o_=J+j)ouQBf{#Wrp|vcV)hZ?qToy5lSob(D*oxZHZSHzu9;$#w$8)pE~O#=vAfa%@#aW81klrgn(DUX-Np`c;=EW7No8i-kt+tMcwIoX
zU62Gc9&_1mh%q?5(375|xAX}{`GYzPZo@#Cl=9V%h-j2&!Pg*zDU5wKn;7zBHsDgp
zz!-J}7~8NR6*RW^=d@P0H3!5WUt8GxtVpZJW2ZVJaB_J>_ITg^8;gHvpV}SK|C~yv
zQ@&Uq{a3s7D4E_AC_oKF_<2A;h*4H1>o$vTKNE)8d#MQOrQbwF6?-jE9t6r~7H*)u
zc?2k(vT!raW(VxaxK$GU<@(XXtPNgSj!{&e5T_3fk87%dDa4z
zmE4@Q0G;;3qF45QVixp=C^jgDfh(r}%o6PoJx4!Fkz-Q5Y{-_;Q0JC`E#soMzlb(}
zV9SSE6Btly`bDi-E=4nhsrdrJ1V1%|8M1R6K@DS}K7Qwj<;3h9QEz-ky(y&J09Ee!
z+|Kb5AOJ@#H$&;+l{^uUB$t-4OD?tnhP25(wjX98QqR{b-yPLG=xK?>@`w|QrpK-^b>cf!xW&J5
zx-Y`VDEaWp$B0NQpJIB9AZnb)>q_i<3TjzNcUz%N`UD}jxZYlc0yrN2F^KfyiSzWM
z%wrYWgdCWob^}De*iF#ENAXbu$j&gx3W@HMIn{C8E(I?`FDuj0Ycujt6zt<2LHpyA
zGOG5ZXt^%@h&`V8i12l^m16`|Q)*pNeF{3YIDkE8kX6InGj=BZ_MZo)9>bAmB%BDc
zh>pOFjwpQm@MpvrGga2oV$8`1&4@mSq6Ez>^dz@smFeQo7O$PyTrRqUxcsJ`mG~eAlf#H80tU|MxsQhT*i`!`PQoc7X)JGJCID4#(jgu%iO71
z&unp0?{HGbeIA8g$c^W9?zP?TNuzt5@|QT}PiQawmb*Q)(cK-ECbW}zrjvQ~=Yjpy
z0TwcjUM6J^?&OFbMR^?U!C?|M=WqB1V`Rcshy)l$ukjV9mOs{CwrVGf;H-!L{VFA;Lf`g#Xir
ztf$|6Cf@-SbU+Pnzh5*kcPjGC1SjLH!3@!~+^H$gOm;Gcn~cP`XQn#uXbAKq5qRbj
z=e^s!$CtFC{h+oB_M#pX~NbO@S+H$+F+qeqJ5s3}OTh)F+V
zLMeizYQGiHi{DD8nMIzmGQ-g(%M{Awmpl2xQg1+=)L)3xkc`_R`fE-`PWL-BQoG=K
ziJV&dEfZ_v#(iaCc?fdDUH4OLo^->TY|9VUj7z*Nq}q5GgqDvB9*mDy2ir?eajWG8
z!B6qR?AB|@N_ON2KgIvEyueyMu6}u$D}KhbKwdqnXrkbh`ttc$$Ef1Oqe>86
zPim%`6CrNZ3hx<+EJDJCnj`v@AwY5fqJ{uz1_%qhUTUUhPq01lw5XmxAz7NU0>C_+K&9*=d87gJ%8ySBgzA9$qd<0Dq}KDIr}F1_NP3WqPf=l0%$
zsQ$$}EWX*+OpnS&c_IX3rWZv<6&JUxXSaD^y(;sl;p?r6PCyVTX(bL3P9sZPaC>~B
ziG7&vVFNzk4~gg>0+5Qy_5xMWnQSeP8sP(}J=S86YSZOWLr7ithLqZ;uaJq#KmB{`
z8arj9?5)2WI-H^+o|G>*!<#tA8m%^~c^;GePU@tny2wz{$L8r3)my)e(uZp(WE`Sp
zLpSp!na%u3wFUFRQNIEcn%(^SLw558&ThVd-TWQoe{MHV@Nn%d&EFUL?yrEpuj4vn
z#S3?$R~3Jxa`9?f2~Dg-k6z0*2KTs{Lg$0U@kzQLqJ~Po;YHy9`+aVJ3TBuEm%GP8LDOv0Hpy0r37S#SS
zbjJC4`X!19Mr+@tc!zF6oiiBT%^VuQaN8e0i{Y?A44YVwj$!!9ErQ{==I4cwF2OMD
zeRdr417dOlqNuetA2yM(Aj+Z||KC@g<@hRMxazvVwYWunRmw}4uHi%Wo2Gu<0mF`U
zo$*sD)h@gTR!yEB3-XwMklFBG>QB~J&Wo|K-35UTA_DSP@e?vX&+L(^t+bqYJBKEApJkgHtl+Zas$34}etJ_x>
z1{|$jf0N>@GN(4pD)gyW)A2%Id0DM^C4NH&k~MWPyKQ@RI@tP*0QAq;YWwE;H@X)D
zH!seuzt_W3F`O`>6O!FoDRv2akEqsEc5-W4Jy4iOd1OHGPW{hUK+HcohEFz-sgwO1
zvz`}l3xe&7Pa=kb&1AdB>kd}<5{EXp&9;g#tdHrlV->{4GOg4bA1hHjYHRW)OqH+5
zexcNppUOe-g6qK6lMNUCHuWAfn9=xB{4z0h%m97C+%s3b`m_*$%z%Gm{(@rA@}ulB)I)<+VN7x+GV)=um2;?aK=8@Xy#`
zg%91{nvYd@8A0wqJ3IpUCnx
z3@(>;r9>}yL5dD&B(}*){Ui3g7{NlF;88-7HHvVyQi8tr$+uVKWTS*V8ev9wu@d1T
z?a|LN?AU9FXV)+CSp3qwdHVF5vs^mS$)*eG(EYyvN^nZ0y2}G`k*W8|&Khy|apFBE
zZpCDY1vX}K^2vGM2?)gRAFX7s%)%g1XhnPc8z*P^H%?tZkx|K(tYo*FK++aA{%xnV
zB)g85qz;QgEUMl|Gr36CUe(HzTU`+dUKvP?5##&MK$xk=W*YxjO&k6%S%tC5pmt`$
z4iR;d4a<)=oU`=I=>N5_bvWK?qoxOf9gEH`>IiNo
z@@+uBuzh#UlW?tnMVQXVremD)>*%UL#C;yD_Qub_=R1r+qI(26<8vL?e3Bga`tw|7nD`s8VTcN66YhsFv=^|U=oK-lCm&*fWt$9pWgkRo%N1_{
zg~-j;d(b@bPdLsAS@YReOBe*UQPgnp%`K9N(xF2%l=01l7{T{~y~r1#isCps-jnI
z4Q^kO+sxsD`qYVPb5ZLrv4o}LZBdP*R$RYBV#-jhp>49~x}Dz+t4!`rTOnq4r|NAArJ6!-t5C}V|CrEW$ciJoJ+z1IgTJx<Am9BiqOPcQ
zZy#bS{_EnGtcot|vkh&ec7&dofPC%X--jl=yL_gWQO
zu3r$`YlWXu6#^)I@)Aa8{2W!9Ve>n7Z@iq{u
z*g|bTmbftHuuj@bhq|VPx)y(h{&jy$oyM2^jrZ}bD)xvWjcz_cLI3Yx^3ST+VzC3H
znH@04Gy!fCI1=jd&)Tf^(zA$unBJT2EW5kD?rpkrm`GiYlk}uq(i81Vt5?=h54St#=EHt^-RbRK$l{gXKzE{+SkJ$$&JJQLQ)4?5!#`?eeSKfmi
zOGb9T!Gi-yRlo;|a{R{33hi
zQQNZ&lB!OsI+XK$wtn23Ol2kCbteu8&v0_jcMpNlZ9}Av?BSeEUPPiU!CCL?BjIHd|9I;?_u(UG&$mY`u^nu&ZjF(YzT3k!SDDMv;Xrd&jyL7
zM*N&Sb$VX>MBQ+*+Z9~sTb!dxyy}t-0}kE}_X9|J!3P|SQz+xLpB2?J0go=>$ZA1x
zIne|;PmK(@;=|KRd^+`}c%_!)=|9OsM~*-;D>18|J>5j2NFZ5c9*fp#(x)u}vF{t1
zD`6@u*r~Dpjcm!)$+GyEvB5gio)t_45`U33@V_bzxj$dparGDPr*YPfqNB6-zKchg
z=s)zRaUjMfJwme)-GPapBP)Pl!#%UA3LZACnz(yNLRrB}|Cn;(IX3?A|2Fht|4rzB
zEEo#COjhXP7H0=SKplN3I*Nm9um@-Oe-G+sh`0*(l`bGE;RZp!gkGUm{z<3?{+Wd>
zaE!-LZV?>JlU@-V_%qpFdU?gu{1SuX-xwxHkP~Rn$h7v)*}g!+ki^+sbg4)p)A#D`
zpEFsZOxZCs^PHiq^A>$v3Z8r43D!YtInzB3&l2J$qEB~_WjZxw&m6$DJ<*kMt!0GV
z5O44xu04V)&J!%|vgX{D52C*hbM7tR|9!>bWS`p~LVSbes?9oiiQsAC4y(H#ckoR1
zTtbJ%W29cwjnwA+NC#Yn)Y6*iy`7~q)?f~_vk3j1E%Huvg$ErXr&&(Ex2fs*%;9W%dO;fp
zu-+7`P7t^`BX<
z$g!sAIvTIdx=!1(*yT&mv}#S>>?Oe8vkde)+zSUp1ARa`gx-)SX-Uk;gn?5{wRpVS
zdecO9aL0h}d2swW5ODzEUBJrIMCS#mr}L3!>e76KGHZbDV*!a^mAyp#GVn`c3SIK3
zk5d=0Fmph{2j&DaV1_)O6L6SXqDEK!-ieGNny;LQw7x39#D{S&Z9v}6nZ{mmz<5|=
zF+FbhlYvP2)rVnV-993t6`
zSkJ`Vg!c-qhD5+PB3|NH^pgh=mmK{uvhsKZ_zTuE^NC05&
zTRw5x@=AW>-@8fDK8Tj?JOolHF=_X(^Q%m
zScYm{%@+!Tjh?~v4leyvfO(}~L|0wZl4%)z#I0&(7ml>P33T>Tt9DD)xLx9CxD@!>U
zs?WSksNYv*)om3%hoBc3n|Oj?ik<$MiGB7po{roNvd#S4LAGo5H3gK*(p|iC%$`hd
zT&b*LaiO@ZAUfzoIl8mcxj!St-ZnOhJ&A2jlIz=F;oUh_zmQp$x>)vuzuKQpJK?1I
z`)FK3&|?sO2?S1tuF;bQtA^GP`Hm&Q*`8JhjvXkuf&(RT$mIDoU~5i~1U(OC&Xc^K
z8vDFlelVCk#vCSjUt-}@j?C%e%%KvqIt)EXGWiP+l1S>{L6S3Ac21RJw_UFz_nnikEEm)f(ZqI4o{J7FgM5R{SuWbj<^vs5&nP2X4KxT(E7
zpt!uF(?2uZSy^62l&zfZEf8z#MPP~aIu&$SxntSv7xguhv*6E8?b?~KynZvHui1()
zH@4xdjBOYLqp=M?phsa*&RAX_i0B)yVPKqz@UirCETFCMTI>eIYlktuK4yj2m@~2j
zVY9L0!0Xh;w7=oE@R6D#5_LEcLWE643sl`2E3{5sF4_MplYL0P%_&!tJ*|43|AA{s
zXLs5(SMuxDs%tocgdZv7}p)BoM+A=*R2((m5|5ZaEYmQ$jE;`
z1$RKG#J;G|$d{~JCqV990kP^Z;$qvH7Cw+z%GNKDP~pd95%M=kB%s7EZxP@D#rANG
z;`C+)2Qqe=jK=-TrU2)wcI
zSVaE-6AuZiI`vd+Y*J?u-+;-*K=J`6#;G)3?M|`wvpJ<**lw79U5=86wRzsf^Qtzj
zFEJC<;h57eLMGPYO?*PHjUcyr@em%jj4CdL+7#PJLnBTV%H?#f{MePI21G0Cv^WWR~6R3-(^-A?>juZbjOhm=i7
zOedDIat083&w&43@k>;q(@GqOs@=kGdNqU2Ud(Ty*N)z-H++2ZYS1}Sv;*L
zRHK7K>^ydaT5gmSgZ)mt(O6c53GC2Q42c(jbvsjY49zNf04}c5dFdw3lX#NG$#dyR7(y7yqHq@nlaoi>OBSk=>hF%Y
z5;^sE-{FdX9n(a#43n*H6*O|Hs#cXmXyi&Z^U4K5SwNkeEwCH`&_iE2H@X84p1AJN
z<^>x)h03uj3rdJ?to4v0O%rT}AW0R7bxE$o5T_r5HWR`Q4xx18lUAtTrvK$@hM~}a
zzbkz32Co?B>r83RH*@44*)WqZhcbp<<;EU>pp|k>!Cfb)QEK{x;Pi?9huc!ij=PLN
z<-!(1&9d0N1Lc=}S-izgu_cPw$wP7-iAco2f@IwxwwN;3-+~C|afJ$WuVRRKD{$V@
zn9O*?buD=}8&*nWU9?}id|cb+W&|^7a@6Ru^gJ)K=C{-6BuaR;$l1SQrrXJauv7Z`
z4mtF56qncNN;`yOur?sXNnpRo4<1dV
z#B;dkWtm)uBzWr&JO+>Ye}G@_3Ej+#VA%Z<`8d%bp-^m)--KLA6fhyzL*U$eN(*G_
zAjMft?7FRpaYF;dZ+L66WkN)sP5YThrmexy&P48$?(PwoKiPlEehz1{_>=W?oPEd;
zdgfM%tupy$qGCK@Rd2GYTlAbg>2y=|7U&?vVX7U{|Aw7PT>RqKf5@afPYP88g#H^{
zkl^o+6Hlw~I=kYrdE8z0n9SZuvNWxPM^xf5yKWzxpIEn^MdoEj4m(+5AGSxU;(!>Y
z>biu`h@WXh-z}3sKdc7+rW&a{SAyfyiM1S`2LqPol`^ZLJVC4#*slmBMEI>!n*rhB
z7>pb=M5G)Y1LmMLY
zOo9dO#8&~24^uZtu)qiEG|7+C+#w9>oDT>v>o-L798-)`)|p;%Jnf|u7>W)0fG|dQ
zjS8*CiBlr$eP}f%g70FFLkS42g&&lwe%WLM4l_GmoElxfY=SE>R^lOULyv_Yh=)k6
zYFpKrDkyF#NqsE&+%iS}>|^;)GJ5Nv80UY-HbF7I>VT95geyYlyCS>5JS$X4|*0SnAdtgUgh>6X;UPbnU(e7`1uLR|qqH
z4FGbo)jN{Y3ml;eW9`0AC_Fnba&}()Zs9}>^!;MizYyFF3+nSY#Eo5{OB5XLS7})`
z(I@*dUGLF@E+BLqV`4AfwV77PZsNgkuONTES7MvYen)Rfcy0U^_
z2<%mDsjLldJ^QOeM9sbDiKv;{yhPAWlEXbJuYnv435IwE2{1(FOwslUj&kMP*fPPn
z0LG(GF^Fbl%iP1P&9wCgiXyiUBIJkOZ*v5?F_|{$`t1V*l$%^a*ba8G6;(NmO&;ox
z448Sz<;4u!T-oOqa2TYhRc`ty(^u`5015QrDjbOX_YLe@|BIveTqq(^RCbo%mf^^)
zohU}?T&&f()oaYA0B9s|x1|RO{Ixm0EXxd@?iR1m`*_sNX1d9E
zLl5T0nabeiIzL(xv+GAPf(gwfEMcu)*58xY>O+|}#9%aT#ZOpX_+Eaa`n2e1YdQ;0ZB1qxMyS1syYeg!BCxVy!~W1iUWd_n!3S_|B)>x`ZrNz5>ZfriDI8{auXY$bCLe)s7y}ca!6o=JnG_9
zHgRQA1XVNsN7M`TvD!`6GIJ?gaBAYTd1aMi_tl(t0b%YP8reS6CH@*_=lM2&^Y8u
zk)N?zfXC@*&@_&sdw??mT@jrxNMIo9ULu27*JEE3(C0FnYbRFfeef{nFN#4>1YGw8
z2+{yb{yu${2!FBuEX7PA)`w(IK;lQZs=)+;g?fuNvsLGE%TOP)M4hRw@f7Vq@y6}M
zB|+vf_}-E@KH~J{!CwD&HwaJe9lR>YGa}gTpV5v@azDZEt=ts^YlI9s$ezJ_jpAoy
z=3Ly&x$=%7b50@)aEvi!)D$@iz8Sg8#7kX*QH`e0Q}0QgdFA?-V$7vfb#Hb%16|Z9
zr5`4dQ$12)RD?neruLI?<&bIn>hGDh(b|a^W;@%V-bqzfK%FE4YLqp{Yh<)cW|4F+
zZ*wHwO1`;HBqQIFJq6|Jb&E+9CdQ(u4dZ=lA1Z6VG3eWuB*hcfN_?PX^)N4^$-W88
zZz`b>`)l#=y_MDaCJGKG@UydV7W78+zc8S-C#ZHuBSkyZtNOb#!0ZS6cn=*e`V=E3rucM;#3wT(n+*s!TrJ47GFx~@=E=@Q}+ykRIWETZ~5HpGTaD(pCYq>!hKr7>b^xg8AJgf?e`Lj
zR87by=VoWJXX+OKXmbvLvpIYu33`6!or=kKxsWSPlq;5pt3?!&l=39ChswbZ7QFyS
zIwqleytg9566expyw>3I3YAWFZ~?E~V4%J7+Tlq}Ga~ej9&*Y#FD{XSEaOCC9l_BC=X1!n>~#V$o)V
zd)B=-)_Ud_eQGb_#ADXI)4BKU+yN=e5r2IZfQ=)kr}(9aJ)5?&WmVuZLuv5~q%J7h
zo+%FOy5H#jB7`f!g!!$s9g?TlS>B!*vtANU(2n%$~9<%HL<>ri6=G+C7sgG=oTgA2W^
zTD?(y`Ntqr>S;%xl7c1jBu6u`k?TKxgw=SCR7jjE1=?qPrjSB0nh>1iK>Bw||077R
zIe7-FhTG}?4a9nxwm4Sch3ODidGJlXF2YX~i(x;|Yo2w&tSBJ5tXG{B+#B~>qmtt!
z!X`&$v0vx9xB)?T+;Lks_X81J#?lbgPtzO@Pzi7UwNO6PS95IR>o`BpAD3~l%l-uY
zNbUmaWwaTLPq-({p3dnrx%NwaEP*;b!M?i5_T&T%?XrDO+SAdWS;Y0?w++X+=h~hp
zBew5ZeYt6_2cgw_?`~Q`e;WHyt2+_6!uBXr;&drd*GH_=w7SD}7jZi{A){QIMl1cc
zCka3H=>i8VF2+6q>P*yr)i*74_+HlDUP=59X76Cg0ZOD`euO|_BOdn?x5Z2lsjy4G
zB=a1ZTCTS^$>Uhzcj#xGWQ6IjhL&FEz~wByFe(9I|C5-xhmF&X2+)ws<-bl~F3U8A
zu58xMwB{3#oC#tdj1S(;(bI??iS#yn#Wu-w%Y>}g4TP+p
zMci2?MELy|q>}`y=c|f+5|8Nws=sb=Jy(Z*{7YuHrZ^YUi9O7O3ZICrV<$)VmkO%?
zN&z@d?nd_y{@t-oQ?6{AJ1I9#{MX*;8F!h?0O~x2l$N
z_^VGJaBrAX;SPR}!$RO!(-vFJg3vr;GaY#CT!+>eJFr}J`1KFl#uhl!06AE
zID8t`&b$K#V{*jkqF>&ULGoTw9Bg?2TRy>-Pq5|l!j%SFNo`!sXY3LKXAO_Q8K%_$
zoK2({IM4B5H$KIu^{9MyG&b|Oz`$vd)W%LeshmThrG4o!)
z^r3LB9zW2M+;44Kl5@>=<77UqN9D7laVnqJB?aRWNo~B2&sf~)Igh~c>et!^dQOVd
za~|k9pY)thdd{c6!Sq~G8&~rgd%)>AkH8tOzdrzH6Ddy5d7$Tf(sMrPIiKwYj=4X-
zv6J`MV@^LMi>s&g6$5bkNilH#&V$`}m{03b`Rp)v<<~U|J}y|*YRu=;;fj%w*cOLQ
zBtgx|_Zwev8Cha_K+r%NLyNQ8*pg
zf#6b&*p0&P7?uDXdvH9)^HH@WP9kuypTEQGL^j0EX1=oA*mLvs_Qu*K$QK
zix#J)L|8a!qcK-;S@>4}4>l9TI4>RLS`PiuS)eh*SQcPQS$mNkUakL`{Y=IIxSBfx
ziizNkas|Uq)U|g7!8fRh>>_fw+Q%Yh_L(_2Xs|1(U9nDX^X7#Z?L310%~D3}$`lkg
z783mgJ6^dqwyr0UlH!ycm-#N_$^pIUm#}v!4X{=_UdK6YH2Gs`@-=CaDCp1{exbm$
znoU>nu9(AO;<7QW^VnpD2shF`{nr-5L)b9);Yi{FPS6quweLAZ@<#-MFfLgtEZ6e|
zxvaCPyBL~@n`EsRSFe|sNybL`3FC^rHjVRE?05bqH+i*_?9e(n!0~J1n%GhyW1)bt
ztYutd57IvVLp!`~*jfgI*0SM}h42}tbIM^r<3uNUn7(V1*rQ|(qBO?j(3({xcQl0_
zGB?9L#BtF|kf~cuSz<>Ii4P|HK(Lk3hWzyzKth2AEXl4|iJ^dLN&o2u40zPbby^!B
zME+poIlBK@$KaiKAn_YIC}#Y#NFug}dR~3{Bv1|S)Z_jrY3oSK(%Z;ap@%sf6bvv3zl&fV-Uk_#x$u_Gn!oU*aH>I2c!1w=M)V^Uu_^zc%BrmMww0^g7WApe?2HCCPfH;9dupqXEPZ;rJM@g`5b>fj#prZ
z1&U;XRNUlmrX&0+kr0&VTvZpiT^Hn>k~$x5p*sCeV?>M5?JsP8aQBm;^ur}
z+4YQ7RL=22f00i;Mm{~{Na#)-V)oLAhg|NeFPB(6{NbHopH4hsP>X{aXmP|?mKYP{
z65h!0Io`ZuyDXut!6oD0_8!;YO@sXzHqf6zhqJ#)qB1i5;pZ?+e}0=u8tl&;r$3dn
zSpK>GtR3i2f<+Wea8v)I{;U(qI}4xbOg+{&r!&L8xHADRF~;C;mdXN$mxL!|Le8SD
zKL0eh80-6!PaycS5vnO}CcPmbdyV|&)3?C8QUcx$2mBj342wx+(Zpn5_7W~_^TuZ<
z`$opkN%r}dW8dQ
zgL2~@HX_6_zhz@pH#Jp1Xu?(Q%Dt$idm9Wqo}bFNUxdT>YF5|Ih<=KerN_w?0hj-N
znQiGrL-JD3H%nd6u`~Tu7f+mVXqMkQ^3NP5UlRHik(e(G)`i3Yu=H;UB#)-!6YRO}
zU|2667Zo|V4u!B&w`@a%5xG87|GZb+-``_*?r9Hw&y_hVzfR{qC5L0a=Zc?Go3=~c
ztO`!e`=zFU*v$jWy?*0KM(!+TC8q+b0463=GXp}Mf|3a6;%#zkd*UIRgTk_7mB5(9
zCRvg~TddGlCt?9*;7cq!$9&>pyhT51eh)w-53x5AbR+kpAR2GyIs#I&
zaqnYq4DEqnoX;uh5X+zS+`S^gS3ni+A0b>GhoGQ-0+Pan&*cmH(nI|(n0cE*4@&4y
z7i70X4`+n43e^+XX@2*JW`?PS7=EUe1rpz;T1Vrxz(0vSCqK+{BahY(?l7OS9P7V|
z5+ACkRu0xGRpP~WezxjE^(U9pa6gdBRaJ~YuG|9tapKR);Ol^r#`N94H6Tn~aoh`1
z1Crl3x(tKa6%U&*B8f~x&W};ydMPUXf*RV_&wve9t*?8~mEf?q=xf>f6dFM9b_hj8
zk-zbE^tFo!!r7@0aA;g5HZeY*@w|(Sd4WvfhE6yZCwmJj4BitVZaXQd7luryUB5E!
z_Ble6c~1t(+EmNp2aaMsSQ
zA!{ct9~GX60hM`uv#fdtjD4ajfICf&Iq{nP5_HyObRIK)8CHb%I{96&fw9PuiG7Z-Vwets(C3zVqMB!&ID`&&;Mn;=aX839>a
z-xL>Om|K!36u&sXM!yMxWS#w=g0{Z^^!>+!zW*3#_M@*kmoJEAMQ%B=W^!K_H;EW;
zbL9_=F3gpm{c4G)QU!}9Mbnci^_hjn9BBJ4v#uhLi4#iOHhUxp?H|X=aloXeyW%zH
zM5MWGrR+CYNWc}SikzvAlRN2*4qrpX-)zhhS!eOfAM41X>Z
zS*&f#R--x)RI?Y8O6_%(dIjG!%28KN*ly#;TH5qOK1M!sTonH%4npy3;wabOkUeg;
zy2puq%QFE&9@WQ9%N1UAw>S85V!4T%TkOHRjQ@;~o2+TqNmSf2?g^Q82ElBX5zMyS
zOLBfQv=d9rbq+PZz*%&5`=8CXDC<{I6<+9h6Hj5l=tjp9)R6MVIQj8Q*KdceqI
z1wapzHykpsjen5z
zQNbgLC76Zk@4^j!Qu}mV;M=3^rEKawxGBY6RM9W^`Wur{+>MXhjjwe&^z^yo-_ATF
z#&Vd*_|T{0Uf-_od)3rkRqAV+?gN@zPIHI*qf$h#m(0DXD@>DPhc)?+c+GSiI;N~Kv1;Mt&hlC;*ZQpx@ASpr(IyhLxomD!AZ|TG?{krU^+jS1`#hY$^
zd1y#DTOZn4T~E>b{MJe9(HsflAfgS0skdkcp*KemYdP|hW$!Jy84pl8TO!(iCk*$+
z5pCxMt9!X=t*#eK>n|ThWos)T?1jtGdSsHE+4?31Jze=z5m}tn(K|8Hrl$~Lz^|mF
z#=}45!QLh3w07lWZ#EG_`WxBo-jQ0SNIy0Hi1fmxoZ?=NzSh6wWPhMbC?aUTQNpM5CIUx#Yg90dGJ$v`AuH*7^=Tfk3(W_uBq0U6+
zaw*q`!gb<%kpSTLB|xOzSZGYd?qT7jYOXX{{&k6VeH8-oTLT4{^i|v5Pgzr|vOA$}
zp2X^^T}2&5R~P0JuAO|dzdYDlH(Fg=$S;WLf0apoZ(#x9(hP`tMO%^pxid{UfxG`|
zTD*@Ii#}Fw;O+^ue#)^rqFxxj#+Kz@QoXb?S0dMxXCsLLJT?KsGyj$FT1L6?qZJ1M
z%Fo?o-CHIfUskiG^mBO*rKpr^@g_9@60uABjY2TrsVV_+5|5@jF}2pUrA{1-@0AF<
zP7L7uM&%7Muo!5|MIN-GrV^jSoxqUC7|0o
zK)~Ra%GJT5H$;@IVx_ic^81E3r@7J|R>XZEMytC1#K9ZnN(gg)`~NIXHX>5R#K}JJ
zkEP0Bob2g89*`XGC9a6qUKz0F
zav8s~ykx)y%5=#fxaNf!P)sn&)bYEGeQXT(Q}6>RN120gTe3s5s&e;mVpbxmP#cbr2kTi}OO2%jJRZtS{S
z^^Fi8yWm=l2O9h5RtZv^!=vFNV>8bao7mQ&HZkg|@D{)$fJLO3h=R4Bi6~f?1%q+v
zMMgW_$SX**FqxlaydbzMQD}k__?6c};cF-ToVH4~~=t^bj$kEUQI`1pOi@oI4_s$XC@HDQZKEyn<`-DW@pa;Djzt
z9r*lutrV`_Cqc(2NJu*MZIXyOUe48-U8?SYYUf8_lA^q&FBa1&p#+X=FuW@)dE%oSz{$5h)hUx?
zF52BB7wzunT{e5-JfPaTS5kFoUmNz{g+X)O%C}aeF-!+c*Gi-tbnON0GVkM#_5Ps#8%vDS+dzg$
z@9Nh`%k;Lx(>)eQggU;#X?;0@NWUxS_mecACW2;@{=tt-zjFrg)eR(M(2VFV`~@Jy
zq3vX*C8@KN=Z5`>D&de#y4-;v-cpKUoJ>#Bmk$NN5M~65BR}t
z&KZo5818J+lAArCEXdwhE?qFVb7dAk;CBBGno@O#?D=wz-$7y*>kK2rSr$X~V*Q*f
zJPx(F4S5iQO^-MP6T~<0EZrd5nOCs393aC7X|Zf5kbS@Y-<-V*T-0^G{}0RvqfX9b
zWR#dxY*}=%a7!Fh6b2{-2MLfAsJ2+nv=@^ZuoR?5HoktF-FCO#)9!lq)XCjbC(l`T
zyGUibAg*Y76w}?X+@iAfL*r>FJvt`G`9I&E?|@kC<^Ow>%=hy7e(sm|_4OX^i>6VT
z+-qfmZS-L#7uMPwMF;1Wzt}O6rly`A*Fz<=g%=|7;{-kI=@Vbdauv*^
zrGaI-BPMWUCmSvxYkTbU2~bV4G>vhQ8{*lN_d$nI*4y+NG0|Z6Cy@9r;%3Ho18Jw}
zu5rR78sn{A9B{}Zw$tvHI7}XH{!A1e`8i
zYXxQlL^VO|>^CpAOb}u>$(aw_b;bJO?ctF!bqhCG-_b-S`6E8{Z)jP<#g8z1yzt~Q
zPvZs*H*&l7tw09GL2O#K?U=?(Sl>srjQ9vNjgd02QAVL)2;oBU21J_b2(zCNmJNr|
zLPq3M2YDnKB^hV+RbAK?3hVpl;W6Hzc5_Whw+pEcJ{gW5$wF#;Y&S5qVtJ|`kq1Z2
zmUz~xZFHXRqGI43d9EFm=SrPt@HgU1s_ooGV1?B7fQ(d7`mF5$9j>mTvX-T=190is
zbQIRH*W<@62js}J``p%JT*x|QKpx{e1#G$Ilm@j^F^3vZ8g)Q0;qB0@z%CZx;tPeL
z0Qk3=b$!^V&4eWT5(>-A%QXt8K~??qRH7Vuf`6*$Ms4Xk4N1sujFR7yf)`UT%mylB
zIrBm{ThbQokR^UlIAk#on-2ufuPn91Jb)djJ}Np;M1JP^B8b5V|3dAqtTsGL*Q2KZ
zLn?s}g3d@UM)#MQ^Bue=xYOGB`{RtL1t4A;Tkh~DM4r{(BOF(E#*+wfj0i`3u5dO5
z(seLWY--2Cz)V03v|J^FWyCZFyt`O=&JCu*DcvIeTHCewu!bbA8V0#!^9n8IUwB{nJyNch`8eM?{gEEuf@e{a9!~S
zMGj3sis(W_6qmQL3=mPQAp;RnT%$7*F9pkKlqmyv`Yq1bbfz`)oJo#iv8cnbgGsgY
zB$G<}{E80&K1~9HjZL5mm*Pioh#@OKJnTnsSKN;v7I+SE+(s3ZRmW7sZZtOIIW@p7
zzVWR0ZMgM5BWrUTYg7Gjc)f2_hqz|FCze22oA&1n`zs)HV2V1pdV?(fa#m+>f_NQ_
zHS^6OdHorPZQ_0}{wY&l_JqFEaS
za{{y55pPenP;cKRM5Bjgk+ysLgI5mwdoD#x#RDM-zu@nw_Z%kGUfu>hvx39!mbiTS
zlvtII_QSESHU3kryPDBaA>DN;2?X7qHHCEpC$4u;{aJQ=y&(EHyAE+2Ax_d;3pNqExn?~OsobItgj(uTPw=7X9VFZMu?O+QP2`jJW}NFsXn~#bWXFQd6p0aSjt=d5dNLiAB3Y{}u+j7@4g&%w44
zc0}-w)Ktpe?mY@OAvQ%v;U+Yzuv=J#P2aQnDb6Z9FRa3Pp0zd~O~4`xKUn*(4f{*7
zfvA#5D}NbE`%7xERQ3!9?kFYd)-VU&@o0R5X4EWM7N?MoIco`Vg((+p35+q3sH6%{_P&jogVdHH(Le0mB&5mg3NO;C9bSCr}w~JTYNZ9yF^*(|I)*F`k
zc$ca_h}gj!73(giVDb(JhpbSoVY&i-cPj3!zakZ2h5C!9tS^waSRakV8gVD6YpANo
zSVR+saIqkjP%&9sJv*ANml!f-;q-A5yxBYr#P^)AeJrQFBP;~$UMwCsJah;=Bv7?W
zBj-uvEQuoz{Ub==U33&ZIxLO{RTv*KvT=hHzF5l#MS6HJ)Pj$
zx}xc%zzM>
z0A@3uZEw
z@Zy=ASexoo18!R*+$ApB7yn?v
zo5Cd-s@qDbUg03uhDey60i&yg+rgMYSE_?sVcgN=>$nDNh+D0L0v
zWlFf2!|5`-Db(Z^sR`d-wv+E0ZHGsCI$F_FB~9Eo`C
zsNa3SeP0#)s2m2_o&IrE=BcBg9jW6zvi%cPrXdxRZrggwZO?N{N2}qD5M`NB;8yM5
z(H+fvk+{4Zmt=jtk*4nXSlSpL(eH6%jp)CS@yLko{{J(gQ>}tVk7%MUBR&pUQpWH&
zd~leXv&SKV#>^Ury5Vu?0#K=YQ(`w8wY9Sl6I19^(H0du{Nt9g*^ddGj5RJS6UT(t
ze(I?#RiR`%c2>@%{;^B_;|zgE95TwIwPsiL>nBcxK29=sgy&Ber07Uu@FVfcEgou%
zHT`ylWYn~oOm)hmqf&z48xgs?xXTxHG|EW*fSQTQ3jZo`!Ghdt>s*t$8r^Hd#U~Di
zdJ|6c39Qt|s*tvTN_l8qV`JF+F_?C;a=>z*Ah7}@
zqYVY>NI<@
zU-yrzrS$_zm=Z|qfg}y8OPV`$*-z`Tw|u_rr_~{ur@HK?DLWSI)E4lR{Y>
zU}EX!-MEw;P~~)coHE4R#xk9T^L?Y8=NP}?9^tF-`Al1^!5-QHn!)J@+0L=?PG?ue
z52pxI_?3as*WHX+d`GV{QhWtHgo+$g%FbiaHniW`#xe3j4}_6K-owHsrBkUD66>1
z{Cc0d5h;jREZy`P-SAsJB7_66E+jCyd73TCm?RqX3JEP%K2*qNm;<)=BacyUv)WCS
zZsug=QOmbY`zs933fE>w9cl)@{~8G(5xB&Eo&LShN8Nh72js+4)A=S&9}`QiGl}QY
z5t2luTJPi@@LW-r`{Yu)2qh>djVlCQeD(Jc;2a##^T=vs{yW};$T;hI8rSMGnkfz5
zrdrpGG*}pAgbBmMOo=_G#DOLprE3xru=#c$FllOh1A|duPmhe}m=!8Tq}RjTJqk1w
zq{M|{1^4*V2|*-rim)<5f;AGV=$MYQ=34uaRDD#aB8k5?5~>JJHR2eKQ*>`vx(-q#
zDcHt!kRm%n28^!Rcdyrx&UP-QG0aP6TKHCbX}HL4+okSUL6Qo4sX3txVd*5`R+=f2
z*~>%XF;>~SMMv~c@@!636-wb8Dv923vOnGItHJkGy&QtxJxv@T;CIo#MdQ(g{3GGf
zDpfH_B%si-T;t+U9WOkzC9}eqF!$eKLKP(?b}+stR=nA6DtWxO<^C
zT^MI*!vJE^GVzc&wfvdYqv?9Vdl2pC8FF4@&Oh`WsTB15{fT&|3KGLNo4-JKs`<>k
zP;=B>HJb3OTyd2qH?K#e!Y;MW>u$H_K&s(jt
zZgThGgK*s$nAk;{`+-$G0gjvH_<}9X>H$sy8eI14jRHslj*zfMtCz{)6
z?(n+|@pRj=1eq4pNIRH_#S&YKjkI06r6$fi=V=BgINrj79PrG9aqQ9oNre|=>m
z50Gze8F-RDW><8}u6WMfJe8f%@IEgg9brFh-sjD8VK%tm;w!7DmyBYIC4_mq>`#OS
zXt@%1DOZ~wtKb$QO}vQtF#P;pk%R=d5cL6P7thh)pRrW&c9_M_dv-K%h{_S#sdmyE
zm;f{@cS8~42wlO?I70Xkg9Y!VebJ|+rl8HVRWd;k6v$fpfF{V-KZj?MCjFRSw&VHG
zWSn`eH$Nkd3rv#Jz}y@9I_|W~d7`cfQdke=ZcyK(a`l}Ww;r#TAH)3%Twl0kT2FF7
zHZ})iA&^e9r34{%=Rc{<4@so;?ak2*7_+21yVF{gqk>v6
z`)CCgL4@lIrm`<*fWcuFry0rOt#Qn7MP_=6(*vb!SfNgbuq{4-;N2HF}}&wFlWHS3ASEuzvU#>QpGQ+L`!7CH
zhyxm11z~^AZ#&*{F0RPbJ6lbp-c&jQ+-msc(9PO>f<)Mh=)Ysz`T?n
zVL}MKFB4!er#Q87K4Iw1U7V0JnF3jFFbM{whEZyGkp5#1n7bgRE&?wvd*?hhko+zI
zA5;Z6uO9RSQ&((Vs?Pw=j#}4zV9tm(QllotE5khK_9riqiD?$2c9GF{%IL$po@*w1
zh$f~%#4GjCn+&~cGh}7j&8=KE*t27i^qUjV)Ha+0B^m3AB)9JBER2lnQci!8v%eO1
zNUx)HWCE$GSgr+M^Tb(gIWJ@w_
z<^`T|=6;@@`#I%KS}i7Uv|G;06j^P5VH^>8>}0Qui#$Nd8=3k#kPI{QZfRBm)+^AC
zM1vLAa-jWXnd&8w6HINqiLhDOV&&71hxf<)0~Q3ox5_dBx`rDgsLKtoP&vr<6AJ`O
zthi66+D>!(C=rU-n-vcdg6HW!SW}+^E%+x8*6ed4eFTm_E?c_i9hNvKvhZa__4BM*
z{|wkyX8D#L+cIJ``PA)zJeGl$15yC~^jxc!}wS3P6oubfA)1le!-5jYR2D{)qON{)dzvt&$Z)k)X#
z*F|@=Pm!~ZN=|uPKVQ#ZE%bS+oht+rHvfz|V~S7*?Nk1Pzv>u_0sBg58`A0_J61en
z89AS%R9BzW2kU`94O41o21(!xb=V}>)szlfE}qqup(|h_4u|eJM)~y{sCSPyY6+aY
zht*0sN(%sj0h@S5MAK}rVuv9&4&xpq&Uyxp;fJ$3e=89*K!Zm2@E`}h4AoGrYzAYg
z=!jV{W{Sh*p6hp1ZBIzZl^B;NIv%yBw2nwSN2L<6_vn|@A3Mh3JT7EPL(IoTqd*p0
zHA>Fz92T8^S$g~5NY5sGri!BqcP60wZj*t2{t#2@Q<+qQ*z2&zgJ#nS72A`iC6dIP
z!tNSS0O8ZDK;qER8j8Kb?N6%jq=PHf%mZ|hbv6wmCJ8J6Je#PO_hBHqSI3UkUvB5i
zWBDssH4<7J`(aQQJ%dMe};nYY3YN*d@AQToLyRC`7%|`cy47P`8kYWxGi^Qi`8kTASSoFZGkY
z;{bm+ZA4_QUgA?xNNZ1wcF2Nn&o2v)@nIy7j>6h*CJmT=h
zh?pGemoL*3UWe8~FYEX~n)U|>8C+yy;f85BEp805_ozp8Ne)c2_o`Rm7(@4LN(kq<
zL-#z-!g+$|HjiK3FROjM`ZY`^6%r5F4iS?PkI}nZ{Y+Rwvz}O&vGx#w)7AQzWS&h)+r*OVFM+buaiw^#HRR1dt
z+2+)Tiq>j~&ZHm%a(IG2A=G&q!riBu?xvz8({xa@)|ENft?Iah68?^u>eJMGrtleg
z!>D}?T*Okht(1Lykhr9WvyZb$A-nZS(3uOR%erorJq*F68Yj2j!xWrkP8Ua%0{bnO
zy;ft|+co*3mh5H25}JhywzkYnfNs~Y-xy{%)CWQt09?U!4B+B|nF$g;8Xm&lF@87G
z=f{vffI8*5%>|BJ^g!c9b;UN9z&&_~m*hElXXEcG$D+%kP_Cib9Rx@4=d0=Ps#WF!
zyC@Q&EoXPIQ*(k4ZI)#`4_ig*S%T3)w_3NzftAM2a<=t|bHm4btU(H6qlv1Ucw>D=i9Yo-U+Qu*L#u!mJQsf{DTQ*?Vx%-6
zFt4&FHaM8mA}vt++5D_7(y|fm&d5f9eNA*d^sh1#M$1M{)P6xW!i2f?TdbAlsQ5^s
zkE3fjU&1pcuJD-IVdq4N@;m`C3~$ld)p2C;u+FgOi10Z-2N&+>8GYG*Fd0N%GNl56
zNuS6hn(0Sp9h9Rach2FHIY}crO@t;@R7qM<8W);Ol^D!tgeH{dps-S1SZUJPf|D3+
zmaf=7&z&fyJu^BM$*O3DL=!Q(18E5OYQ=*yOWa_0mShIUW6$)l?#k?);g;0)5_irb
zcVG=W?L|Y(3#}F6ToFfiDG+f-o;!}XVaN16S&=&HeJ?f?W(Y
zDdVD9H97vlKl4EZ^!dF?jB(UmkUG-pjE)u3>fds!0p7?{bSy!R3?(~1bXGn!i?>5s
zg(QVxLRb)5l_^0a7Nn|+PU$-Lsn?L~3ON8VaAREfJvS~9*GN*8zWO3^LmsX+Sc5H1
z9%2fq5BKY>KGc<}IuFNU42-p{RYGpC^Tju)J?d$hmL+=
zmt=!VQ{TH+HYfy@Y)}Lj0&ogdD*>-1+O^uu%UFR9Vfxn!$@@nNx$3M!IK|G_Qj1^?gLf-Xat{1u`=wjZ^Sl|g
z1${-de6sa@aSPfDoBH`J$j9C)i^q=;!taNHWIM4{t^9D(_i0U
zfZ~*2{S#*%m!ElT$vovCULB>&cBntfPq|xy(lb4Y_vjo6*j}J1g&H0wEG`A8^==XBL4eai@b4b)6GzB|wTZrr
zP%@Rlt5ohkF*9UciRoXb`h@ienZ+4Q;*57?iJ{8^U-o)6iInl>OG<$@6Hv1