Skip to content

Commit 23ec9d2

Browse files
committed
raise BadPythonDllException (internal, derived from MissingMethodException) instead of confusing TypeLoadException when PythonDLL was not configured properly
1 parent fc989d5 commit 23ec9d2

File tree

5 files changed

+60
-21
lines changed

5 files changed

+60
-21
lines changed

README.rst

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,11 @@ module:
4545
Embedding Python in .NET
4646
------------------------
4747

48-
- You must set `Runtime.PythonDLL` property or `PYTHONNET_PYDLL` environment variable
49-
starting with version 3.0, otherwise you will receive `TypeInitializationException`.
50-
Typical values are `python38.dll` (Windows), `libpython3.8.dylib` (Mac),
51-
`libpython3.8.so` (most other *nix).
48+
- You must set ``Runtime.PythonDLL`` property or ``PYTHONNET_PYDLL`` environment variable
49+
starting with version 3.0, otherwise you will receive ``BadPythonDllException``
50+
(internal, derived from ``MissingMethodException``) upon calling ``Initialize``.
51+
Typical values are ``python38.dll`` (Windows), ``libpython3.8.dylib`` (Mac),
52+
``libpython3.8.so`` (most other *nix).
5253
- All calls to python should be inside a
5354
``using (Py.GIL()) {/* Your code here */}`` block.
5455
- Import python modules using ``dynamic mod = Py.Import("mod")``, then

src/embed_tests/TestPythonEngineProperties.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ public static void GetPythonPathDefault()
8181
public static void GetProgramNameDefault()
8282
{
8383
PythonEngine.Initialize();
84-
string s = PythonEngine.PythonHome;
84+
string s = PythonEngine.ProgramName;
8585

8686
Assert.NotNull(s);
8787
PythonEngine.Shutdown();

src/runtime/platform/LibraryLoader.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ public IntPtr Load(string dllToLoad)
111111
{
112112
var res = WindowsLoader.LoadLibrary(dllToLoad);
113113
if (res == IntPtr.Zero)
114-
throw new DllNotFoundException($"Could not load {dllToLoad}", new Win32Exception());
114+
throw new DllNotFoundException($"Could not load {dllToLoad}.", new Win32Exception());
115115
return res;
116116
}
117117

@@ -128,7 +128,7 @@ public IntPtr GetFunction(IntPtr hModule, string procedureName)
128128

129129
var res = WindowsLoader.GetProcAddress(hModule, procedureName);
130130
if (res == IntPtr.Zero)
131-
throw new MissingMethodException($"Failed to load symbol {procedureName}", new Win32Exception());
131+
throw new MissingMethodException($"Failed to load symbol {procedureName}.", new Win32Exception());
132132
return res;
133133
}
134134

src/runtime/pythonengine.cs

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -86,13 +86,15 @@ public static string ProgramName
8686
{
8787
get
8888
{
89-
IntPtr p = Runtime.Py_GetProgramName();
89+
IntPtr p = Runtime.TryUsingDll(() => Runtime.Py_GetProgramName());
9090
return UcsMarshaler.PtrToPy3UnicodePy2String(p) ?? "";
9191
}
9292
set
9393
{
9494
Marshal.FreeHGlobal(_programName);
95-
_programName = UcsMarshaler.Py3UnicodePy2StringtoPtr(value);
95+
_programName = Runtime.TryUsingDll(
96+
() => UcsMarshaler.Py3UnicodePy2StringtoPtr(value)
97+
);
9698
Runtime.Py_SetProgramName(_programName);
9799
}
98100
}
@@ -101,14 +103,16 @@ public static string PythonHome
101103
{
102104
get
103105
{
104-
IntPtr p = Runtime.Py_GetPythonHome();
106+
IntPtr p = Runtime.TryUsingDll(() => Runtime.Py_GetPythonHome());
105107
return UcsMarshaler.PtrToPy3UnicodePy2String(p) ?? "";
106108
}
107109
set
108110
{
109111
// this value is null in the beginning
110112
Marshal.FreeHGlobal(_pythonHome);
111-
_pythonHome = UcsMarshaler.Py3UnicodePy2StringtoPtr(value);
113+
_pythonHome = Runtime.TryUsingDll(
114+
() => UcsMarshaler.Py3UnicodePy2StringtoPtr(value)
115+
);
112116
Runtime.Py_SetPythonHome(_pythonHome);
113117
}
114118
}
@@ -117,13 +121,15 @@ public static string PythonPath
117121
{
118122
get
119123
{
120-
IntPtr p = Runtime.Py_GetPath();
124+
IntPtr p = Runtime.TryUsingDll(() => Runtime.Py_GetPath());
121125
return UcsMarshaler.PtrToPy3UnicodePy2String(p) ?? "";
122126
}
123127
set
124128
{
125129
Marshal.FreeHGlobal(_pythonPath);
126-
_pythonPath = UcsMarshaler.Py3UnicodePy2StringtoPtr(value);
130+
_pythonPath = Runtime.TryUsingDll(
131+
() => UcsMarshaler.Py3UnicodePy2StringtoPtr(value)
132+
);
127133
Runtime.Py_SetPath(_pythonPath);
128134
}
129135
}

