@@ -1543,42 +1543,41 @@ final class PyFunction {
15431543
15441544public struct PythonFunction {
15451545 /// Called directly by the Python C API
1546- private var function : Unmanaged < PyFunction >
1546+ private var function : PyFunction
15471547
15481548 public init ( _ fn: @escaping ( PythonObject ) throws -> PythonConvertible ) {
1549- let function = PyFunction { argumentsAsTuple in
1549+ function = PyFunction { argumentsAsTuple in
15501550 return try fn ( argumentsAsTuple [ 0 ] )
15511551 }
1552- self . function = Unmanaged . passRetained ( function)
15531552 }
15541553
15551554 /// For cases where the Swift function should accept more (or less) than one parameter, accept an ordered array of all arguments instead
15561555 public init ( _ fn: @escaping ( [ PythonObject ] ) throws -> PythonConvertible ) {
1557- let function = PyFunction { argumentsAsTuple in
1556+ function = PyFunction { argumentsAsTuple in
15581557 return try fn ( argumentsAsTuple. map { $0 } )
15591558 }
1560- self . function = Unmanaged . passRetained ( function)
15611559 }
15621560
1563- // FIXME: Memory management issue:
1564- // It is necessary to pass a retained reference to `PythonFunction` so that it
1565- // outlives the `PyReference` of the PyCFunction we create below. If we don't,
1566- // Python tries to access what then has become a garbage pointer when it cleans
1567- // up the CFunction. This means the entire `PythonFunction` currently leaks.
1568- public func deallocate( ) {
1569- function. release ( )
1570- }
15711561}
15721562
15731563extension PythonFunction : PythonConvertible {
15741564 public var pythonObject : PythonObject {
1575- _ = Python // Ensure Python is initialized.
1565+ // Ensure Python is initialized, and check for version match.
1566+ let versionMajor = Python . versionInfo. major
1567+ let versionMinor = Python . versionInfo. minor
1568+ guard ( versionMajor == 3 && versionMinor >= 1 ) || versionMajor > 3 else {
1569+ fatalError ( " PythonFunction only supports Python 3.1 and above. " )
1570+ }
15761571
1577- let funcPointer = function. toOpaque ( )
1572+ let funcPointer = Unmanaged . passRetained ( function) . toOpaque ( )
1573+ let capsulePointer = PyCapsule_New ( funcPointer, nil , { capsulePointer in
1574+ let funcPointer = PyCapsule_GetPointer ( capsulePointer, nil )
1575+ Unmanaged < PyFunction > . fromOpaque ( funcPointer) . release ( )
1576+ } )
15781577
15791578 let pyFuncPointer = PyCFunction_New (
15801579 PythonFunction . sharedMethodDefinition,
1581- funcPointer
1580+ capsulePointer
15821581 )
15831582
15841583 return PythonObject ( consuming: pyFuncPointer)
@@ -1609,11 +1608,12 @@ fileprivate extension PythonFunction {
16091608 } ( )
16101609
16111610 private static let sharedMethodImplementation : @convention ( c) ( PyObjectPointer ? , PyObjectPointer ? ) -> PyObjectPointer ? = { context, argumentsPointer in
1612- guard let argumentsPointer = argumentsPointer, let selfPointer = context else {
1611+ guard let argumentsPointer = argumentsPointer, let capsulePointer = context else {
16131612 return nil
16141613 }
16151614
1616- let function = Unmanaged < PyFunction > . fromOpaque ( selfPointer) . takeUnretainedValue ( )
1615+ let funcPointer = PyCapsule_GetPointer ( capsulePointer, nil )
1616+ let function = Unmanaged < PyFunction > . fromOpaque ( funcPointer) . takeUnretainedValue ( )
16171617
16181618 do {
16191619 let argumentsAsTuple = PythonObject ( consuming: argumentsPointer)
0 commit comments