Skip to content

Commit 70e2e0a

Browse files
authored
Merge pull request #1817 from filmor/env-config
Implement configuring clr from environment
2 parents 43d1640 + 7da7889 commit 70e2e0a

File tree

11 files changed

+150
-482
lines changed

11 files changed

+150
-482
lines changed

.github/workflows/ARM.yml

+2-2
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ jobs:
3636
3737
- name: Set Python DLL path (non Windows)
3838
run: |
39-
python -m pythonnet.find_libpython --export >> $GITHUB_ENV
39+
echo PYTHONNET_PYDLL=$(python -m find_libpython) >> $GITHUB_ENV
4040
4141
- name: Embedding tests
4242
run: dotnet test --logger "console;verbosity=detailed" src/embed_tests/
@@ -45,7 +45,7 @@ jobs:
4545
run: python -m pytest --runtime mono
4646

4747
- name: Python Tests (.NET Core)
48-
run: python -m pytest --runtime netcore
48+
run: python -m pytest --runtime coreclr
4949

5050
- name: Python tests run from .NET
5151
run: dotnet test src/python_tests_runner/

.github/workflows/main.yml

+5-4
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
name: Main (x64)
1+
name: Main
22

33
on:
44
push:
@@ -57,12 +57,12 @@ jobs:
5757
- name: Set Python DLL path (non Windows)
5858
if: ${{ matrix.os != 'windows' }}
5959
run: |
60-
python -m pythonnet.find_libpython --export >> $GITHUB_ENV
60+
echo PYTHONNET_PYDLL=$(python -m find_libpython) >> $GITHUB_ENV
6161
6262
- name: Set Python DLL path (Windows)
6363
if: ${{ matrix.os == 'windows' }}
6464
run: |
65-
python -m pythonnet.find_libpython --export | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
65+
Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append -InputObject "PYTHONNET_PYDLL=$(python -m find_libpython)"
6666
6767
- name: Embedding tests
6868
run: dotnet test --runtime any-${{ matrix.platform }} --logger "console;verbosity=detailed" src/embed_tests/
@@ -73,9 +73,10 @@ jobs:
7373
if: ${{ matrix.os != 'windows' }}
7474
run: pytest --runtime mono
7575

76+
# TODO: Run these tests on Windows x86
7677
- name: Python Tests (.NET Core)
7778
if: ${{ matrix.platform == 'x64' }}
78-
run: pytest --runtime netcore
79+
run: pytest --runtime coreclr
7980

8081
- name: Python Tests (.NET Framework)
8182
if: ${{ matrix.os == 'windows' }}

.github/workflows/nuget-preview.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ jobs:
4646
- name: Set Python DLL path (non Windows)
4747
if: ${{ matrix.os != 'windows' }}
4848
run: |
49-
python -m pythonnet.find_libpython --export >> $GITHUB_ENV
49+
echo PYTHONNET_PYDLL=$(python -m find_libpython) >> $GITHUB_ENV
5050
5151
- name: Python Tests
5252
run: pytest

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ and other `PyObject` derived types when called from Python.
2929
- .NET classes, that have `__call__` method are callable from Python
3030
- `PyIterable` type, that wraps any iterable object in Python
3131
- `PythonEngine` properties for supported Python versions: `MinSupportedVersion`, `MaxSupportedVersion`, and `IsSupportedVersion`
32+
- The runtime that is loaded on `import clr` can now be configured via environment variables
3233

3334

3435
### Changed

pythonnet/__init__.py

+96-20
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,115 @@
11
import sys
2+
from pathlib import Path
3+
from typing import Dict, Optional, Union
24
import clr_loader
35

4-
_RUNTIME = None
5-
_LOADER_ASSEMBLY = None
6-
_FFI = None
7-
_LOADED = False
6+
__all__ = ["set_runtime", "set_default_runtime", "load"]
87

8+
_RUNTIME: Optional[clr_loader.Runtime] = None
9+
_LOADER_ASSEMBLY: Optional[clr_loader.wrappers.Assembly] = None
10+
_LOADED: bool = False
11+
12+
13+
def set_runtime(runtime: Union[clr_loader.Runtime, str], **params: str) -> None:
14+
"""Set up a clr_loader runtime without loading it
15+
16+
:param runtime: Either an already initialised `clr_loader` runtime, or one
17+
of netfx, coreclr, mono, or default. If a string parameter is given, the
18+
runtime will be created."""
919

10-
def set_runtime(runtime):
1120
global _RUNTIME
1221
if _LOADED:
13-
raise RuntimeError("The runtime {} has already been loaded".format(_RUNTIME))
22+
raise RuntimeError(f"The runtime {_RUNTIME} has already been loaded")
23+
24+
if isinstance(runtime, str):
25+
runtime = _create_runtime_from_spec(runtime, params)
1426

