From 3f124699e4bdcf0b0fd5904c5b7f4cf77d601064 Mon Sep 17 00:00:00 2001 From: Benedikt Reinartz Date: Fri, 16 Sep 2022 21:21:18 +0200 Subject: [PATCH 1/3] Bump clr-loader dependency to 0.2.3 and adjust interface - Supports loading without explicitly specifying the runtime config now - Exposes information on the loaded runtime --- pyproject.toml | 2 +- pythonnet/__init__.py | 21 ++++++++++++++------- tests/conftest.py | 10 ++++++---- 3 files changed, 21 insertions(+), 12 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 5ee89d3b7..39c7c14fb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -10,7 +10,7 @@ license = {text = "MIT"} readme = "README.rst" dependencies = [ - "clr_loader>=0.1.7" + "clr_loader>=0.2.2,<0.3.0" ] classifiers = [ diff --git a/pythonnet/__init__.py b/pythonnet/__init__.py index 9876a0bec..c847c4c74 100644 --- a/pythonnet/__init__.py +++ b/pythonnet/__init__.py @@ -1,12 +1,12 @@ import sys from pathlib import Path -from typing import Dict, Optional, Union +from typing import Dict, Optional, Union, Any import clr_loader __all__ = ["set_runtime", "set_runtime_from_env", "load"] _RUNTIME: Optional[clr_loader.Runtime] = None -_LOADER_ASSEMBLY: Optional[clr_loader.wrappers.Assembly] = None +_LOADER_ASSEMBLY: Optional[clr_loader.Assembly] = None _LOADED: bool = False @@ -27,6 +27,13 @@ def set_runtime(runtime: Union[clr_loader.Runtime, str], **params: str) -> None: _RUNTIME = runtime +def get_runtime_info() -> Optional[clr_loader.RuntimeInfo]: + if _RUNTIME is None: + return None + else: + return _RUNTIME.info() + + def _get_params_from_env(prefix: str) -> Dict[str, str]: from os import environ @@ -43,7 +50,7 @@ def _get_params_from_env(prefix: str) -> Dict[str, str]: def _create_runtime_from_spec( - spec: str, params: Optional[Dict[str, str]] = None + spec: str, params: Optional[Dict[str, Any]] = None ) -> clr_loader.Runtime: if spec == "default": if sys.platform == "win32": @@ -109,9 +116,9 @@ def load( dll_path = Path(__file__).parent / "runtime" / "Python.Runtime.dll" - _LOADER_ASSEMBLY = _RUNTIME.get_assembly(str(dll_path)) + _LOADER_ASSEMBLY = assembly = _RUNTIME.get_assembly(str(dll_path)) + func = assembly.get_function("Python.Runtime.Loader.Initialize") - func = _LOADER_ASSEMBLY["Python.Runtime.Loader.Initialize"] if func(b"") != 0: raise RuntimeError("Failed to initialize Python.Runtime.dll") @@ -125,12 +132,12 @@ def unload() -> None: global _RUNTIME, _LOADER_ASSEMBLY if _LOADER_ASSEMBLY is not None: - func = _LOADER_ASSEMBLY["Python.Runtime.Loader.Shutdown"] + func = _LOADER_ASSEMBLY.get_function("Python.Runtime.Loader.Shutdown") if func(b"full_shutdown") != 0: raise RuntimeError("Failed to call Python.NET shutdown") _LOADER_ASSEMBLY = None if _RUNTIME is not None: - # TODO: Add explicit `close` to clr_loader + _RUNTIME.shutdown() _RUNTIME = None diff --git a/tests/conftest.py b/tests/conftest.py index fcd1d224a..e61e3680e 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -53,10 +53,12 @@ def pytest_configure(config): runtime_params = {} if runtime_opt == "coreclr": - fw = "net6.0" - runtime_params["runtime_config"] = str( - bin_path / "Python.Test.runtimeconfig.json" - ) + # This is optional now: + # + # fw = "net6.0" + # runtime_params["runtime_config"] = str( + # bin_path / "Python.Test.runtimeconfig.json" + # ) collect_ignore.append("domain_tests/test_domain_reload.py") else: domain_tests_dir = cwd / "domain_tests" From db52ddef247cdc008bae18b5ad0fee2f1c690b21 Mon Sep 17 00:00:00 2001 From: Benedikt Reinartz Date: Fri, 16 Sep 2022 21:34:45 +0200 Subject: [PATCH 2/3] Improve error message if the runtime fails to load --- pythonnet/__init__.py | 39 ++++++++++++++++++++++++++++----------- 1 file changed, 28 insertions(+), 11 deletions(-) diff --git a/pythonnet/__init__.py b/pythonnet/__init__.py index c847c4c74..2cde01233 100644 --- a/pythonnet/__init__.py +++ b/pythonnet/__init__.py @@ -28,6 +28,8 @@ def set_runtime(runtime: Union[clr_loader.Runtime, str], **params: str) -> None: def get_runtime_info() -> Optional[clr_loader.RuntimeInfo]: + """Retrieve information on the configured runtime""" + if _RUNTIME is None: return None else: @@ -52,7 +54,9 @@ def _get_params_from_env(prefix: str) -> Dict[str, str]: def _create_runtime_from_spec( spec: str, params: Optional[Dict[str, Any]] = None ) -> clr_loader.Runtime: + was_default = False if spec == "default": + was_default = True if sys.platform == "win32": spec = "netfx" else: @@ -60,14 +64,29 @@ def _create_runtime_from_spec( params = params or _get_params_from_env(spec) - if spec == "netfx": - return clr_loader.get_netfx(**params) - elif spec == "mono": - return clr_loader.get_mono(**params) - elif spec == "coreclr": - return clr_loader.get_coreclr(**params) - else: - raise RuntimeError(f"Invalid runtime name: '{spec}'") + try: + if spec == "netfx": + return clr_loader.get_netfx(**params) + elif spec == "mono": + return clr_loader.get_mono(**params) + elif spec == "coreclr": + return clr_loader.get_coreclr(**params) + else: + raise RuntimeError(f"Invalid runtime name: '{spec}'") + except Exception as exc: + if was_default: + raise RuntimeError( + f"""Failed to create a default .NET runtime, which would + have been "{spec}" on this system. Either install a + compatible runtime or configure it explicitly via + `set_runtime` or the `PYTHONNET_*` environment variables + (see set_runtime_from_env).""" + ) from exc + else: + raise RuntimeError( + f"""Failed to create a .NET runtime ({spec}) using the + parameters {params}.""" + ) from exc def set_runtime_from_env() -> None: @@ -92,9 +111,7 @@ def set_runtime_from_env() -> None: set_runtime(runtime) -def load( - runtime: Union[clr_loader.Runtime, str, None] = None, **params: str -) -> None: +def load(runtime: Union[clr_loader.Runtime, str, None] = None, **params: str) -> None: """Load Python.NET in the specified runtime The same parameters as for `set_runtime` can be used. By default, From fd8fd3b88edbc59237a9c00176006657a1a4dc63 Mon Sep 17 00:00:00 2001 From: Benedikt Reinartz Date: Sat, 17 Sep 2022 08:55:28 +0200 Subject: [PATCH 3/3] Fix manifest so we can build with python -m build --- MANIFEST.in | 1 + 1 file changed, 1 insertion(+) diff --git a/MANIFEST.in b/MANIFEST.in index 6458d5778..71473c2c3 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,6 +1,7 @@ graft src/runtime prune src/runtime/obj prune src/runtime/bin +include src/pythonnet.snk include Directory.Build.* include pythonnet.sln include version.txt