@@ -45,17 +45,20 @@ pub(crate) fn gil_is_acquired() -> bool {
45
45
/// If both the Python interpreter and Python threading are already initialized,
46
46
/// this function has no effect.
47
47
///
48
- /// # Features
48
+ /// # Availability
49
49
///
50
- /// This function is only available with the `embedding` feature.
50
+ /// This function is only available when linking against Python distributions that contain a
51
+ /// shared library.
52
+ ///
53
+ /// This function is not available on PyPy.
51
54
///
52
55
/// # Panic
53
56
/// If the Python interpreter is initialized but Python threading is not,
54
57
/// a panic occurs.
55
58
/// It is not possible to safely access the Python runtime unless the main
56
59
/// thread (the thread which originally initialized Python) also initializes
57
60
/// threading.
58
- #[ cfg( feature = "embedding" ) ]
61
+ #[ cfg( all ( Py_SHARED , not ( PyPy ) ) ) ]
59
62
pub fn prepare_freethreaded_python ( ) {
60
63
// Protect against race conditions when Python is not yet initialized
61
64
// and multiple threads concurrently call 'prepare_freethreaded_python()'.
@@ -67,42 +70,38 @@ pub fn prepare_freethreaded_python() {
67
70
// as we can't make the existing Python main thread acquire the GIL.
68
71
assert_ne ! ( ffi:: PyEval_ThreadsInitialized ( ) , 0 ) ;
69
72
} else {
70
- // TODO remove this cfg once build.rs rejects embedding feature misuse.
71
- #[ cfg( not( PyPy ) ) ]
72
- {
73
- // Initialize Python.
74
- // We use Py_InitializeEx() with initsigs=0 to disable Python signal handling.
75
- // Signal handling depends on the notion of a 'main thread', which doesn't exist in this case.
76
- // Note that the 'main thread' notion in Python isn't documented properly;
77
- // and running Python without one is not officially supported.
78
-
79
- ffi:: Py_InitializeEx ( 0 ) ;
80
-
81
- // Make sure Py_Finalize will be called before exiting.
82
- extern "C" fn finalize ( ) {
83
- unsafe {
84
- if ffi:: Py_IsInitialized ( ) != 0 {
85
- ffi:: PyGILState_Ensure ( ) ;
86
- ffi:: Py_Finalize ( ) ;
87
- }
88
- }
89
- }
90
- libc:: atexit ( finalize) ;
73
+ // Initialize Python.
74
+ // We use Py_InitializeEx() with initsigs=0 to disable Python signal handling.
75
+ // Signal handling depends on the notion of a 'main thread', which doesn't exist in this case.
76
+ // Note that the 'main thread' notion in Python isn't documented properly;
77
+ // and running Python without one is not officially supported.
91
78
92
- // > Changed in version 3.7: This function is now called by Py_Initialize(), so you don’t have
93
- // > to call it yourself anymore.
94
- #[ cfg( not( Py_3_7 ) ) ]
95
- if ffi:: PyEval_ThreadsInitialized ( ) == 0 {
96
- ffi:: PyEval_InitThreads ( ) ;
79
+ ffi:: Py_InitializeEx ( 0 ) ;
80
+
81
+ // Make sure Py_Finalize will be called before exiting.
82
+ extern "C" fn finalize ( ) {
83
+ unsafe {
84
+ if ffi:: Py_IsInitialized ( ) != 0 {
85
+ ffi:: PyGILState_Ensure ( ) ;
86
+ ffi:: Py_Finalize ( ) ;
87
+ }
97
88
}
89
+ }
90
+ libc:: atexit ( finalize) ;
98
91
99
- // Py_InitializeEx() will acquire the GIL, but we don't want to hold it at this point
100
- // (it's not acquired in the other code paths)
101
- // So immediately release the GIL:
102
- let _thread_state = ffi:: PyEval_SaveThread ( ) ;
103
- // Note that the PyThreadState returned by PyEval_SaveThread is also held in TLS by the Python runtime,
104
- // and will be restored by PyGILState_Ensure.
92
+ // > Changed in version 3.7: This function is now called by Py_Initialize(), so you don’t have
93
+ // > to call it yourself anymore.
94
+ #[ cfg( not( Py_3_7 ) ) ]
95
+ if ffi:: PyEval_ThreadsInitialized ( ) == 0 {
96
+ ffi:: PyEval_InitThreads ( ) ;
105
97
}
98
+
99
+ // Py_InitializeEx() will acquire the GIL, but we don't want to hold it at this point
100
+ // (it's not acquired in the other code paths)
101
+ // So immediately release the GIL:
102
+ let _thread_state = ffi:: PyEval_SaveThread ( ) ;
103
+ // Note that the PyThreadState returned by PyEval_SaveThread is also held in TLS by the Python runtime,
104
+ // and will be restored by PyGILState_Ensure.
106
105
}
107
106
} ) ;
108
107
}
@@ -137,24 +136,56 @@ impl GILGuard {
137
136
/// If PyO3 does not yet have a `GILPool` for tracking owned PyObject references, then this
138
137
/// new `GILGuard` will also contain a `GILPool`.
139
138
pub ( crate ) fn acquire ( ) -> GILGuard {
140
- #[ cfg( feature = "auto-initialize" ) ]
141
- prepare_freethreaded_python ( ) ;
142
-
143
- #[ cfg( not( feature = "auto-initialize" ) ) ]
144
- START . call_once_force ( |_| unsafe {
145
- // Use call_once_force because if there is a panic because the interpreter is not
146
- // initialized, it's fine for the user to initialize the interpreter and retry.
147
- assert_ne ! (
148
- ffi:: Py_IsInitialized ( ) ,
149
- 0 ,
150
- "The Python interpreter is not initalized and the `auto-initialize` feature is not enabled."
151
- ) ;
152
- assert_ne ! (
153
- ffi:: PyEval_ThreadsInitialized ( ) ,
154
- 0 ,
155
- "Python threading is not initalized and the `auto-initialize` feature is not enabled."
156
- ) ;
157
- } ) ;
139
+ // Maybe auto-initialize the GIL:
140
+ // - If auto-initialize feature set and supported, try to initalize the interpreter.
141
+ // - If the auto-initialize feature is set but unsupported, emit hard errors only when
142
+ // the extension-module feature is not activated - extension modules don't care about
143
+ // auto-initialize so this avoids breaking existing builds.
144
+ // - Otherwise, just check the GIL is initialized.
145
+ cfg_if:: cfg_if! {
146
+ if #[ cfg( all( feature = "auto-initialize" , Py_SHARED , not( PyPy ) ) ) ] {
147
+ prepare_freethreaded_python( ) ;
148
+ } else if #[ cfg( all( feature = "auto-initialize" , not( feature = "extension-module" ) , not( Py_SHARED ) , not( rustdoc) ) ) ] {
149
+ compile_error!( concat!(
150
+ "The `auto-initialize` feature is not supported when linking Python " ,
151
+ "statically instead of with a shared library.\n " ,
152
+ "\n " ,
153
+ "Please disable the `auto-initialize` feature, for example by entering the following " ,
154
+ "in your cargo.toml:\n " ,
155
+ "\n " ,
156
+ " pyo3 = { version = \" 0.13.0\" , default-features = false }\n " ,
157
+ "\n " ,
158
+ "Alternatively, compile PyO3 using a Python distribution which contains a shared " ,
159
+ "libary."
160
+ ) ) ;
161
+ } else if #[ cfg( all( feature = "auto-initialize" , not( feature = "extension-module" ) , PyPy , not( rustdoc) ) ) ] {
162
+ compile_error!( concat!(
163
+ "The `auto-initialize` feature is not supported by PyPy.\n " ,
164
+ "\n " ,
165
+ "Please disable the `auto-initialize` feature, for example by entering the following " ,
166
+ "in your cargo.toml:\n " ,
167
+ "\n " ,
168
+ " pyo3 = { version = \" 0.13.0\" , default-features = false }\n " ,
169
+ ) ) ;
170
+ } else {
171
+ // extension module feature enabled and PyPy or static linking
172
+ // OR auto-initialize feature not enabled
173
+ START . call_once_force( |_| unsafe {
174
+ // Use call_once_force because if there is a panic because the interpreter is not
175
+ // initialized, it's fine for the user to initialize the interpreter and retry.
176
+ assert_ne!(
177
+ ffi:: Py_IsInitialized ( ) ,
178
+ 0 ,
179
+ "The Python interpreter is not initalized and the `auto-initialize` feature is not enabled."
180
+ ) ;
181
+ assert_ne!(
182
+ ffi:: PyEval_ThreadsInitialized ( ) ,
183
+ 0 ,
184
+ "Python threading is not initalized and the `auto-initialize` feature is not enabled."
185
+ ) ;
186
+ } ) ;
187
+ }
188
+ }
158
189
159
190
let gstate = unsafe { ffi:: PyGILState_Ensure ( ) } ; // acquire GIL
160
191
0 commit comments