You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
pythonnet: 3.0.3, 3.0.5 (and other 3.0.x versions)
clr-loader: 0.2.6 (also tested with 0.2.7.post0)
.NET Runtimes Tested:
.NET 8 (e.g., 8.0.16) - Fails
.NET 9 (e.g., 9.0.5) - Works
OS: Windows 10/11 (primarily tested on Windows)
Problem:
When using pythonnet 3.0.x with Python 3.12 to interact with .NET 8 assemblies via CoreCLR, pythonnet successfully loads the .NET 8 runtime and the target assemblies into the AppDomain. However, it fails to properly expose the .NET types to the Python environment. This results in AttributeError when trying to access types directly from an imported namespace, or ImportError when a Python module within a package attempts to import these .NET types.
The same operations succeed when the .NET assembly is compiled for .NET 9 and the .NET 9 runtime is used with the same pythonnet version.
Observed Behavior (.NET 8):
.NET 8 CoreCLR is loaded (confirmed via System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription and assemblies like System.Private.CoreLib, Version=8.0.0.0 in AppDomain).
Target .NET 8 assembly is loaded into System.AppDomain.CurrentDomain.GetAssemblies().
clr.AddReference("Path/To/Assembly.dll") completes without error.
Importing the assembly's namespace (e.g., import MyNamespace) succeeds.
Attempting to access a type (e.g., MyNamespace.MyClass) results in AttributeError: module 'MyNamespace' has no attribute 'MyClass'.
Alternatively, if a package's Python module tries from MyNamespace import MyClass (after clr.AddReference in __init__.py), it fails with ImportError: cannot import name 'MyClass' from 'MyNamespace'.
The clr module object consistently lacks the References attribute (i.e., hasattr(clr, "References") is False) after .NET CoreCLR initialization.
clr.FindAssembly("AssemblyName") returns a string path to the DLL, not an Assembly object.
Expected Behavior:
Types from .NET 8 assemblies should be accessible in Python after clr.AddReference() and importing the namespace.
No AttributeError or ImportError should occur when accessing correctly referenced .NET types.
The behavior of clr.References and clr.FindAssembly() with .NET CoreCLR should ideally be consistent with .NET Framework or clearly documented if different.
Steps to Reproduce (Minimal Case):
Create a simple .NET 8 Class Library (MinimalNet8Lib):
namespaceMinimalNet8Lib;publicclassGreeter{publicstaticstringSayHello(stringname){return$"Hello, {name} from .NET 8!";}publicstringInstanceHello(stringname){return$"Instance hello to {name} from .NET 8!";}}
Build the library: dotnet build -c Release
Create a Python Test Script (test_minimal_net8.py):
importosimportsysfrompathlibimportPathprint(f"Python version: {sys.version}")
print(f"Python executable: {sys.executable}")
# --- Crucial: Set PYTHONNET_RUNTIME to coreclr BEFORE pythonnet import ---os.environ["PYTHONNET_RUNTIME"] ="coreclr"try:
print("Attempting to import pythonnet and clr_loader...")
importpythonnetimportclr_loaderprint(f"pythonnet version: {pythonnet.__version__}")
print(f"clr_loader version: {clr_loader.__version__}")
exceptExceptionase:
print(f"Error importing pythonnet/clr_loader: {e}")
sys.exit(1)
# --- Setup Paths ---# Adjust this path to your MinimalNet8Lib.dll# Assumes the script is run from a directory where MinimalNet8Lib/bin/Release/net8.0/MinimalNet8Lib.dll existsscript_dir=Path(__file__).parent.resolve()
dll_path=script_dir/"MinimalNet8Lib"/"bin"/"Release"/"net8.0"/"MinimalNet8Lib.dll"runtime_config_path=script_dir/"MinimalNet8Lib"/"bin"/"Release"/"net8.0"/"MinimalNet8Lib.runtimeconfig.json"# Optional: Specify DOTNET_ROOT if not discoverable# dotnet_root = Path(os.environ.get("DOTNET_ROOT", "C:/Program Files/dotnet"))print(f"Target DLL: {dll_path}")
print(f"Runtime Config: {runtime_config_path}")
ifnotdll_path.exists():
print(f"ERROR: DLL not found at {dll_path}")
sys.exit(1)
ifnotruntime_config_path.exists():
print(f"ERROR: Runtime config not found at {runtime_config_path}")
sys.exit(1)
try:
print("\n--- Initializing .NET CoreCLR Runtime ---")
# Using clr_loader to get the runtimert=clr_loader.get_coreclr(runtime_config=str(runtime_config_path)) #, dotnet_root=str(dotnet_root)print(f"CoreCLR runtime object: {rt}")
print("Setting pythonnet runtime...")
pythonnet.set_runtime(rt)
print("Pythonnet runtime set.")
print("Attempting to import clr...")
importclrprint("Successfully imported clr.")
print(f"clr module: {clr}")
ifhasattr(clr, "__version__"):
print(f"clr (pythonnet) version attribute: {clr.__version__}")
exceptExceptionase:
print(f"ERROR during runtime initialization or clr import: {e}")
importtracebacktraceback.print_exc()
sys.exit(1)
print("\n--- Runtime and Assembly Diagnostics ---")
try:
fromSystemimportEnvironment, AppDomainfromSystem.Runtime.InteropServicesimportRuntimeInformationprint(f"PythonEngine.Version: {pythonnet.get_version()}")
print(f"PythonEngine.IsInitialized: {pythonnet.is_initialized()}")
print(f"System.Environment.Version: {Environment.Version}")
print(f"RuntimeInformation.FrameworkDescription: {RuntimeInformation.FrameworkDescription}")
print(f"Adding reference to: {str(dll_path)}")
clr.AddReference(str(dll_path))
print("clr.AddReference completed.")
print("Assemblies in Current AppDomain:")
forassemblyinAppDomain.CurrentDomain.GetAssemblies():
print(f" - {assembly.FullName}")
if"MinimalNet8Lib"inassembly.FullName:
print(f" FOUND MinimalNet8Lib: {assembly.FullName}, Location: {assembly.Location}")
print(f"clr.FindAssembly('MinimalNet8Lib'): {clr.FindAssembly('MinimalNet8Lib')}")
print(f"hasattr(clr, 'References'): {hasattr(clr, 'References')}")
ifhasattr(clr, 'References') andclr.ReferencesisnotNone:
print(f"Number of clr.References: {len(clr.References)}")
else:
print("clr.References attribute is missing or None.")
exceptExceptionase:
print(f"Error during diagnostics: {e}")
importtracebacktraceback.print_exc()
# Continue to type access attemptprint("\n--- Attempting to Access .NET Types ---")
try:
print("Attempting: import MinimalNet8Lib")
importMinimalNet8Libprint("Successfully imported MinimalNet8Lib namespace.")
print("Attempting: greeter = MinimalNet8Lib.Greeter()")
greeter=MinimalNet8Lib.Greeter() # This is where AttributeError typically occursprint(f"Successfully created Greeter instance: {greeter}")
print(f"Calling instance method: {greeter.InstanceHello('Python')}")
print(f"Calling static method: {MinimalNet8Lib.Greeter.SayHello('Python')}")
print("\nSUCCESS: .NET 8 types accessed successfully!")
exceptAttributeErrorasae:
print(f"FAILURE: AttributeError: {ae}")
print("This indicates types from the .NET 8 assembly were not exposed correctly.")
importtracebacktraceback.print_exc()
exceptExceptionase:
print(f"FAILURE: An unexpected error occurred: {e}")
importtracebacktraceback.print_exc()
print("\nScript finished.")
Run the Python script: python test_minimal_net8.py
This will show the AttributeError.
Test with .NET 9 (Works):
Change TargetFramework in MinimalNet8Lib.csproj to net9.0.
Rebuild: dotnet build -c Release.
Update dll_path and runtime_config_path in test_minimal_net8.py to point to the net9.0 outputs.
Ensure .NET 9 SDK/Runtime is installed and discoverable.
Run test_minimal_net8.py. The script should complete successfully, accessing the .NET types.
Additional Notes:
The issue has been observed with pythonnet versions 3.0.3 and 3.0.5.
The problem is reproducible with both a minimal class library and more complex, real-world libraries (e.g., Tableau.Migration.dll v5.0.1, which targets only net8.0 and uses pythonnet.load("coreclr") in its __init__.py, leading to an ImportError when its Python submodules try to import the .NET types).
Setting PYTHONNET_RUNTIME=coreclr and using clr_loader.get_coreclr() followed by pythonnet.set_runtime() before import clr is a reliable way to initialize .NET CoreCLR. Simpler methods like just pythonnet.load("coreclr") or import clr can sometimes lead to .NET Framework being loaded if not carefully managed, but the type exposure issue persists even with explicit and correct .NET 8 CoreCLR initialization.
We believe this is a significant issue for users attempting to leverage .NET 8 libraries with Python via pythonnet. The fact that it works seamlessly with .NET 9 suggests a specific interaction problem with .NET 8.
The text was updated successfully, but these errors were encountered:
Environment:
Problem:
When using
pythonnet
3.0.x with Python 3.12 to interact with .NET 8 assemblies via CoreCLR,pythonnet
successfully loads the .NET 8 runtime and the target assemblies into the AppDomain. However, it fails to properly expose the .NET types to the Python environment. This results inAttributeError
when trying to access types directly from an imported namespace, orImportError
when a Python module within a package attempts to import these .NET types.The same operations succeed when the .NET assembly is compiled for .NET 9 and the .NET 9 runtime is used with the same
pythonnet
version.Observed Behavior (.NET 8):
System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription
and assemblies likeSystem.Private.CoreLib, Version=8.0.0.0
in AppDomain).System.AppDomain.CurrentDomain.GetAssemblies()
.clr.AddReference("Path/To/Assembly.dll")
completes without error.import MyNamespace
) succeeds.MyNamespace.MyClass
) results inAttributeError: module 'MyNamespace' has no attribute 'MyClass'
.from MyNamespace import MyClass
(afterclr.AddReference
in__init__.py
), it fails withImportError: cannot import name 'MyClass' from 'MyNamespace'
.clr
module object consistently lacks theReferences
attribute (i.e.,hasattr(clr, "References")
isFalse
) after .NET CoreCLR initialization.clr.FindAssembly("AssemblyName")
returns a string path to the DLL, not anAssembly
object.Expected Behavior:
clr.AddReference()
and importing the namespace.AttributeError
orImportError
should occur when accessing correctly referenced .NET types.clr.References
andclr.FindAssembly()
with .NET CoreCLR should ideally be consistent with .NET Framework or clearly documented if different.Steps to Reproduce (Minimal Case):
Create a simple .NET 8 Class Library (
MinimalNet8Lib
):MinimalNet8Lib.csproj
:Class1.cs
(or e.g.,Greeter.cs
):dotnet build -c Release
Create a Python Test Script (
test_minimal_net8.py
):Run the Python script:
python test_minimal_net8.py
This will show the
AttributeError
.Test with .NET 9 (Works):
TargetFramework
inMinimalNet8Lib.csproj
tonet9.0
.dotnet build -c Release
.dll_path
andruntime_config_path
intest_minimal_net8.py
to point to thenet9.0
outputs.test_minimal_net8.py
. The script should complete successfully, accessing the .NET types.Additional Notes:
pythonnet
versions 3.0.3 and 3.0.5.Tableau.Migration.dll
v5.0.1, which targets onlynet8.0
and usespythonnet.load("coreclr")
in its__init__.py
, leading to anImportError
when its Python submodules try to import the .NET types).PYTHONNET_RUNTIME=coreclr
and usingclr_loader.get_coreclr()
followed bypythonnet.set_runtime()
beforeimport clr
is a reliable way to initialize .NET CoreCLR. Simpler methods like justpythonnet.load("coreclr")
orimport clr
can sometimes lead to .NET Framework being loaded if not carefully managed, but the type exposure issue persists even with explicit and correct .NET 8 CoreCLR initialization.We believe this is a significant issue for users attempting to leverage .NET 8 libraries with Python via
pythonnet
. The fact that it works seamlessly with .NET 9 suggests a specific interaction problem with .NET 8.The text was updated successfully, but these errors were encountered: