Skip to content

Commit 386bddf

Browse files
committed
Simplify python initialization
1 parent 0ffa5df commit 386bddf

File tree

2 files changed

+44
-19
lines changed

2 files changed

+44
-19
lines changed

ExtensionService/InitializePython.swift

Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,38 +2,37 @@ import Foundation
22
import Python
33
import PythonHelper
44
import PythonKit
5+
import Logger
56

7+
@MainActor
68
func initializePython() {
79
guard let sitePackagePath = Bundle.main.path(forResource: "site-packages", ofType: nil),
810
let stdLibPath = Bundle.main.path(forResource: "python-stdlib", ofType: nil),
911
let libDynloadPath = Bundle.main.path(
1012
forResource: "python-stdlib/lib-dynload",
1113
ofType: nil
1214
)
13-
else { return }
14-
15-
setenv("PYTHONHOME", stdLibPath, 1)
16-
setenv("PYTHONPATH", "\(stdLibPath):\(libDynloadPath):\(sitePackagePath)", 1)
17-
18-
// Initialize python
19-
Py_Initialize()
20-
21-
// Immediately release the thread, so that we can ensure the GIL state later.
22-
// We may not recover the thread because all future tasks will be done in the Python Thread.
23-
_ = PyEval_SaveThread()
15+
else {
16+
Logger.service.info("Python is not installed!")
17+
return
18+
}
2419

25-
// Setup GIL state guard.
26-
PythonHelper.gilStateEnsure = { PyGILState_Ensure() }
27-
PythonHelper.gilStateRelease = { gilState in PyGILState_Release(gilState as! PyGILState_STATE) }
20+
PythonHelper.initializePython(
21+
sitePackagePath: sitePackagePath,
22+
stdLibPath: stdLibPath,
23+
libDynloadPath: libDynloadPath,
24+
Py_Initialize: Py_Initialize,
25+
PyEval_SaveThread: PyEval_SaveThread,
26+
PyGILState_Ensure: PyGILState_Ensure,
27+
PyGILState_Release: PyGILState_Release
28+
)
2829

2930
Task {
3031
// All future task should run inside runPython.
3132
try runPython {
3233
let sys = Python.import("sys")
33-
print("Python Version: \(sys.version_info.major).\(sys.version_info.minor)")
34+
Logger.service.info("Python Version: \(sys.version_info.major).\(sys.version_info.minor)")
3435
}
3536
}
3637
}
3738

38-
let queue = DispatchQueue(label: "")
39-

Tool/Sources/PythonHelper/RunPython.swift

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import Foundation
22
import PythonKit
33

4-
public var gilStateEnsure: (() -> Any)!
5-
public var gilStateRelease: ((Any) -> Void)!
4+
var gilStateEnsure: (() -> Any)!
5+
var gilStateRelease: ((Any) -> Void)!
66
func gilStateGuard<T>(_ closure: @escaping () throws -> T) throws -> T {
77
let state = gilStateEnsure()
88
do {
@@ -15,6 +15,32 @@ func gilStateGuard<T>(_ closure: @escaping () throws -> T) throws -> T {
1515
}
1616
}
1717

18+
@MainActor
19+
var isPythonInitialized = false
20+
@MainActor
21+
public func initializePython<GilState, ThreadState>(
22+
sitePackagePath: String,
23+
stdLibPath: String,
24+
libDynloadPath: String,
25+
Py_Initialize: () -> Void,
26+
PyEval_SaveThread: () -> ThreadState,
27+
PyGILState_Ensure: @escaping () -> GilState,
28+
PyGILState_Release: @escaping (GilState) -> Void
29+
) {
30+
guard !isPythonInitialized else { return }
31+
setenv("PYTHONHOME", stdLibPath, 1)
32+
setenv("PYTHONPATH", "\(stdLibPath):\(libDynloadPath):\(sitePackagePath)", 1)
33+
isPythonInitialized = true
34+
// Initialize python
35+
Py_Initialize()
36+
// Immediately release the thread, so that we can ensure the GIL state later.
37+
// We may not recover the thread because all future tasks will be done in the other threads.
38+
_ = PyEval_SaveThread()
39+
// Setup GIL state guard.
40+
gilStateEnsure = { PyGILState_Ensure() }
41+
gilStateRelease = { gilState in PyGILState_Release(gilState as! GilState) }
42+
}
43+
1844
public func runPython<T>(
1945
usePythonThread: Bool = false,
2046
_ closure: @escaping () throws -> T

0 commit comments

Comments
 (0)