src/runtime/runtime.cs

Lines changed: 40 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,6 @@ internal static Version PyVersion
9393
}
9494
}
9595

96-
9796
/// <summary>
9897
/// Initialize the runtime...
9998
/// </summary>
@@ -113,7 +112,10 @@ internal static void Initialize(bool initSigs = false, ShutdownMode mode = Shutd
113112
}
114113
ShutdownMode = mode;
115114

116-
if (Py_IsInitialized() == 0)
115+
bool interpreterAlreadyInitialized = TryUsingDll(
116+
() => Py_IsInitialized() != 0
117+
);
118+
if (!interpreterAlreadyInitialized)
117119
{
118120
Py_InitializeEx(initSigs ? 1 : 0);
119121
if (PyEval_ThreadsInitialized() == 0)
@@ -787,6 +789,34 @@ internal static unsafe long Refcount(IntPtr op)
787789
return *p;
788790
}
789791

792+
/// <summary>
793+
/// Call specified function, and handle PythonDLL-related failures.
794+
/// </summary>
795+
internal static T TryUsingDll<T>(Func<T> op)
796+
{
797+
try
798+
{
799+
return op();
800+
}
801+
catch (TypeInitializationException loadFailure)
802+
{
803+
var delegatesLoadFailure = loadFailure;
804+
// failure to load Delegates type might have been the cause
805+
// of failure to load some higher-level type
806+
while (delegatesLoadFailure.InnerException is TypeInitializationException nested)
807+
{
808+
delegatesLoadFailure = nested;
809+
}
810+
811+
if (delegatesLoadFailure.InnerException is BadPythonDllException badDll)
812+
{
813+
throw badDll;
814+
}
815+
816+
throw;
817+
}
818+
}
819+
790820
/// <summary>
791821
/// Export of Macro Py_XIncRef. Use XIncref instead.
792822
/// Limit this function usage for Testing and Py_Debug builds
@@ -2270,10 +2300,6 @@ internal static void SetNoSiteFlag()
22702300
if (_PythonDll != "__Internal")
22712301
{
22722302
dllLocal = loader.Load(_PythonDll);
2273-
if (dllLocal == IntPtr.Zero)
2274-
{
2275-
throw new Exception($"Cannot load {_PythonDll}");
2276-
}
22772303
}
22782304
try
22792305
{
@@ -2617,8 +2643,8 @@ static Delegates()
26172643
}
26182644
catch (MissingMethodException e) when (libraryHandle == IntPtr.Zero)
26192645
{
2620-
throw new MissingMethodException(
2621-
"Did you forget to set Runtime.PythonDLL?" +
2646+
throw new BadPythonDllException(
2647+
"Runtime.PythonDLL was not set or does not point to a supported Python runtime DLL." +
26222648
" See https://github.com/pythonnet/pythonnet#embedding-python-in-net",
26232649
e);
26242650
}
@@ -2889,6 +2915,12 @@ static Delegates()
28892915
}
28902916
}
28912917

2918+
internal class BadPythonDllException : MissingMethodException
2919+
{
2920+
public BadPythonDllException(string message, Exception innerException)
2921+
: base(message, innerException) { }
2922+
}
2923+
28922924

28932925
public enum ShutdownMode
28942926
{

0 commit comments

Comments
 (0)