diff --git a/README.rst b/README.rst
index 996bfab27..c0e4229d3 100644
--- a/README.rst
+++ b/README.rst
@@ -45,10 +45,11 @@ module:
Embedding Python in .NET
------------------------
-- You must set `Runtime.PythonDLL` property or `PYTHONNET_PYDLL` environment variable
- starting with version 3.0, otherwise you will receive `TypeInitializationException`.
- Typical values are `python38.dll` (Windows), `libpython3.8.dylib` (Mac),
- `libpython3.8.so` (most other *nix).
+- You must set ``Runtime.PythonDLL`` property or ``PYTHONNET_PYDLL`` environment variable
+ starting with version 3.0, otherwise you will receive ``BadPythonDllException``
+ (internal, derived from ``MissingMethodException``) upon calling ``Initialize``.
+ Typical values are ``python38.dll`` (Windows), ``libpython3.8.dylib`` (Mac),
+ ``libpython3.8.so`` (most other *nix).
- All calls to python should be inside a
``using (Py.GIL()) {/* Your code here */}`` block.
- Import python modules using ``dynamic mod = Py.Import("mod")``, then
diff --git a/src/embed_tests/TestPythonEngineProperties.cs b/src/embed_tests/TestPythonEngineProperties.cs
index 626e3c77f..ca9164a1d 100644
--- a/src/embed_tests/TestPythonEngineProperties.cs
+++ b/src/embed_tests/TestPythonEngineProperties.cs
@@ -81,7 +81,7 @@ public static void GetPythonPathDefault()
public static void GetProgramNameDefault()
{
PythonEngine.Initialize();
- string s = PythonEngine.PythonHome;
+ string s = PythonEngine.ProgramName;
Assert.NotNull(s);
PythonEngine.Shutdown();
diff --git a/src/runtime/platform/LibraryLoader.cs b/src/runtime/platform/LibraryLoader.cs
index e361f87e4..78bf48112 100644
--- a/src/runtime/platform/LibraryLoader.cs
+++ b/src/runtime/platform/LibraryLoader.cs
@@ -111,7 +111,7 @@ public IntPtr Load(string dllToLoad)
{
var res = WindowsLoader.LoadLibrary(dllToLoad);
if (res == IntPtr.Zero)
- throw new DllNotFoundException($"Could not load {dllToLoad}", new Win32Exception());
+ throw new DllNotFoundException($"Could not load {dllToLoad}.", new Win32Exception());
return res;
}
@@ -128,7 +128,7 @@ 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());
+ throw new MissingMethodException($"Failed to load symbol {procedureName}.", new Win32Exception());
return res;
}
diff --git a/src/runtime/pythonengine.cs b/src/runtime/pythonengine.cs
index d7322dcc2..ece70c485 100644
--- a/src/runtime/pythonengine.cs
+++ b/src/runtime/pythonengine.cs
@@ -86,13 +86,15 @@ public static string ProgramName
{
get
{
- IntPtr p = Runtime.Py_GetProgramName();
+ IntPtr p = Runtime.TryUsingDll(() => Runtime.Py_GetProgramName());
return UcsMarshaler.PtrToPy3UnicodePy2String(p) ?? "";
}
set
{
Marshal.FreeHGlobal(_programName);
- _programName = UcsMarshaler.Py3UnicodePy2StringtoPtr(value);
+ _programName = Runtime.TryUsingDll(
+ () => UcsMarshaler.Py3UnicodePy2StringtoPtr(value)
+ );
Runtime.Py_SetProgramName(_programName);
}
}
@@ -101,14 +103,16 @@ public static string PythonHome
{
get
{
- IntPtr p = Runtime.Py_GetPythonHome();
+ IntPtr p = Runtime.TryUsingDll(() => Runtime.Py_GetPythonHome());
return UcsMarshaler.PtrToPy3UnicodePy2String(p) ?? "";
}
set
{
// this value is null in the beginning
Marshal.FreeHGlobal(_pythonHome);
- _pythonHome = UcsMarshaler.Py3UnicodePy2StringtoPtr(value);
+ _pythonHome = Runtime.TryUsingDll(
+ () => UcsMarshaler.Py3UnicodePy2StringtoPtr(value)
+ );
Runtime.Py_SetPythonHome(_pythonHome);
}
}
@@ -117,13 +121,15 @@ public static string PythonPath
{
get
{
- IntPtr p = Runtime.Py_GetPath();
+ IntPtr p = Runtime.TryUsingDll(() => Runtime.Py_GetPath());
return UcsMarshaler.PtrToPy3UnicodePy2String(p) ?? "";
}
set
{
Marshal.FreeHGlobal(_pythonPath);
- _pythonPath = UcsMarshaler.Py3UnicodePy2StringtoPtr(value);
+ _pythonPath = Runtime.TryUsingDll(
+ () => UcsMarshaler.Py3UnicodePy2StringtoPtr(value)
+ );
Runtime.Py_SetPath(_pythonPath);
}
}
diff --git a/src/runtime/runtime.cs b/src/runtime/runtime.cs
index 318c7b794..a7420f946 100644
--- a/src/runtime/runtime.cs
+++ b/src/runtime/runtime.cs
@@ -93,7 +93,6 @@ internal static Version PyVersion
}
}
-
///
/// Initialize the runtime...
///
@@ -113,7 +112,10 @@ internal static void Initialize(bool initSigs = false, ShutdownMode mode = Shutd
}
ShutdownMode = mode;
- if (Py_IsInitialized() == 0)
+ bool interpreterAlreadyInitialized = TryUsingDll(
+ () => Py_IsInitialized() != 0
+ );
+ if (!interpreterAlreadyInitialized)
{
Py_InitializeEx(initSigs ? 1 : 0);
if (PyEval_ThreadsInitialized() == 0)
@@ -787,6 +789,34 @@ internal static unsafe long Refcount(IntPtr op)
return *p;
}
+ ///
+ /// Call specified function, and handle PythonDLL-related failures.
+ ///
+ internal static T TryUsingDll(Func op)
+ {
+ try
+ {
+ return op();
+ }
+ catch (TypeInitializationException loadFailure)
+ {
+ var delegatesLoadFailure = loadFailure;
+ // failure to load Delegates type might have been the cause
+ // of failure to load some higher-level type
+ while (delegatesLoadFailure.InnerException is TypeInitializationException nested)
+ {
+ delegatesLoadFailure = nested;
+ }
+
+ if (delegatesLoadFailure.InnerException is BadPythonDllException badDll)
+ {
+ throw badDll;
+ }
+
+ throw;
+ }
+ }
+
///
/// Export of Macro Py_XIncRef. Use XIncref instead.
/// Limit this function usage for Testing and Py_Debug builds
@@ -2270,10 +2300,6 @@ internal static void SetNoSiteFlag()
if (_PythonDll != "__Internal")
{
dllLocal = loader.Load(_PythonDll);
- if (dllLocal == IntPtr.Zero)
- {
- throw new Exception($"Cannot load {_PythonDll}");
- }
}
try
{
@@ -2617,8 +2643,8 @@ static Delegates()
}
catch (MissingMethodException e) when (libraryHandle == IntPtr.Zero)
{
- throw new MissingMethodException(
- "Did you forget to set Runtime.PythonDLL?" +
+ throw new BadPythonDllException(
+ "Runtime.PythonDLL was not set or does not point to a supported Python runtime DLL." +
" See https://github.com/pythonnet/pythonnet#embedding-python-in-net",
e);
}
@@ -2889,6 +2915,12 @@ static Delegates()
}
}
+ internal class BadPythonDllException : MissingMethodException
+ {
+ public BadPythonDllException(string message, Exception innerException)
+ : base(message, innerException) { }
+ }
+
public enum ShutdownMode
{