1527
_RUNTIME = runtime
1628

1729

18-
def set_default_runtime() -> None:
19-
if sys.platform == "win32":
20-
set_runtime(clr_loader.get_netfx())
30+
def _get_params_from_env(prefix: str) -> Dict[str, str]:
31+
from os import environ
32+
33+
full_prefix = f"PYTHONNET_{prefix.upper()}"
34+
len_ = len(full_prefix)
35+
36+
env_vars = {
37+
(k[len_:].lower()): v
38+
for k, v in environ.items()
39+
if k.upper().startswith(full_prefix)
40+
}
41+
42+
return env_vars
43+
44+
45+
def _create_runtime_from_spec(
46+
spec: str, params: Optional[Dict[str, str]] = None
47+
) -> clr_loader.Runtime:
48+
if spec == "default":
49+
if sys.platform == "win32":
50+
spec = "netfx"
51+
else:
52+
spec = "mono"
53+
54+
params = params or _get_params_from_env(spec)
55+
56+
if spec == "netfx":
57+
return clr_loader.get_netfx(**params)
58+
elif spec == "mono":
59+
return clr_loader.get_mono(**params)
60+
elif spec == "coreclr":
61+
return clr_loader.get_coreclr(**params)
2162
else:
22-
set_runtime(clr_loader.get_mono())
63+
raise RuntimeError(f"Invalid runtime name: '{spec}'")
2364

2465

25-
def load():
26-
global _FFI, _LOADED, _LOADER_ASSEMBLY
66+
def set_default_runtime() -> None:
67+
"""Set up the default runtime
68+
69+
This will use the environment variable PYTHONNET_RUNTIME to decide the
70+
runtime to use, which may be one of netfx, coreclr or mono. The parameters
71+
of the respective clr_loader.get_<runtime> functions can also be given as
72+
environment variables, named `PYTHONNET_<RUNTIME>_<PARAM_NAME>`. In
73+
particular, to use `PYTHONNET_RUNTIME=coreclr`, the variable
74+
`PYTHONNET_CORECLR_RUNTIME_CONFIG` has to be set to a valid
75+
`.runtimeconfig.json`.
76+
77+
If no environment variable is specified, a globally installed Mono is used
78+
for all environments but Windows, on Windows the legacy .NET Framework is
79+
used.
80+
"""
81+
from os import environ
82+
83+
print("Set default RUNTIME")
84+
raise RuntimeError("Shouldn't be called here")
85+
86+
spec = environ.get("PYTHONNET_RUNTIME", "default")
87+
runtime = _create_runtime_from_spec(spec)
88+
set_runtime(runtime)
89+
90+
91+
def load(
92+
runtime: Union[clr_loader.Runtime, str] = "default", **params: Dict[str, str]
93+
) -> None:
94+
"""Load Python.NET in the specified runtime
95+
96+
The same parameters as for `set_runtime` can be used. By default,
97+
`set_default_runtime` is called if no environment has been set yet and no
98+
parameters are passed."""
99+
global _LOADED, _LOADER_ASSEMBLY
27100

28101
if _LOADED:
29102
return
30103

31-
from os.path import join, dirname
104+
if _RUNTIME is None:
105+
set_runtime(runtime, **params)
32106

33107
if _RUNTIME is None:
34-
# TODO: Warn, in the future the runtime must be set explicitly, either
35-
# as a config/env variable or via set_runtime
36-
set_default_runtime()
108+
raise RuntimeError("No valid runtime selected")
37109

38-
dll_path = join(dirname(__file__), "runtime", "Python.Runtime.dll")
110+
dll_path = Path(__file__).parent / "runtime" / "Python.Runtime.dll"
39111

40-
_LOADER_ASSEMBLY = _RUNTIME.get_assembly(dll_path)
112+
_LOADER_ASSEMBLY = _RUNTIME.get_assembly(str(dll_path))
41113

42114
func = _LOADER_ASSEMBLY["Python.Runtime.Loader.Initialize"]
43115
if func(b"") != 0:
@@ -48,13 +120,17 @@ def load():
48120
atexit.register(unload)
49121

50122

51-
def unload():
52-
global _RUNTIME
123+
def unload() -> None:
124+
"""Explicitly unload a laoded runtime and shut down Python.NET"""
125+
126+
global _RUNTIME, _LOADER_ASSEMBLY
53127
if _LOADER_ASSEMBLY is not None:
54128
func = _LOADER_ASSEMBLY["Python.Runtime.Loader.Shutdown"]
55129
if func(b"full_shutdown") != 0:
56130
raise RuntimeError("Failed to call Python.NET shutdown")
57131

132+
_LOADER_ASSEMBLY = None
133+
58134
if _RUNTIME is not None:
59135
# TODO: Add explicit `close` to clr_loader
60136
_RUNTIME = None

0 commit comments

Comments
 (0)