@@ -28,10 +28,9 @@ import WinSDK
28
28
//===----------------------------------------------------------------------===//
29
29
30
30
public struct PythonLibrary {
31
- private static let shared = PythonLibrary ( )
32
31
private static let pythonInitializeSymbolName = " Py_Initialize "
33
32
private static let pythonLegacySymbolName = " PyString_AsString "
34
- private static var librarySymbolsLoaded = false
33
+ private static var isPythonLibraryLoaded = false
35
34
36
35
#if canImport(Darwin)
37
36
private static let defaultLibraryHandle = UnsafeMutableRawPointer ( bitPattern: - 2 ) // RTLD_DEFAULT
@@ -41,60 +40,61 @@ public struct PythonLibrary {
41
40
private static let defaultLibraryHandle : UnsafeMutableRawPointer ? = nil // Unsupported
42
41
#endif
43
42
44
- private let pythonLibraryHandle : UnsafeMutableRawPointer ?
45
- private let isLegacyPython : Bool
46
-
47
- private init ( ) {
48
- self . pythonLibraryHandle = PythonLibrary . loadPythonLibrary ( )
49
-
50
- // Check if Python is legacy (Python 2)
51
- self . isLegacyPython = Self . loadSymbol ( pythonLibraryHandle, PythonLibrary . pythonLegacySymbolName) != nil
43
+ private static let pythonLibraryHandle : UnsafeMutableRawPointer ? = {
44
+ let pythonLibraryHandle = Self . loadPythonLibrary ( )
45
+ guard Self . isPythonLibraryLoaded ( at: pythonLibraryHandle) else {
46
+ fatalError ( """
47
+ Python library not found. Set the \( Environment . library. key) \
48
+ environment variable with the path to a Python library.
49
+ """ )
50
+ }
51
+ Self . isPythonLibraryLoaded = true
52
+ return pythonLibraryHandle
53
+ } ( )
54
+ private static let isLegacyPython : Bool = {
55
+ let isLegacyPython = Self . loadSymbol ( Self . pythonLibraryHandle, Self . pythonLegacySymbolName) != nil
52
56
if isLegacyPython {
53
- PythonLibrary . log ( " Loaded legacy Python library, using legacy symbols... " )
57
+ Self . log ( " Loaded legacy Python library, using legacy symbols... " )
54
58
}
55
- PythonLibrary . librarySymbolsLoaded = true
56
- }
57
-
58
- static func loadSymbol(
59
- _ libraryHandle: UnsafeMutableRawPointer ? , _ name: String ) -> UnsafeMutableRawPointer ? {
60
- #if canImport(Darwin) || canImport(Glibc)
61
- return dlsym ( libraryHandle, name)
62
- #elseif os(Windows)
63
- guard let libraryHandle = libraryHandle else { return nil }
64
- let moduleHandle = libraryHandle
65
- . assumingMemoryBound ( to: HINSTANCE__ . self)
66
- let moduleSymbol = GetProcAddress ( moduleHandle, name)
67
- return unsafeBitCast ( moduleSymbol, to: UnsafeMutableRawPointer ? . self)
68
- #endif
69
- }
59
+ return isLegacyPython
60
+ } ( )
70
61
71
- static func loadSymbol< T> (
62
+ internal static func loadSymbol< T> (
72
63
name: String , legacyName: String ? = nil , type: T . Type = T . self) -> T {
73
64
var name = name
74
- if let legacyName = legacyName, PythonLibrary . shared . isLegacyPython {
65
+ if let legacyName = legacyName, self . isLegacyPython {
75
66
name = legacyName
76
67
}
77
68
78
69
log ( " Loading symbol ' \( name) ' from the Python library... " )
79
- return unsafeBitCast ( loadSymbol ( PythonLibrary . shared . pythonLibraryHandle, name) , to: type)
70
+ return unsafeBitCast ( self . loadSymbol ( self . pythonLibraryHandle, name) , to: type)
80
71
}
81
72
}
82
73
83
74
// Methods of `PythonLibrary` required to set a given Python version.
84
- public extension PythonLibrary {
85
- static func useVersion ( _ major : Int , _ minor : Int ? = nil ) {
86
- precondition ( !librarySymbolsLoaded , """
87
- Error: \( # function) should not be called after any Python library \
75
+ extension PythonLibrary {
76
+ private static func enforceNonLoadedPythonLibrary ( function : String = #function ) {
77
+ precondition ( !self . isPythonLibraryLoaded , """
78
+ Error: \( function) should not be called after any Python library \
88
79
has already been loaded.
89
80
""" )
81
+ }
82
+
83
+ public static func useVersion( _ major: Int , _ minor: Int ? = nil ) {
84
+ self . enforceNonLoadedPythonLibrary ( )
90
85
let version = PythonVersion ( major: major, minor: minor)
91
86
PythonLibrary . Environment. version. set ( version. versionString)
92
87
}
88
+
89
+ public static func usePythonLibrary( at path: String ) {
90
+ self . enforceNonLoadedPythonLibrary ( )
91
+ PythonLibrary . Environment. library. set ( path)
92
+ }
93
93
}
94
94
95
95
// `PythonVersion` struct that defines a given Python version.
96
- private extension PythonLibrary {
97
- struct PythonVersion {
96
+ extension PythonLibrary {
97
+ private struct PythonVersion {
98
98
let major : Int
99
99
let minor : Int ?
100
100
@@ -116,8 +116,8 @@ private extension PythonLibrary {
116
116
}
117
117
118
118
// `PythonLibrary.Environment` enum used to read and set environment variables.
119
- private extension PythonLibrary {
120
- enum Environment : String {
119
+ extension PythonLibrary {
120
+ private enum Environment : String {
121
121
private static let keyPrefix = " PYTHON "
122
122
private static let keySeparator = " _ "
123
123
@@ -145,34 +145,30 @@ private extension PythonLibrary {
145
145
}
146
146
147
147
// Methods of `PythonLibrary` required to load the Python library.
148
- private extension PythonLibrary {
149
- static let supportedMajorVersions : [ Int ] = [ 3 , 2 ]
150
- static let supportedMinorVersions : [ Int ] = Array ( 0 ... 30 ) . reversed ( )
148
+ extension PythonLibrary {
149
+ private static let supportedMajorVersions : [ Int ] = [ 3 , 2 ]
150
+ private static let supportedMinorVersions : [ Int ] = Array ( 0 ... 30 ) . reversed ( )
151
151
152
- static let libraryPathVersionCharacter : Character = " : "
152
+ private static let libraryPathVersionCharacter : Character = " : "
153
153
154
154
#if canImport(Darwin)
155
- static var libraryNames = [ " Python.framework/Versions/:/Python " ]
156
- static var libraryPathExtensions = [ " " ]
157
- static var librarySearchPaths = [ " " , " /usr/local/Frameworks/ " ]
158
- static var libraryVersionSeparator = " . "
155
+ private static var libraryNames = [ " Python.framework/Versions/:/Python " ]
156
+ private static var libraryPathExtensions = [ " " ]
157
+ private static var librarySearchPaths = [ " " , " /usr/local/Frameworks/ " ]
158
+ private static var libraryVersionSeparator = " . "
159
159
#elseif os(Linux)
160
- static var libraryNames = [ " libpython: " , " libpython:m " ]
161
- static var libraryPathExtensions = [ " .so " ]
162
- static var librarySearchPaths = [ " " ]
163
- static var libraryVersionSeparator = " . "
160
+ private static var libraryNames = [ " libpython: " , " libpython:m " ]
161
+ private static var libraryPathExtensions = [ " .so " ]
162
+ private static var librarySearchPaths = [ " " ]
163
+ private static var libraryVersionSeparator = " . "
164
164
#elseif os(Windows)
165
- static var libraryNames = [ " python: " ]
166
- static var libraryPathExtensions = [ " .dll " ]
167
- static var librarySearchPaths = [ " " ]
168
- static var libraryVersionSeparator = " "
165
+ private static var libraryNames = [ " python: " ]
166
+ private static var libraryPathExtensions = [ " .dll " ]
167
+ private static var librarySearchPaths = [ " " ]
168
+ private static var libraryVersionSeparator = " "
169
169
#endif
170
170
171
- private static var isPythonLibraryPreloaded : Bool {
172
- return self . loadSymbol ( self . defaultLibraryHandle, self . pythonInitializeSymbolName) != nil
173
- }
174
-
175
- static let libraryPaths : [ String ] = {
171
+ private static let libraryPaths : [ String ] = {
176
172
var libraryPaths : [ String ] = [ ]
177
173
for librarySearchPath in librarySearchPaths {
178
174
for libraryName in libraryNames {
@@ -185,14 +181,40 @@ private extension PythonLibrary {
185
181
}
186
182
return libraryPaths
187
183
} ( )
184
+
185
+ private static func loadSymbol(
186
+ _ libraryHandle: UnsafeMutableRawPointer ? , _ name: String ) -> UnsafeMutableRawPointer ? {
187
+ #if canImport(Darwin) || canImport(Glibc)
188
+ return dlsym ( libraryHandle, name)
189
+ #elseif os(Windows)
190
+ guard let libraryHandle = libraryHandle else { return nil }
191
+ let moduleHandle = libraryHandle
192
+ . assumingMemoryBound ( to: HINSTANCE__ . self)
193
+ let moduleSymbol = GetProcAddress ( moduleHandle, name)
194
+ return unsafeBitCast ( moduleSymbol, to: UnsafeMutableRawPointer ? . self)
195
+ #endif
196
+ }
197
+
198
+ private static func isPythonLibraryLoaded( at pythonLibraryHandle: UnsafeMutableRawPointer ? = nil ) -> Bool {
199
+ let pythonLibraryHandle = pythonLibraryHandle ?? self . defaultLibraryHandle
200
+ return self . loadSymbol ( pythonLibraryHandle, self . pythonInitializeSymbolName) != nil
201
+ }
188
202
189
- static func loadPythonLibrary( ) -> UnsafeMutableRawPointer ? {
190
- if self . isPythonLibraryPreloaded {
191
- return self . defaultLibraryHandle
203
+ private static func loadPythonLibrary( ) -> UnsafeMutableRawPointer ? {
204
+ let pythonLibraryHandle : UnsafeMutableRawPointer ?
205
+ if self . isPythonLibraryLoaded ( ) {
206
+ pythonLibraryHandle = self . defaultLibraryHandle
192
207
}
193
208
else if let pythonLibraryPath = Environment . library. value {
194
- return self . loadPythonLibrary ( at: pythonLibraryPath)
209
+ pythonLibraryHandle = self . loadPythonLibrary ( at: pythonLibraryPath)
195
210
}
211
+ else {
212
+ pythonLibraryHandle = self . findAndLoadExternalPythonLibrary ( )
213
+ }
214
+ return pythonLibraryHandle
215
+ }
216
+
217
+ private static func findAndLoadExternalPythonLibrary( ) -> UnsafeMutableRawPointer ? {
196
218
for majorVersion in supportedMajorVersions {
197
219
for minorVersion in supportedMinorVersions {
198
220
for libraryPath in libraryPaths {
@@ -205,13 +227,10 @@ private extension PythonLibrary {
205
227
}
206
228
}
207
229
}
208
- fatalError ( """
209
- Python library not found. Set the \( Environment . library. key) \
210
- environment variable with the path to a Python library.
211
- """ )
230
+ return nil
212
231
}
213
232
214
- static func loadPythonLibrary(
233
+ private static func loadPythonLibrary(
215
234
at path: String , version: PythonVersion ) -> UnsafeMutableRawPointer ? {
216
235
let versionString = version. versionString
217
236
@@ -231,8 +250,8 @@ private extension PythonLibrary {
231
250
return self . loadPythonLibrary ( at: path)
232
251
}
233
252
234
- static func loadPythonLibrary( at path: String ) -> UnsafeMutableRawPointer ? {
235
- log ( " Trying to load library at ' \( path) '... " )
253
+ private static func loadPythonLibrary( at path: String ) -> UnsafeMutableRawPointer ? {
254
+ self . log ( " Trying to load library at ' \( path) '... " )
236
255
#if canImport(Darwin) || canImport(Glibc)
237
256
// Must be RTLD_GLOBAL because subsequent .so files from the imported python
238
257
// modules may depend on this .so file.
@@ -242,15 +261,15 @@ private extension PythonLibrary {
242
261
#endif
243
262
244
263
if pythonLibraryHandle != nil {
245
- log ( " Library at ' \( path) ' was sucessfully loaded. " )
264
+ self . log ( " Library at ' \( path) ' was sucessfully loaded. " )
246
265
}
247
266
return pythonLibraryHandle
248
267
}
249
268
}
250
269
251
270
// Methods of `PythonLibrary` used for logging messages.
252
- private extension PythonLibrary {
253
- static func log( _ message: String ) {
271
+ extension PythonLibrary {
272
+ private static func log( _ message: String ) {
254
273
guard Environment . loaderLogging. value != nil else { return }
255
274
fputs ( message + " \n " , stderr)
256
275
}
0 commit comments