Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ jobs:
run: ruff format --check
- name: Check lints
run: ruff check
- name: Check types
run: uv run --group dev ty check clr_loader

test:
runs-on: ${{ matrix.os.instance }}
Expand Down
6 changes: 2 additions & 4 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,8 @@ target/
# pyenv
.python-version

# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# ty
.ty_cache/

# Pyre type checker
.pyre/
Expand Down
6 changes: 4 additions & 2 deletions Justfile
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,15 @@ test:
lint:
uv run ruff check

typecheck:
uv run ty check clr_loader

format:
uv run ruff format

check: lint test
check: lint typecheck test
uv run ruff format --check

docs output="doc/html/":
uv run --group doc sphinx-build doc/ doc/html/


3 changes: 2 additions & 1 deletion clr_loader/ffi/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,8 @@ def _path_to_version(path: Path) -> tuple[int, int, int]:
# Handle pre-release versions like "10.0.0-rc.1" by taking only the version part
version_part = name.split("-")[0]
res = list(map(int, version_part.split(".")))
return tuple(res + [0, 0, 0])[:3]
v = (res + [0, 0, 0])[:3]
return (v[0], v[1], v[2])
except Exception: # noqa
return (0, 0, 0)

Expand Down
10 changes: 7 additions & 3 deletions clr_loader/hostfxr.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import sys
from collections.abc import Generator
from pathlib import Path
from typing import Any

from .ffi import ffi, load_hostfxr
from .types import Runtime, RuntimeInfo, StrOrPath
Expand Down Expand Up @@ -144,7 +145,7 @@ def info(self):
def _get_handle_for_runtime_config(
dll, dotnet_root: StrOrPath, runtime_config: StrOrPath
):
params = ffi.new("hostfxr_initialize_parameters*")
params: Any = ffi.new("hostfxr_initialize_parameters*")
params.size = ffi.sizeof("hostfxr_initialize_parameters")
# params.host_path = ffi.new("char_t[]", encode(sys.executable))
params.host_path = ffi.NULL
Expand All @@ -164,7 +165,7 @@ def _get_handle_for_runtime_config(
def _get_handle_for_dotnet_command_line(
dll, dotnet_root: StrOrPath, entry_dll: StrOrPath
):
params = ffi.new("hostfxr_initialize_parameters*")
params: Any = ffi.new("hostfxr_initialize_parameters*")
params.size = ffi.sizeof("hostfxr_initialize_parameters")
params.host_path = ffi.NULL
dotnet_root_p = ffi.new("char_t[]", encode(str(Path(dotnet_root))))
Expand Down Expand Up @@ -209,4 +210,7 @@ def encode(string: str):
return string.encode("utf8")

def decode(char_ptr) -> str:
return ffi.string(char_ptr).decode("utf8")
res = ffi.string(char_ptr)
if isinstance(res, bytes):
return res.decode("utf8")
return str(res)
7 changes: 6 additions & 1 deletion clr_loader/mono.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,12 @@ def initialize(

build = _MONO.mono_get_runtime_build_info()
_check_result(build, "Failed to get Mono version")
ver_str = ffi.string(build).decode("utf8") # e.g. '6.12.0.122 (tarball)'
build_bytes = ffi.string(build)
ver_str = (
build_bytes.decode("utf8")
if isinstance(build_bytes, bytes)
else str(build_bytes)
) # e.g. '6.12.0.122 (tarball)'

ver = re.match(r"^(?P<major>\d+)\.(?P<minor>\d+)\.[\d.]+", ver_str)
if ver is not None:
Expand Down
5 changes: 3 additions & 2 deletions clr_loader/netfx.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,16 @@

class NetFx(Runtime):
def __init__(self, domain: str | None = None, config_file: Path | None = None):
self._domain: str | None = None
self._domain: Any = None

initialize()
config_file_s: Any
if config_file is not None:
config_file_s = str(config_file).encode("utf8")
else:
config_file_s = ffi.NULL

domain_s = domain.encode("utf8") if domain else ffi.NULL
domain_s: Any = domain.encode("utf8") if domain else ffi.NULL

self._domain_name: str | None = domain
self._config_file: Path | None = config_file
Expand Down
28 changes: 14 additions & 14 deletions clr_loader/util/find.py
Original file line number Diff line number Diff line change
Expand Up @@ -161,31 +161,31 @@ def _find_mono_unix(
if candidate.exists():
return candidate

if res := find_library(unix_name):
return Path(res)
if lib_path := find_library(unix_name):
return Path(lib_path)

if macos:
res = (
candidate = (
Path("/Library/Frameworks/Mono.framework/Versions/Current/lib")
/ lib_filename
)
if res.exists():
return res
if candidate.exists():
return candidate

# Use HOMEBREW_PREFIX environment variable if available
if homebrew_prefix := os.environ.get("HOMEBREW_PREFIX"):
res = Path(homebrew_prefix) / "opt/mono/lib" / lib_filename
if res.exists():
return res
candidate = Path(homebrew_prefix) / "opt/mono/lib" / lib_filename
if candidate.exists():
return candidate

# Check for native Apple Silicon (arm64)
if platform.machine() == "arm64":
res = Path("/opt/homebrew/opt/mono/lib") / lib_filename
if res.exists():
return res
candidate = Path("/opt/homebrew/opt/mono/lib") / lib_filename
if candidate.exists():
return candidate
else:
res = Path("/usr/local/opt/mono/lib") / lib_filename
if res.exists():
return res
candidate = Path("/usr/local/opt/mono/lib") / lib_filename
if candidate.exists():
return candidate

raise RuntimeError("Could not find libmono")
2 changes: 2 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,10 @@ Documentation = "https://pythonnet.github.io/clr-loader/"

[dependency-groups]
dev = [
"ty >=0.0.1",
"pytest >=9.0, <10.0",
"ruff >=0.15",
"types-cffi",
]
doc = ["furo >=2025.0", "sphinx >=9.0, <10.0"]

Expand Down
53 changes: 52 additions & 1 deletion uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading