Skip to content

fix: load .NET assemblies when running on .NET 9+ - #174

Merged
arjenkroezen-msft merged 1 commit into
mainfrom
fix/dotnet9-assembly-loading
Sep 24, 2026
Merged

arjenkroezen-msft merged 1 commit into
mainfrom
fix/dotnet9-assembly-loading

Conversation

@arjenkroezen-msft

Copy link
Copy Markdown
Collaborator

Summary

Fixes the ci / build failure seen on every CI/CD PR run since January 2026 (including #173, #167, #168 and #171).

Root cause

GitHub-hosted runners now have .NET 8, 9 and 10 installed.

  • With pythonnet 3.0.x / clr_loader 0.2.x (the versions in poetry.lock, and what Python 3.9 installs), runtimes are sorted by version string, so "9.0.x" beats "8.0.x" and "10.0.x". The evaluator therefore runs on .NET 9.
  • On .NET 9+, clr.AddReference("/abs/path/Foo.dll") in pythonnet 3.0.x parses the path as an assembly name and throws FileNotFoundException before its full-path fallback runs.
  • load_dotnet_assemblies() swallows that exception, so the tests fail later with ModuleNotFoundError: No module named 'Microsoft.Azure'.

This affects users too, not just CI: anyone on Python 3.9 (or with pythonnet 3.0.x pinned) who has .NET 9 installed next to .NET 8 hits the same error.

Changes

  • _pythonnet/__init__.py: pass the assembly path without the .dll suffix. pythonnet then probes and loads <path>.dll directly, which works on .NET 8, 9 and 10 with pythonnet 3.0.x and 3.1.x.
  • ci.yml: pin the e2e matrix to 3.13 instead of 3.x. 3.x now resolves to Python 3.14, which requires-python = ">=3.9,<3.14" rejects, so the e2e jobs would fail as soon as build is green.

Validation (local, with .NET 8.0 + 9.0 + 10.0 runtimes installed like the runners)

Environment Runtime picked Before After
Py 3.9, pythonnet 3.0.5 (Poetry lock), full pre-commit run --all-files .NET 9 ❌ No module named 'Microsoft.Azure' ✅ all hooks, 264 tests
Py 3.9, pythonnet 3.0.5, .NET 8 only .NET 8 ✅ ✅ 264 tests
Py 3.13, pythonnet 3.1.0 .NET 10 / .NET 8 ✅ ✅ 264 tests
e2e: built whl + sdist installed on Py 3.9 and 3.13, pytest examples .NET 9 / .NET 10 — ✅ 43 tests each

Windows was not tested locally.

After this merges, #173 (action pinning) should go green on a rebase / re-run. It touches different lines of ci.yml, so there's no conflict.

GitHub-hosted runners now ship .NET 8, 9 and 10. clr_loader 0.2.x (used
with pythonnet 3.0.x, e.g. on Python 3.9) picks the runtime by string
sort, so it selects .NET 9. On .NET 9+, pythonnet 3.0.x's
clr.AddReference("<abs path>.dll") parses the path as an assembly name
and throws FileNotFoundException before its full-path fallback runs. The
error was swallowed, so imports failed with
"No module named 'Microsoft.Azure'".

Pass the path without the ".dll" suffix so pythonnet probes and loads
"<path>.dll" directly, which works on .NET 8, 9 and 10 with pythonnet
3.0.x and 3.1.x.

Also pin the e2e matrix to Python 3.13 instead of '3.x', which now
resolves to 3.14 and is outside requires-python (<3.14).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@arjenkroezen-msft
arjenkroezen-msft force-pushed the fix/dotnet9-assembly-loading branch from 2835298 to 87bf48c Compare September 24, 2026 08:16

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

@arjenkroezen-msft
arjenkroezen-msft merged commit 784d25f into main Sep 24, 2026
27 of 28 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants