1
1
import sys
2
+ from pathlib import Path
3
+ from typing import Dict , Optional , Union
2
4
import clr_loader
3
5
4
- _RUNTIME = None
5
- _LOADER_ASSEMBLY = None
6
- _FFI = None
7
- _LOADED = False
6
+ __all__ = ["set_runtime" , "set_default_runtime" , "load" ]
8
7
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."""
9
19
10
- def set_runtime (runtime ):
11
20
global _RUNTIME
12
21
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 )
14
26
15
27
_RUNTIME = runtime
16
28
17
29
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 )
21
62
else :
22
- set_runtime ( clr_loader . get_mono () )
63
+ raise RuntimeError ( f"Invalid runtime name: ' { spec } '" )
23
64
24
65
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
27
100
28
101
if _LOADED :
29
102
return
30
103
31
- from os .path import join , dirname
104
+ if _RUNTIME is None :
105
+ set_runtime (runtime , ** params )
32
106
33
107
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" )
37
109
38
- dll_path = join ( dirname ( __file__ ), "runtime" , "Python.Runtime.dll" )
110
+ dll_path = Path ( __file__ ). parent / "runtime" / "Python.Runtime.dll"
39
111
40
- _LOADER_ASSEMBLY = _RUNTIME .get_assembly (dll_path )
112
+ _LOADER_ASSEMBLY = _RUNTIME .get_assembly (str ( dll_path ) )
41
113
42
114
func = _LOADER_ASSEMBLY ["Python.Runtime.Loader.Initialize" ]
43
115
if func (b"" ) != 0 :
@@ -48,13 +120,17 @@ def load():
48
120
atexit .register (unload )
49
121
50
122
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
53
127
if _LOADER_ASSEMBLY is not None :
54
128
func = _LOADER_ASSEMBLY ["Python.Runtime.Loader.Shutdown" ]
55
129
if func (b"full_shutdown" ) != 0 :
56
130
raise RuntimeError ("Failed to call Python.NET shutdown" )
57
131
132
+ _LOADER_ASSEMBLY = None
133
+
58
134
if _RUNTIME is not None :
59
135
# TODO: Add explicit `close` to clr_loader
60
136
_RUNTIME = None
0 commit comments