From eca5ac904a4568fa06c356dfa8f628873e0192ae Mon Sep 17 00:00:00 2001 From: Benedikt Reinartz Date: Tue, 24 Jan 2017 15:52:14 +0100 Subject: [PATCH 1/5] Implement Py.SetArgv. --- src/runtime/pythonengine.cs | 30 +++++++++++++++++++++++++++++- src/runtime/runtime.cs | 19 +++++++++++++++++-- 2 files changed, 46 insertions(+), 3 deletions(-) diff --git a/src/runtime/pythonengine.cs b/src/runtime/pythonengine.cs index c296f1b87..626a8ffca 100644 --- a/src/runtime/pythonengine.cs +++ b/src/runtime/pythonengine.cs @@ -500,7 +500,7 @@ public class KeywordArguments : PyDict public static KeywordArguments kw(params object[] kv) { var dict = new KeywordArguments(); - if (kv.Length%2 != 0) + if (kv.Length % 2 != 0) throw new ArgumentException("Must have an equal number of keys and values"); for (int i = 0; i < kv.Length; i += 2) { @@ -521,5 +521,33 @@ public static PyObject Import(string name) { return PythonEngine.ImportModule(name); } + + public static void SetArgv() + { + IEnumerable args; + try + { + args = Environment.GetCommandLineArgs(); + } + catch (NotSupportedException) + { + args = Enumerable.Empty(); + } + + SetArgv( + new[] { "" }.Concat( + Environment.GetCommandLineArgs().Skip(1) + ) + ); + } + + public static void SetArgv(IEnumerable argv) + { + using (GIL()) + { + var arr = argv.ToArray(); + Runtime.PySys_SetArgvEx(arr.Length, arr, 0); + } + } } } diff --git a/src/runtime/runtime.cs b/src/runtime/runtime.cs index 5f0ecfbd2..9b5c4ffb5 100644 --- a/src/runtime/runtime.cs +++ b/src/runtime/runtime.cs @@ -2027,11 +2027,26 @@ internal unsafe static extern IntPtr internal unsafe static extern IntPtr PyImport_GetModuleDict(); - +#if PYTHON3 [DllImport(Runtime.dll, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true, CharSet = CharSet.Ansi)] internal unsafe static extern void - PySys_SetArgv(int argc, IntPtr argv); + PySys_SetArgvEx( + int argc, + [MarshalAsAttribute(UnmanagedType.LPArray, ArraySubType = UnmanagedType.LPWStr)] + string[] argv, + int updatepath + ); +#elif PYTHON2 + [DllImport(Runtime.dll, CallingConvention = CallingConvention.Cdecl, + ExactSpelling = true, CharSet = CharSet.Ansi)] + internal unsafe static extern void + PySys_SetArgvEx( + int argc, + string[] argv, + int updatepath + ); +#endif [DllImport(Runtime.dll, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true, CharSet = CharSet.Ansi)] From 331524f16a0a60afd7f845a9b73222e386747cf7 Mon Sep 17 00:00:00 2001 From: Benedikt Reinartz Date: Thu, 26 Jan 2017 17:32:16 +0100 Subject: [PATCH 2/5] Add a `Py.Throw` function. Checks whether an error occurred and in case it has throws a PythonException. --- src/runtime/pythonengine.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/runtime/pythonengine.cs b/src/runtime/pythonengine.cs index 626a8ffca..93df4c778 100644 --- a/src/runtime/pythonengine.cs +++ b/src/runtime/pythonengine.cs @@ -549,5 +549,13 @@ public static void SetArgv(IEnumerable argv) Runtime.PySys_SetArgvEx(arr.Length, arr, 0); } } + + internal static void Throw() + { + if (Runtime.PyErr_Occurred() != 0) + { + throw new PythonException(); + } + } } } From a0849a9d490bc199752e498946153911bf0e7761 Mon Sep 17 00:00:00 2001 From: Benedikt Reinartz Date: Mon, 30 Jan 2017 14:03:18 +0100 Subject: [PATCH 3/5] Add overload for Initialize to pass specific args. --- src/embed_tests/InitializeTest.cs | 36 +++++++++++++++++++++++++++++++ src/runtime/pythonengine.cs | 19 +++++++++++++--- 2 files changed, 52 insertions(+), 3 deletions(-) diff --git a/src/embed_tests/InitializeTest.cs b/src/embed_tests/InitializeTest.cs index a9667343c..212fbcff1 100644 --- a/src/embed_tests/InitializeTest.cs +++ b/src/embed_tests/InitializeTest.cs @@ -9,6 +9,42 @@ namespace Python.EmbeddingTest { public class InitializeTest { + [Test] + public static void LoadSpecificArgs() + { + var args = new[] { "test1", "test2" }; + PythonEngine.Initialize(args); + try + { + using (var argv = new PyList(Runtime.Runtime.PySys_GetObject("argv"))) + { + Assert.That(argv[0].ToString() == args[0]); + Assert.That(argv[1].ToString() == args[1]); + } + } + finally + { + PythonEngine.Shutdown(); + } + } + + [Test] + public static void LoadDefaultArgs() + { + PythonEngine.Initialize(); + try + { + using (var argv = new PyList(Runtime.Runtime.PySys_GetObject("argv"))) + { + Assert.That(argv.Length() != 0); + } + } + finally + { + PythonEngine.Shutdown(); + } + } + [Test] public static void Test() { diff --git a/src/runtime/pythonengine.cs b/src/runtime/pythonengine.cs index 93df4c778..06510d955 100644 --- a/src/runtime/pythonengine.cs +++ b/src/runtime/pythonengine.cs @@ -2,6 +2,8 @@ using System.IO; using System.Threading; using System.Reflection; +using System.Collections.Generic; +using System.Linq; namespace Python.Runtime { @@ -102,6 +104,11 @@ public static int RunSimpleString(string code) #endregion + public static void Initialize() + { + Initialize(Enumerable.Empty()); + } + /// /// Initialize Method /// @@ -112,7 +119,7 @@ public static int RunSimpleString(string code) /// first call. It is *not* necessary to hold the Python global /// interpreter lock (GIL) to call this method. /// - public static void Initialize() + public static void Initialize(IEnumerable args) { if (!initialized) { @@ -126,6 +133,9 @@ public static void Initialize() initialized = true; Exceptions.Clear(); + Py.SetArgv(args); + Py.Throw(); + // 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). @@ -552,9 +562,12 @@ public static void SetArgv(IEnumerable argv) internal static void Throw() { - if (Runtime.PyErr_Occurred() != 0) + using (GIL()) { - throw new PythonException(); + if (Runtime.PyErr_Occurred() != 0) + { + throw new PythonException(); + } } } } From 4f67c5da5d592d11fb90b287c5e4b70ab68e30e8 Mon Sep 17 00:00:00 2001 From: Benedikt Reinartz Date: Tue, 31 Jan 2017 13:47:17 +0100 Subject: [PATCH 4/5] Allow the engine to be initialized as a Disposable. --- src/embed_tests/InitializeTest.cs | 30 ++++++++---------------------- src/runtime/pythonengine.cs | 30 ++++++++++++++++++++++++++++-- 2 files changed, 36 insertions(+), 24 deletions(-) diff --git a/src/embed_tests/InitializeTest.cs b/src/embed_tests/InitializeTest.cs index 212fbcff1..6c6f0f1f1 100644 --- a/src/embed_tests/InitializeTest.cs +++ b/src/embed_tests/InitializeTest.cs @@ -13,40 +13,26 @@ public class InitializeTest public static void LoadSpecificArgs() { var args = new[] { "test1", "test2" }; - PythonEngine.Initialize(args); - try + using (new PythonEngine(args)) + using (var argv = new PyList(Runtime.Runtime.PySys_GetObject("argv"))) { - using (var argv = new PyList(Runtime.Runtime.PySys_GetObject("argv"))) - { - Assert.That(argv[0].ToString() == args[0]); - Assert.That(argv[1].ToString() == args[1]); - } - } - finally - { - PythonEngine.Shutdown(); + Assert.That(argv[0].ToString() == args[0]); + Assert.That(argv[1].ToString() == args[1]); } } [Test] public static void LoadDefaultArgs() { - PythonEngine.Initialize(); - try - { - using (var argv = new PyList(Runtime.Runtime.PySys_GetObject("argv"))) - { - Assert.That(argv.Length() != 0); - } - } - finally + using (new PythonEngine()) + using (var argv = new PyList(Runtime.Runtime.PySys_GetObject("argv"))) { - PythonEngine.Shutdown(); + Assert.That(argv.Length() != 0); } } [Test] - public static void Test() + public static void StartAndStopTwice() { PythonEngine.Initialize(); PythonEngine.Shutdown(); diff --git a/src/runtime/pythonengine.cs b/src/runtime/pythonengine.cs index 06510d955..0449d3c31 100644 --- a/src/runtime/pythonengine.cs +++ b/src/runtime/pythonengine.cs @@ -10,11 +10,31 @@ namespace Python.Runtime /// /// This class provides the public interface of the Python runtime. /// - public class PythonEngine + public class PythonEngine : IDisposable { private static DelegateManager delegateManager; private static bool initialized; + public PythonEngine() + { + Initialize(); + } + + public PythonEngine(params string[] args) + { + Initialize(args); + } + + public PythonEngine(IEnumerable args) + { + Initialize(args); + } + + public void Dispose() + { + Shutdown(); + } + #region Properties public static bool IsInitialized @@ -197,7 +217,8 @@ public static void Initialize(IEnumerable args) // when it is imported by the CLR extension module. //==================================================================== #if PYTHON3 - public static IntPtr InitExt() { + public static IntPtr InitExt() + { #elif PYTHON2 public static void InitExt() { @@ -551,6 +572,11 @@ public static void SetArgv() ); } + public static void SetArgv(params string[] argv) + { + SetArgv(argv as IEnumerable); + } + public static void SetArgv(IEnumerable argv) { using (GIL()) From 3785c40a10d4754803374112212fc87a8da7e31d Mon Sep 17 00:00:00 2001 From: Benedikt Reinartz Date: Tue, 31 Jan 2017 14:32:09 +0100 Subject: [PATCH 5/5] Use Py.Throw in a few more places. In particular in Py.Import. --- src/runtime/pythonengine.cs | 27 ++++++--------------------- 1 file changed, 6 insertions(+), 21 deletions(-) diff --git a/src/runtime/pythonengine.cs b/src/runtime/pythonengine.cs index 0449d3c31..754aaf2cd 100644 --- a/src/runtime/pythonengine.cs +++ b/src/runtime/pythonengine.cs @@ -154,7 +154,6 @@ public static void Initialize(IEnumerable args) Exceptions.Clear(); Py.SetArgv(args); - Py.Throw(); // 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 @@ -382,10 +381,7 @@ public static void EndAllowThreads(IntPtr ts) public static PyObject ImportModule(string name) { IntPtr op = Runtime.PyImport_ImportModule(name); - if (op == IntPtr.Zero) - { - return null; - } + Py.Throw(); return new PyObject(op); } @@ -401,10 +397,7 @@ public static PyObject ImportModule(string name) public static PyObject ReloadModule(PyObject module) { IntPtr op = Runtime.PyImport_ReloadModule(module.Handle); - if (op == IntPtr.Zero) - { - throw new PythonException(); - } + Py.Throw(); return new PyObject(op); } @@ -420,15 +413,9 @@ public static PyObject ReloadModule(PyObject module) public static PyObject ModuleFromString(string name, string code) { IntPtr c = Runtime.Py_CompileString(code, "none", (IntPtr)257); - if (c == IntPtr.Zero) - { - throw new PythonException(); - } + Py.Throw(); IntPtr m = Runtime.PyImport_ExecCodeModule(name, c); - if (m == IntPtr.Zero) - { - throw new PythonException(); - } + Py.Throw(); return new PyObject(m); } @@ -476,10 +463,7 @@ public static PyObject RunString( code, flag, globals.Value, locals.Value ); - if (Runtime.PyErr_Occurred() != 0) - { - throw new PythonException(); - } + Py.Throw(); return new PyObject(result); } @@ -583,6 +567,7 @@ public static void SetArgv(IEnumerable argv) { var arr = argv.ToArray(); Runtime.PySys_SetArgvEx(arr.Length, arr, 0); + Py.Throw(); } }