From 600b5fa2456953a675e8a62ae9f230e51b377737 Mon Sep 17 00:00:00 2001 From: Benedikt Reinartz Date: Mon, 7 Aug 2023 15:08:56 +0200 Subject: [PATCH 1/2] Delay actually loading the runtime to fix property setting --- clr_loader/hostfxr.py | 20 +++++++++----------- tests/test_common.py | 14 +++++++++++++- 2 files changed, 22 insertions(+), 12 deletions(-) diff --git a/clr_loader/hostfxr.py b/clr_loader/hostfxr.py index 522fec5..225b4c7 100644 --- a/clr_loader/hostfxr.py +++ b/clr_loader/hostfxr.py @@ -20,9 +20,8 @@ def __init__(self, runtime_config: Path, dotnet_root: Path, **params: str): self._dotnet_root = Path(dotnet_root) self._dll = load_hostfxr(self._dotnet_root) - self._is_initialized = False self._handle = _get_handle(self._dll, self._dotnet_root, runtime_config) - self._load_func = _get_load_func(self._dll, self._handle) + self._load_func = None for key, value in params.items(): self[key] = value @@ -36,7 +35,7 @@ def dotnet_root(self) -> Path: @property def is_initialized(self) -> bool: - return self._is_initialized + return self._load_func is not None @property def is_shutdown(self) -> bool: @@ -81,10 +80,15 @@ def __iter__(self) -> Generator[Tuple[str, str], None, None]: for i in range(size_ptr[0]): yield (decode(keys_ptr[i]), decode(values_ptr[i])) + def _get_load_func(self): + if self._load_func is None: + self._load_func = _get_load_func(self._dll, self._handle) + + return self._load_func + def _get_callable(self, assembly_path: StrOrPath, typename: str, function: str): # TODO: Maybe use coreclr_get_delegate as well, supported with newer API # versions of hostfxr - self._is_initialized = True # Append assembly name to typename assembly_path = Path(assembly_path) @@ -92,7 +96,7 @@ def _get_callable(self, assembly_path: StrOrPath, typename: str, function: str): typename = f"{typename}, {assembly_name}" delegate_ptr = ffi.new("void**") - res = self._load_func( + res = self._get_load_func()( encode(str(assembly_path)), encode(typename), encode(function), @@ -103,12 +107,6 @@ def _get_callable(self, assembly_path: StrOrPath, typename: str, function: str): check_result(res) return ffi.cast("component_entry_point_fn", delegate_ptr[0]) - def _check_initialized(self) -> None: - if self._handle is None: - raise RuntimeError("Runtime is shut down") - elif not self._is_initialized: - raise RuntimeError("Runtime is not initialized") - def shutdown(self) -> None: if self._handle is not None: self._dll.hostfxr_close(self._handle) diff --git a/tests/test_common.py b/tests/test_common.py index 250c290..bcc29e0 100644 --- a/tests/test_common.py +++ b/tests/test_common.py @@ -18,7 +18,7 @@ def example_netcore(tmpdir_factory): def build_example(tmpdir_factory, framework): out = Path(tmpdir_factory.mktemp(f"example-{framework}")) - proj_path = Path(__file__).parent.parent / "example" + proj_path = Path(__file__).parent.parent / "example" / "example.csproj" check_call(["dotnet", "build", str(proj_path), "-o", str(out), "-f", framework]) @@ -75,6 +75,18 @@ def test_coreclr(example_netcore: Path): run_tests(asm) +def test_coreclr_properties(example_netcore: Path): + from clr_loader import get_coreclr + + coreclr = get_coreclr( + runtime_config=example_netcore / "example.runtimeconfig.json", + properties=dict(APP_CONTEXT_BASE_DIRECTORY=str(example_netcore)), + ) + asm = coreclr.get_assembly(example_netcore / "example.dll") + + run_tests(asm) + + def test_coreclr_autogenerated_runtimeconfig(example_netstandard: Path): from multiprocessing import get_context From d0190949761f6b3fc9043ff1bb97bc18fecba772 Mon Sep 17 00:00:00 2001 From: Benedikt Reinartz Date: Mon, 7 Aug 2023 15:17:37 +0200 Subject: [PATCH 2/2] Launch test for properties in separate process --- tests/test_common.py | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/tests/test_common.py b/tests/test_common.py index bcc29e0..a33dec0 100644 --- a/tests/test_common.py +++ b/tests/test_common.py @@ -76,15 +76,16 @@ def test_coreclr(example_netcore: Path): def test_coreclr_properties(example_netcore: Path): - from clr_loader import get_coreclr + from multiprocessing import get_context - coreclr = get_coreclr( - runtime_config=example_netcore / "example.runtimeconfig.json", - properties=dict(APP_CONTEXT_BASE_DIRECTORY=str(example_netcore)), + p = get_context("spawn").Process( + target=_do_test_coreclr_autogenerated_runtimeconfig, + args=(example_netstandard,), + kwargs=dict(properties=dict(APP_CONTEXT_BASE_DIRECTORY=str(example_netcore))), ) - asm = coreclr.get_assembly(example_netcore / "example.dll") - - run_tests(asm) + p.start() + p.join() + p.close() def test_coreclr_autogenerated_runtimeconfig(example_netstandard: Path): @@ -98,10 +99,12 @@ def test_coreclr_autogenerated_runtimeconfig(example_netstandard: Path): p.close() -def _do_test_coreclr_autogenerated_runtimeconfig(example_netstandard: Path): +def _do_test_coreclr_autogenerated_runtimeconfig( + example_netstandard: Path, **properties +): from clr_loader import get_coreclr - coreclr = get_coreclr() + coreclr = get_coreclr(properties=properties) asm = coreclr.get_assembly(example_netstandard / "example.dll") run_tests(asm)