See More

//===-- Python.swift ------------------------------------------*- swift -*-===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// // // This file defines an interoperability layer for talking to Python from Swift. // //===----------------------------------------------------------------------===// // // The model provided by this file is completely dynamic and does not require // invasive compiler support. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// // `PyReference` definition //===----------------------------------------------------------------------===// /// Typealias used when passing or returning a `PyObject` pointer with /// implied ownership. @usableFromInline typealias OwnedPyObjectPointer = PyObjectPointer /// A primitive reference to a Python C API `PyObject`. /// /// A `PyReference` instance has ownership of its underlying `PyObject`, which /// must be non-null. /// // - Note: When Swift has ownership, `PyReference` should be removed. // `PythonObject` will define copy constructors, move constructors, etc. to // implement move semantics. @usableFromInline @_fixed_layout final class PyReference { private var pointer: OwnedPyObjectPointer // This `PyReference`, once deleted, will make no delta change to the // python object's reference count. It will however, retain the reference for // the lifespan of this object. init(_ pointer: OwnedPyObjectPointer) { self.pointer = pointer Py_IncRef(pointer) } // This `PyReference` adopts the +1 reference and will decrement it in the // future. init(consuming pointer: PyObjectPointer) { self.pointer = pointer } deinit { Py_DecRef(pointer) } var borrowedPyObject: PyObjectPointer { return pointer } var ownedPyObject: OwnedPyObjectPointer { Py_IncRef(pointer) return pointer } } //===----------------------------------------------------------------------===// // `PythonObject` definition //===----------------------------------------------------------------------===// // - Note: When Swift has ownership, `PythonObject` will define copy // constructors, move constructors, etc. to implement move semantics. /// `PythonObject` represents an object in Python and supports dynamic member /// lookup. Any member access like `object.foo` will dynamically request the /// Python runtime for a member with the specified name in this object. /// /// `PythonObject` is passed to and returned from all Python function calls and /// member references. It supports standard Python arithmetic and comparison /// operators. /// /// Internally, `PythonObject` is implemented as a reference-counted pointer to /// a Python C API `PyObject`. @dynamicCallable @dynamicMemberLookup public struct PythonObject { /// The underlying `PyReference`. fileprivate var reference: PyReference @usableFromInline init(_ pointer: PyReference) { reference = pointer } /// Creates a new instance and a new reference. init(_ pointer: OwnedPyObjectPointer) { reference = PyReference(pointer) } /// Creates a new instance consuming the specified `PyObject` pointer. init(consuming pointer: PyObjectPointer) { reference = PyReference(consuming: pointer) } fileprivate var borrowedPyObject: PyObjectPointer { return reference.borrowedPyObject } fileprivate var ownedPyObject: OwnedPyObjectPointer { return reference.ownedPyObject } } // Make `print(python)` print a pretty form of the `PythonObject`. extension PythonObject : CustomStringConvertible { /// A textual description of this `PythonObject`, produced by `Python.str`. public var description: String { // The `str` function is used here because it is designed to return // human-readable descriptions of Python objects. The Python REPL also uses // it for printing descriptions. // `repr` is not used because it is not designed to be readable and takes // too long for large objects. return String(Python.str(self))! } } // Make `PythonObject` show up nicely in the Xcode Playground results sidebar. extension PythonObject : CustomPlaygroundDisplayConvertible { public var playgroundDescription: Any { return description } } // Mirror representation, used by debugger/REPL. extension PythonObject : CustomReflectable { public var customMirror: Mirror { return Mirror(self, children: [], displayStyle: .struct) } } //===----------------------------------------------------------------------===// // `PythonConvertible` protocol //===----------------------------------------------------------------------===// /// A type whose values can be converted to a `PythonObject`. public protocol PythonConvertible { /// A `PythonObject` instance representing this value. var pythonObject: PythonObject { get } } public extension PythonObject { /// Creates a new instance from a `PythonConvertible` value. init(_ object: T) { self.init(object.pythonObject) } } /// Internal helpers to convert `PythonConvertible` values to owned and borrowed /// `PyObject` instances. These should not be made public. fileprivate extension PythonConvertible { var borrowedPyObject: PyObjectPointer { return pythonObject.borrowedPyObject } var ownedPyObject: OwnedPyObjectPointer { return pythonObject.ownedPyObject } } //===----------------------------------------------------------------------===// // `ConvertibleFromPython` protocol //===----------------------------------------------------------------------===// /// A type that can be initialized from a `PythonObject`. public protocol ConvertibleFromPython { /// Creates a new instance from the given `PythonObject`, if possible. /// - Note: Conversion may fail if the given `PythonObject` instance is /// incompatible (e.g. a Python `string` object cannot be converted into an /// `Int`). init?(_ object: PythonObject) } // `PythonObject` is trivially `PythonConvertible`. extension PythonObject : PythonConvertible, ConvertibleFromPython { public init(_ object: PythonObject) { self.init(consuming: object.ownedPyObject) } public var pythonObject: PythonObject { return self } } //===----------------------------------------------------------------------===// // `PythonObject` callable implementation //===----------------------------------------------------------------------===// public extension PythonObject { /// Returns a callable version of this `PythonObject`. When called, the result /// throws a Swift error if the underlying Python function throws a Python /// exception. var throwing: ThrowingPythonObject { return ThrowingPythonObject(self) } } /// An error produced by a failable Python operation. public enum PythonError : Error, Equatable { /// A Python runtime exception, produced by calling a Python function. case exception(PythonObject, traceback: PythonObject?) /// A failed call on a `PythonObject`. /// Reasons for failure include: /// - A non-callable Python object was called. /// - An incorrect number of arguments were provided to the callable Python /// object. /// - An invalid keyword argument was specified. case invalidCall(PythonObject) /// A module import error. case invalidModule(String) } extension PythonError : CustomStringConvertible { public var description: String { switch self { case .exception(let e, let t): var exceptionDescription = "Python exception: \(e)" if let t = t { let traceback = Python.import("traceback") exceptionDescription += """ \nTraceback: \(PythonObject("").join(traceback.format_tb(t))) """ } return exceptionDescription case .invalidCall(let e): return "Invalid Python call: \(e)" case .invalidModule(let m): return "Invalid Python module: \(m)" } } } // Reflect a Python error (which must be active) into a Swift error if one is // active. private func throwPythonErrorIfPresent() throws { if PyErr_Occurred() == nil { return } var type: PyObjectPointer? var value: PyObjectPointer? var traceback: PyObjectPointer? // Fetch the exception and clear the exception state. PyErr_Fetch(&type, &value, &traceback) // The value for the exception may not be set but the type always should be. let resultObject = PythonObject(consuming: value ?? type!) let tracebackObject = traceback.flatMap { PythonObject(consuming: $0) } throw PythonError.exception(resultObject, traceback: tracebackObject) } /// A `PythonObject` wrapper that enables throwing method calls. /// Exceptions produced by Python functions are reflected as Swift errors and /// thrown. /// - Note: It is intentional that `ThrowingPythonObject` does not have the /// `@dynamicCallable` attribute because the call syntax is unintuitive: /// `x.throwing(arg1, arg2, ...)`. The methods will still be named /// `dynamicallyCall` until further discussion/design. public struct ThrowingPythonObject { private var base: PythonObject fileprivate init(_ base: PythonObject) { self.base = base } /// Call `self` with the specified positional arguments. /// If the call fails for some reason, `PythonError.invalidCall` is thrown. /// - Precondition: `self` must be a Python callable. /// - Parameter args: Positional arguments for the Python callable. @discardableResult public func dynamicallyCall( withArguments args: PythonConvertible...) throws -> PythonObject { return try dynamicallyCall(withArguments: args) } /// Call `self` with the specified positional arguments. /// If the call fails for some reason, `PythonError.invalidCall` is thrown. /// - Precondition: `self` must be a Python callable. /// - Parameter args: Positional arguments for the Python callable. @discardableResult public func dynamicallyCall( withArguments args: [PythonConvertible] = []) throws -> PythonObject { try throwPythonErrorIfPresent() // Positional arguments are passed as a tuple of objects. let argTuple = pyTuple(args.map { $0.pythonObject }) defer { Py_DecRef(argTuple) } // Python calls always return a non-null object when successful. If the // Python function produces the equivalent of C `void`, it returns the // `None` object. A `null` result of `PyObjectCall` happens when there is an // error, like `self` not being a Python callable. let selfObject = base.ownedPyObject defer { Py_DecRef(selfObject) } guard let result = PyObject_CallObject(selfObject, argTuple) else { // If a Python exception was thrown, throw a corresponding Swift error. try throwPythonErrorIfPresent() throw PythonError.invalidCall(base) } return PythonObject(consuming: result) } /// Call `self` with the specified arguments. /// If the call fails for some reason, `PythonError.invalidCall` is thrown. /// - Precondition: `self` must be a Python callable. /// - Parameter args: Positional or keyword arguments for the Python callable. @discardableResult public func dynamicallyCall( withKeywordArguments args: KeyValuePairs = [:]) throws -> PythonObject { return try _dynamicallyCall(args) } /// Alias for the function above that lets the caller dynamically construct the argument list, without using a dictionary literal. /// This function must be called explicitly on a `PythonObject` because `@dynamicCallable` does not recognize it. @discardableResult public func dynamicallyCall( withKeywordArguments args: [(key: String, value: PythonConvertible)] = []) throws -> PythonObject { return try _dynamicallyCall(args) } /// Implementation of `dynamicallyCall(withKeywordArguments)`. private func _dynamicallyCall(_ args: T) throws -> PythonObject where T.Element == (key: String, value: PythonConvertible) { try throwPythonErrorIfPresent() // An array containing positional arguments. var positionalArgs: [PythonObject] = [] // A dictionary object for storing keyword arguments, if any exist. var kwdictObject: OwnedPyObjectPointer? = nil for (key, value) in args { if key.isEmpty { positionalArgs.append(value.pythonObject) continue } // Initialize dictionary object if necessary. if kwdictObject == nil { kwdictObject = PyDict_New()! } // Add key-value pair to the dictionary object. // TODO: Handle duplicate keys. // In Python, `SyntaxError: keyword argument repeated` is thrown. let k = PythonObject(key).ownedPyObject let v = value.ownedPyObject PyDict_SetItem(kwdictObject, k, v) Py_DecRef(k) Py_DecRef(v) } defer { Py_DecRef(kwdictObject) } // Py_DecRef is `nil` safe. // Positional arguments are passed as a tuple of objects. let argTuple = pyTuple(positionalArgs) defer { Py_DecRef(argTuple) } // Python calls always return a non-null object when successful. If the // Python function produces the equivalent of C `void`, it returns the // `None` object. A `null` result of `PyObjectCall` happens when there is an // error, like `self` not being a Python callable. let selfObject = base.ownedPyObject defer { Py_DecRef(selfObject) } guard let result = PyObject_Call(selfObject, argTuple, kwdictObject) else { // If a Python exception was thrown, throw a corresponding Swift error. try throwPythonErrorIfPresent() throw PythonError.invalidCall(base) } return PythonObject(consuming: result) } /// Converts to a 2-tuple, if possible. public var tuple2: (PythonObject, PythonObject)? { let ct = base.checking guard let elt0 = ct[0], let elt1 = ct[1] else { return nil } return (elt0, elt1) } /// Converts to a 3-tuple, if possible. public var tuple3: (PythonObject, PythonObject, PythonObject)? { let ct = base.checking guard let elt0 = ct[0], let elt1 = ct[1], let elt2 = ct[2] else { return nil } return (elt0, elt1, elt2) } /// Converts to a 4-tuple, if possible. public var tuple4: (PythonObject, PythonObject, PythonObject, PythonObject)? { let ct = base.checking guard let elt0 = ct[0], let elt1 = ct[1], let elt2 = ct[2], let elt3 = ct[3] else { return nil } return (elt0, elt1, elt2, elt3) } public var count: Int? { base.checking.count } } //===----------------------------------------------------------------------===// // `PythonObject` member access implementation //===----------------------------------------------------------------------===// public extension PythonObject { /// Returns a `PythonObject` wrapper capable of member accesses. var checking: CheckingPythonObject { return CheckingPythonObject(self) } } /// A `PythonObject` wrapper that enables member accesses. /// Member access operations return an `Optional` result. When member access /// fails, `nil` is returned. @dynamicMemberLookup public struct CheckingPythonObject { /// The underlying `PythonObject`. private var base: PythonObject fileprivate init(_ base: PythonObject) { self.base = base } public subscript(dynamicMember name: String) -> PythonObject? { get { let selfObject = base.ownedPyObject defer { Py_DecRef(selfObject) } guard let result = PyObject_GetAttrString(selfObject, name) else { PyErr_Clear() return nil } // `PyObject_GetAttrString` returns +1 result. return PythonObject(consuming: result) } } /// Access the element corresponding to the specified `PythonConvertible` /// values representing a key. /// - Note: This is equivalent to `object[key]` in Python. public subscript(key: [PythonConvertible]) -> PythonObject? { get { let keyObject = flattenedSubscriptIndices(key) let selfObject = base.ownedPyObject defer { Py_DecRef(keyObject) Py_DecRef(selfObject) } // `PyObject_GetItem` returns +1 reference. if let result = PyObject_GetItem(selfObject, keyObject) { return PythonObject(consuming: result) } PyErr_Clear() return nil } nonmutating set { let keyObject = flattenedSubscriptIndices(key) let selfObject = base.ownedPyObject defer { Py_DecRef(keyObject) Py_DecRef(selfObject) } if let newValue = newValue { let newValueObject = newValue.ownedPyObject PyObject_SetItem(selfObject, keyObject, newValueObject) Py_DecRef(newValueObject) } else { // Assigning `nil` deletes the key, just like Swift dictionaries. PyObject_DelItem(selfObject, keyObject) } } } /// Access the element corresponding to the specified `PythonConvertible` /// values representing a key. /// - Note: This is equivalent to `object[key]` in Python. public subscript(key: PythonConvertible...) -> PythonObject? { get { return self[key] } nonmutating set { self[key] = newValue } } /// Converts to a 2-tuple, if possible. public var tuple2: (PythonObject, PythonObject)? { guard let elt0 = self[0], let elt1 = self[1] else { return nil } return (elt0, elt1) } /// Converts to a 3-tuple, if possible. public var tuple3: (PythonObject, PythonObject, PythonObject)? { guard let elt0 = self[0], let elt1 = self[1], let elt2 = self[2] else { return nil } return (elt0, elt1, elt2) } /// Converts to a 4-tuple, if possible. public var tuple4: (PythonObject, PythonObject, PythonObject, PythonObject)? { guard let elt0 = self[0], let elt1 = self[1], let elt2 = self[2], let elt3 = self[3] else { return nil } return (elt0, elt1, elt2, elt3) } public var count: Int? { Int(Python.len(base)) } } //===----------------------------------------------------------------------===// // Core `PythonObject` API //===----------------------------------------------------------------------===// /// Converts an array of indices into a `PythonObject` representing a flattened /// index. private func flattenedSubscriptIndices( _ indices: [PythonConvertible]) -> OwnedPyObjectPointer { if indices.count == 1 { return indices[0].ownedPyObject } return pyTuple(indices.map { $0.pythonObject }) } public extension PythonObject { subscript(dynamicMember memberName: String) -> PythonObject { get { guard let member = checking[dynamicMember: memberName] else { fatalError("Could not access PythonObject member '\(memberName)'") } return member } nonmutating set { let selfObject = ownedPyObject defer { Py_DecRef(selfObject) } let valueObject = newValue.ownedPyObject defer { Py_DecRef(valueObject) } if PyObject_SetAttrString(selfObject, memberName, valueObject) == -1 { try! throwPythonErrorIfPresent() fatalError(""" Could not set PythonObject member '\(memberName)' to the specified \ value """) } } } /// Access the element corresponding to the specified `PythonConvertible` /// values representing a key. /// - Note: This is equivalent to `object[key]` in Python. subscript(key: PythonConvertible...) -> PythonObject { get { guard let item = checking[key] else { fatalError(""" Could not access PythonObject element corresponding to the specified \ key values: \(key) """) } return item } nonmutating set { checking[key] = newValue } } /// Converts to a 2-tuple. var tuple2: (PythonObject, PythonObject) { guard let result = checking.tuple2 else { fatalError("Could not convert PythonObject to a 2-element tuple") } return result } /// Converts to a 3-tuple. var tuple3: (PythonObject, PythonObject, PythonObject) { guard let result = checking.tuple3 else { fatalError("Could not convert PythonObject to a 3-element tuple") } return result } /// Converts to a 4-tuple. var tuple4: (PythonObject, PythonObject, PythonObject, PythonObject) { guard let result = checking.tuple4 else { fatalError("Could not convert PythonObject to a 4-element tuple") } return result } /// Call `self` with the specified positional arguments. /// - Precondition: `self` must be a Python callable. /// - Parameter args: Positional arguments for the Python callable. @discardableResult func dynamicallyCall( withArguments args: [PythonConvertible] = []) -> PythonObject { return try! throwing.dynamicallyCall(withArguments: args) } /// Call `self` with the specified arguments. /// - Precondition: `self` must be a Python callable. /// - Parameter args: Positional or keyword arguments for the Python callable. @discardableResult func dynamicallyCall( withKeywordArguments args: KeyValuePairs = [:]) -> PythonObject { return try! throwing.dynamicallyCall(withKeywordArguments: args) } /// Alias for the function above that lets the caller dynamically construct the argument list, without using a dictionary literal. /// This function must be called explicitly on a `PythonObject` because `@dynamicCallable` does not recognize it. @discardableResult func dynamicallyCall( withKeywordArguments args: [(key: String, value: PythonConvertible)] = []) -> PythonObject { return try! throwing.dynamicallyCall(withKeywordArguments: args) } } //===----------------------------------------------------------------------===// // Python interface implementation //===----------------------------------------------------------------------===// /// The global Python interface. /// /// You can import Python modules and access Python builtin types and functions /// via the `Python` global variable. /// /// import Python /// // Import modules. /// let os = Python.import("os") /// let np = Python.import("numpy") /// /// // Use builtin types and functions. /// let list: PythonObject = [1, 2, 3] /// print(Python.len(list)) // Prints 3. /// print(Python.type(list) == Python.list) // Prints true. @_fixed_layout public let Python = PythonInterface() /// An interface for Python. /// /// `PythonInterface` allows interaction with Python. It can be used to import /// modules and dynamically access Python builtin types and functions. /// - Note: It is not intended for `PythonInterface` to be initialized /// directly. Instead, please use the global instance of `PythonInterface` /// called `Python`. @dynamicMemberLookup public struct PythonInterface { /// A dictionary of the Python builtins. public let builtins: PythonObject init() { Py_Initialize() // Initialize Python builtins = PythonObject(PyEval_GetBuiltins()) // Runtime Fixes: PyRun_SimpleString(""" import sys import os # Some Python modules expect to have at least one argument in `sys.argv`: sys.argv = [""] # Some Python modules require `sys.executable` to return the path # to the Python interpreter executable. In Darwin, Python 3 returns the # main process executable path instead: if sys.version_info.major == 3 and sys.platform == "darwin": executable_name = "python{}.{}".format(sys.version_info.major, sys.version_info.minor) sys.executable = os.path.join(sys.exec_prefix, "bin", executable_name) """) } public func attemptImport(_ name: String) throws -> PythonObject { guard let module = PyImport_ImportModule(name) else { try throwPythonErrorIfPresent() throw PythonError.invalidModule(name) } return PythonObject(consuming: module) } public func `import`(_ name: String) -> PythonObject { return try! attemptImport(name) } public subscript(dynamicMember name: String) -> PythonObject { return builtins[name] } // The Python runtime version. // Equivalent to `sys.version` in Python. public var version: PythonObject { return self.import("sys").version } // The Python runtime version information. // Equivalent to `sys.version_info` in Python. public var versionInfo: PythonObject { return self.import("sys").version_info } /// Emulates a Python `with` statement. /// - Parameter object: A context manager object. /// - Parameter body: A closure to call on the result of `object.__enter__()`. public func with(_ object: PythonObject, _ body: (PythonObject) throws -> Void) rethrows { let yieldValue = object.__enter__() try body(yieldValue) yieldValue.__exit__() } } //===----------------------------------------------------------------------===// // Helpers for Python tuple types //===----------------------------------------------------------------------===// // Create a Python tuple object with the specified elements. private func pyTuple(_ vals: T) -> OwnedPyObjectPointer where T.Element : PythonConvertible { let tuple = PyTuple_New(vals.count)! for (index, element) in vals.enumerated() { // `PyTuple_SetItem` steals the reference of the object stored. PyTuple_SetItem(tuple, index, element.ownedPyObject) } return tuple } public extension PythonObject { // Tuples require explicit support because tuple types cannot conform to // protocols. init(tupleOf elements: PythonConvertible...) { self.init(tupleContentsOf: elements) } init(tupleContentsOf elements: T) where T.Element == PythonConvertible { self.init(consuming: pyTuple(elements.map { $0.pythonObject })) } init(tupleContentsOf elements: T) where T.Element : PythonConvertible { self.init(consuming: pyTuple(elements)) } } //===----------------------------------------------------------------------===// // `PythonConvertible` conformance for basic Swift types //===----------------------------------------------------------------------===// /// Return true if the specified objects an instance of the low-level Python /// type descriptor passed in as 'type'. private func isType(_ object: PythonObject, type: PyObjectPointer) -> Bool { let typePyRef = PythonObject(type) let result = Python.isinstance(object, typePyRef) // We cannot use the normal failable Bool initializer from `PythonObject` // here because would cause an infinite loop. let pyObject = result.ownedPyObject defer { Py_DecRef(pyObject) } // Anything not equal to `Py_ZeroStruct` is truthy. return pyObject != _Py_ZeroStruct } extension Bool : PythonConvertible, ConvertibleFromPython { public init?(_ pythonObject: PythonObject) { guard isType(pythonObject, type: PyBool_Type) else { return nil } let pyObject = pythonObject.ownedPyObject defer { Py_DecRef(pyObject) } self = pyObject == _Py_TrueStruct } public var pythonObject: PythonObject { _ = Python // Ensure Python is initialized. return PythonObject(consuming: PyBool_FromLong(self ? 1 : 0)) } } extension String : PythonConvertible, ConvertibleFromPython { public init?(_ pythonObject: PythonObject) { let pyObject = pythonObject.ownedPyObject defer { Py_DecRef(pyObject) } guard let cString = PyString_AsString(pyObject) else { PyErr_Clear() return nil } self.init(cString: cString) } public var pythonObject: PythonObject { _ = Python // Ensure Python is initialized. let v = utf8CString.withUnsafeBufferPointer { // 1 is subtracted from the C string length to trim the trailing null // character (`\0`). PyString_FromStringAndSize($0.baseAddress, $0.count - 1)! } return PythonObject(consuming: v) } } fileprivate extension PythonObject { // Converts a `PythonObject` to the given type by applying the appropriate // converter function and checking the error value. func converted( withError errorValue: T, by converter: (OwnedPyObjectPointer) -> T ) -> T? { let pyObject = ownedPyObject defer { Py_DecRef(pyObject) } assert(PyErr_Occurred() == nil, "Python error occurred somewhere but wasn't handled") let value = converter(pyObject) guard value != errorValue || PyErr_Occurred() == nil else { PyErr_Clear() return nil } return value } } extension Int : PythonConvertible, ConvertibleFromPython { public init?(_ pythonObject: PythonObject) { // `PyInt_AsLong` return -1 and sets an error if the Python object is not // integer compatible. guard let value = pythonObject.converted( withError: -1, by: PyInt_AsLong) else { return nil } self = value } public var pythonObject: PythonObject { _ = Python // Ensure Python is initialized. return PythonObject(consuming: PyInt_FromLong(self)) } } extension UInt : PythonConvertible, ConvertibleFromPython { public init?(_ pythonObject: PythonObject) { // `PyInt_AsUnsignedLongMask` isn't documented as such, but in fact it does // return -1 and set an error if the Python object is not integer // compatible. guard let value = pythonObject.converted( withError: ~0, by: PyInt_AsUnsignedLongMask) else { return nil } self = value } public var pythonObject: PythonObject { _ = Python // Ensure Python is initialized. return PythonObject(consuming: PyInt_FromSize_t(self)) } } extension Double : PythonConvertible, ConvertibleFromPython { public init?(_ pythonObject: PythonObject) { // `PyFloat_AsDouble` return -1 and sets an error if the Python object is // not float compatible. guard let value = pythonObject.converted( withError: -1, by: PyFloat_AsDouble) else { return nil } self = value } public var pythonObject: PythonObject { _ = Python // Ensure Python is initialized. return PythonObject(consuming: PyFloat_FromDouble(self)) } } //===----------------------------------------------------------------------===// // `PythonConvertible` conformances for `FixedWidthInteger` and `Float` //===----------------------------------------------------------------------===// // Any `FixedWidthInteger` type is `PythonConvertible` via the `Int`/`UInt` // implementation. extension Int8 : PythonConvertible, ConvertibleFromPython { public init?(_ pythonObject: PythonObject) { guard let i = Int(pythonObject) else { return nil } self.init(i) } public var pythonObject: PythonObject { return Int(self).pythonObject } } extension Int16 : PythonConvertible, ConvertibleFromPython { public init?(_ pythonObject: PythonObject) { guard let i = Int(pythonObject) else { return nil } self.init(i) } public var pythonObject: PythonObject { return Int(self).pythonObject } } extension Int32 : PythonConvertible, ConvertibleFromPython { public init?(_ pythonObject: PythonObject) { guard let i = Int(pythonObject) else { return nil } self.init(i) } public var pythonObject: PythonObject { return Int(self).pythonObject } } extension Int64 : PythonConvertible, ConvertibleFromPython { public init?(_ pythonObject: PythonObject) { guard let i = Int(pythonObject) else { return nil } self.init(i) } public var pythonObject: PythonObject { return Int(self).pythonObject } } extension UInt8 : PythonConvertible, ConvertibleFromPython { public init?(_ pythonObject: PythonObject) { guard let i = UInt(pythonObject) else { return nil } self.init(i) } public var pythonObject: PythonObject { return UInt(self).pythonObject } } extension UInt16 : PythonConvertible, ConvertibleFromPython { public init?(_ pythonObject: PythonObject) { guard let i = UInt(pythonObject) else { return nil } self.init(i) } public var pythonObject: PythonObject { return UInt(self).pythonObject } } extension UInt32 : PythonConvertible, ConvertibleFromPython { public init?(_ pythonObject: PythonObject) { guard let i = UInt(pythonObject) else { return nil } self.init(i) } public var pythonObject: PythonObject { return UInt(self).pythonObject } } extension UInt64 : PythonConvertible, ConvertibleFromPython { public init?(_ pythonObject: PythonObject) { guard let i = UInt(pythonObject) else { return nil } self.init(i) } public var pythonObject: PythonObject { return UInt(self).pythonObject } } // `Float` is `PythonConvertible` via the `Double` implementation. extension Float : PythonConvertible, ConvertibleFromPython { public init?(_ pythonObject: PythonObject) { guard let v = Double(pythonObject) else { return nil } self.init(v) } public var pythonObject: PythonObject { return Double(self).pythonObject } } //===----------------------------------------------------------------------===// // `PythonConvertible` conformance for `Optional` //===----------------------------------------------------------------------===// extension Optional : PythonConvertible where Wrapped : PythonConvertible { public var pythonObject: PythonObject { return self?.pythonObject ?? Python.None } } //===----------------------------------------------------------------------===// // `ConvertibleFromPython` conformance for `Optional` //===----------------------------------------------------------------------===// extension Optional : ConvertibleFromPython where Wrapped : ConvertibleFromPython { public init?(_ object: PythonObject) { if object == Python.None { self = .none } else { guard let converted = Wrapped(object) else { return nil } self = .some(converted) } } } //===----------------------------------------------------------------------===// // `PythonConvertible` and `ConvertibleFromPython conformance for // `Array` and `Dictionary` //===----------------------------------------------------------------------===// // `Array` conditionally conforms to `PythonConvertible` if the `Element` // associated type does. extension Array : PythonConvertible where Element : PythonConvertible { public var pythonObject: PythonObject { _ = Python // Ensure Python is initialized. let list = PyList_New(count)! for (index, element) in enumerated() { // `PyList_SetItem` steals the reference of the object stored. _ = PyList_SetItem(list, index, element.ownedPyObject) } return PythonObject(consuming: list) } } extension Array : ConvertibleFromPython where Element : ConvertibleFromPython { public init?(_ pythonObject: PythonObject) { self = [] for elementObject in pythonObject { guard let element = Element(elementObject) else { return nil } append(element) } } } // `Dictionary` conditionally conforms to `PythonConvertible` if the `Key` and // `Value` associated types do. extension Dictionary : PythonConvertible where Key : PythonConvertible, Value : PythonConvertible { public var pythonObject: PythonObject { _ = Python // Ensure Python is initialized. let dict = PyDict_New()! for (key, value) in self { let k = key.ownedPyObject let v = value.ownedPyObject PyDict_SetItem(dict, k, v) Py_DecRef(k) Py_DecRef(v) } return PythonObject(consuming: dict) } } extension Dictionary : ConvertibleFromPython where Key : ConvertibleFromPython, Value : ConvertibleFromPython { public init?(_ pythonDict: PythonObject) { self = [:] // Iterate over the Python dictionary, converting its keys and values to // Swift `Key` and `Value` pairs. var key, value: PyObjectPointer? var position: Int = 0 while PyDict_Next( pythonDict.borrowedPyObject, &position, &key, &value) != 0 { // If any key or value is not convertible to the corresponding Swift // type, then the entire dictionary is not convertible. if let swiftKey = Key(PythonObject(key!)), let swiftValue = Value(PythonObject(value!)) { // It is possible that there are duplicate keys after conversion. We // silently allow duplicate keys and pick a nondeterministic result if // there is a collision. self[swiftKey] = swiftValue } else { return nil } } } } //===----------------------------------------------------------------------===// // `PythonConvertible` and `ConvertibleFromPython` conformances // for `Range` types //===----------------------------------------------------------------------===// extension Range : PythonConvertible where Bound : PythonConvertible { public var pythonObject: PythonObject { _ = Python // Ensure Python is initialized. return Python.slice(lowerBound, upperBound, Python.None) } } extension Range : ConvertibleFromPython where Bound : ConvertibleFromPython { public init?(_ pythonObject: PythonObject) { guard isType(pythonObject, type: PySlice_Type) else { return nil } guard let lowerBound = Bound(pythonObject.start), let upperBound = Bound(pythonObject.stop) else { return nil } guard pythonObject.step == Python.None else { return nil } self.init(uncheckedBounds: (lowerBound, upperBound)) } } extension PartialRangeFrom : PythonConvertible where Bound : PythonConvertible { public var pythonObject: PythonObject { _ = Python // Ensure Python is initialized. return Python.slice(lowerBound, Python.None, Python.None) } } extension PartialRangeFrom : ConvertibleFromPython where Bound : ConvertibleFromPython { public init?(_ pythonObject: PythonObject) { guard isType(pythonObject, type: PySlice_Type) else { return nil } guard let lowerBound = Bound(pythonObject.start) else { return nil } guard pythonObject.stop == Python.None, pythonObject.step == Python.None else { return nil } self.init(lowerBound) } } extension PartialRangeUpTo : PythonConvertible where Bound : PythonConvertible { public var pythonObject: PythonObject { _ = Python // Ensure Python is initialized. return Python.slice(Python.None, upperBound, Python.None) } } extension PartialRangeUpTo : ConvertibleFromPython where Bound : ConvertibleFromPython { public init?(_ pythonObject: PythonObject) { guard isType(pythonObject, type: PySlice_Type) else { return nil } guard let upperBound = Bound(pythonObject.stop) else { return nil } guard pythonObject.start == Python.None, pythonObject.step == Python.None else { return nil } self.init(upperBound) } } //===----------------------------------------------------------------------===// // Standard operators and conformances //===----------------------------------------------------------------------===// private typealias PythonBinaryOp = (OwnedPyObjectPointer?, OwnedPyObjectPointer?) -> OwnedPyObjectPointer? private typealias PythonUnaryOp = (OwnedPyObjectPointer?) -> OwnedPyObjectPointer? private func performBinaryOp( _ op: PythonBinaryOp, lhs: PythonObject, rhs: PythonObject) -> PythonObject { let result = op(lhs.borrowedPyObject, rhs.borrowedPyObject) // If binary operation fails (e.g. due to `TypeError`), throw an exception. try! throwPythonErrorIfPresent() return PythonObject(consuming: result!) } private func performUnaryOp( _ op: PythonUnaryOp, operand: PythonObject) -> PythonObject { let result = op(operand.borrowedPyObject) // If unary operation fails (e.g. due to `TypeError`), throw an exception. try! throwPythonErrorIfPresent() return PythonObject(consuming: result!) } public extension PythonObject { static func + (lhs: PythonObject, rhs: PythonObject) -> PythonObject { return performBinaryOp(PyNumber_Add, lhs: lhs, rhs: rhs) } static func - (lhs: PythonObject, rhs: PythonObject) -> PythonObject { return performBinaryOp(PyNumber_Subtract, lhs: lhs, rhs: rhs) } static func * (lhs: PythonObject, rhs: PythonObject) -> PythonObject { return performBinaryOp(PyNumber_Multiply, lhs: lhs, rhs: rhs) } static func / (lhs: PythonObject, rhs: PythonObject) -> PythonObject { return performBinaryOp(PyNumber_TrueDivide, lhs: lhs, rhs: rhs) } static func += (lhs: inout PythonObject, rhs: PythonObject) { lhs = performBinaryOp(PyNumber_InPlaceAdd, lhs: lhs, rhs: rhs) } static func -= (lhs: inout PythonObject, rhs: PythonObject) { lhs = performBinaryOp(PyNumber_InPlaceSubtract, lhs: lhs, rhs: rhs) } static func *= (lhs: inout PythonObject, rhs: PythonObject) { lhs = performBinaryOp(PyNumber_InPlaceMultiply, lhs: lhs, rhs: rhs) } static func /= (lhs: inout PythonObject, rhs: PythonObject) { lhs = performBinaryOp(PyNumber_InPlaceTrueDivide, lhs: lhs, rhs: rhs) } } public extension PythonObject { static func & (lhs: PythonObject, rhs: PythonObject) -> PythonObject { return performBinaryOp(PyNumber_And, lhs: lhs, rhs: rhs) } static func | (lhs: PythonObject, rhs: PythonObject) -> PythonObject { return performBinaryOp(PyNumber_Or, lhs: lhs, rhs: rhs) } static func ^ (lhs: PythonObject, rhs: PythonObject) -> PythonObject { return performBinaryOp(PyNumber_Xor, lhs: lhs, rhs: rhs) } static func &= (lhs: inout PythonObject, rhs: PythonObject) { lhs = performBinaryOp(PyNumber_InPlaceAnd, lhs: lhs, rhs: rhs) } static func |= (lhs: inout PythonObject, rhs: PythonObject) { lhs = performBinaryOp(PyNumber_InPlaceOr, lhs: lhs, rhs: rhs) } static func ^= (lhs: inout PythonObject, rhs: PythonObject) { lhs = performBinaryOp(PyNumber_InPlaceXor, lhs: lhs, rhs: rhs) } static prefix func ~ (_ operand: Self) -> Self { return performUnaryOp(PyNumber_Invert, operand: operand) } } extension PythonObject : SignedNumeric { public init(exactly value: T) { self.init(Int(value)) } public typealias Magnitude = PythonObject public var magnitude: PythonObject { return self < 0 ? -self : self } // Override the default implementation of `-` prefix function // from SignedNumeric (https://bugs.swift.org/browse/SR-13293). public static prefix func - (_ operand: Self) -> Self { return performUnaryOp(PyNumber_Negative, operand: operand) } } extension PythonObject : Strideable { public typealias Stride = PythonObject public func distance(to other: PythonObject) -> Stride { return other - self } public func advanced(by stride: Stride) -> PythonObject { return self + stride } } extension PythonObject : Equatable, Comparable { // `Equatable` and `Comparable` are implemented using rich comparison. // This is consistent with how Python handles comparisons. private func compared(to other: PythonObject, byOp: Int32) -> Bool { let lhsObject = ownedPyObject let rhsObject = other.ownedPyObject defer { Py_DecRef(lhsObject) Py_DecRef(rhsObject) } assert(PyErr_Occurred() == nil, "Python error occurred somewhere but wasn't handled") switch PyObject_RichCompareBool(lhsObject, rhsObject, byOp) { case 0: return false case 1: return true default: try! throwPythonErrorIfPresent() fatalError("No result or error returned when comparing \(self) to \(other)") } } public static func == (lhs: PythonObject, rhs: PythonObject) -> Bool { return lhs.compared(to: rhs, byOp: Py_EQ) } public static func != (lhs: PythonObject, rhs: PythonObject) -> Bool { return lhs.compared(to: rhs, byOp: Py_NE) } public static func < (lhs: PythonObject, rhs: PythonObject) -> Bool { return lhs.compared(to: rhs, byOp: Py_LT) } public static func <= (lhs: PythonObject, rhs: PythonObject) -> Bool { return lhs.compared(to: rhs, byOp: Py_LE) } public static func > (lhs: PythonObject, rhs: PythonObject) -> Bool { return lhs.compared(to: rhs, byOp: Py_GT) } public static func >= (lhs: PythonObject, rhs: PythonObject) -> Bool { return lhs.compared(to: rhs, byOp: Py_GE) } } public extension PythonObject { private func compared(to other: PythonObject, byOp: Int32) -> PythonObject { let lhsObject = ownedPyObject let rhsObject = other.ownedPyObject defer { Py_DecRef(lhsObject) Py_DecRef(rhsObject) } assert(PyErr_Occurred() == nil, "Python error occurred somewhere but wasn't handled") guard let result = PyObject_RichCompare(lhsObject, rhsObject, byOp) else { // If a Python exception was thrown, throw a corresponding Swift error. try! throwPythonErrorIfPresent() fatalError("No result or error returned when comparing \(self) to \(other)") } return PythonObject(consuming: result) } static func == (lhs: PythonObject, rhs: PythonObject) -> PythonObject { return lhs.compared(to: rhs, byOp: Py_EQ) } static func != (lhs: PythonObject, rhs: PythonObject) -> PythonObject { return lhs.compared(to: rhs, byOp: Py_NE) } static func < (lhs: PythonObject, rhs: PythonObject) -> PythonObject { return lhs.compared(to: rhs, byOp: Py_LT) } static func <= (lhs: PythonObject, rhs: PythonObject) -> PythonObject { return lhs.compared(to: rhs, byOp: Py_LE) } static func > (lhs: PythonObject, rhs: PythonObject) -> PythonObject { return lhs.compared(to: rhs, byOp: Py_GT) } static func >= (lhs: PythonObject, rhs: PythonObject) -> PythonObject { return lhs.compared(to: rhs, byOp: Py_GE) } } extension PythonObject : Hashable { public func hash(into hasher: inout Hasher) { guard let hash = Int(self.__hash__()) else { fatalError("Cannot use '__hash__' on \(self)") } hasher.combine(hash) } } extension PythonObject : MutableCollection { public typealias Index = PythonObject public typealias Element = PythonObject public var startIndex: Index { return 0 } public var endIndex: Index { return Python.len(self) } public func index(after i: Index) -> Index { return i + PythonObject(1) } public subscript(index: PythonObject) -> PythonObject { get { return self[index as PythonConvertible] } set { self[index as PythonConvertible] = newValue } } } extension PythonObject : Sequence { public struct Iterator : IteratorProtocol { fileprivate let pythonIterator: PythonObject public func next() -> PythonObject? { guard let result = PyIter_Next(self.pythonIterator.borrowedPyObject) else { try! throwPythonErrorIfPresent() return nil } return PythonObject(consuming: result) } } public func makeIterator() -> Iterator { guard let result = PyObject_GetIter(borrowedPyObject) else { try! throwPythonErrorIfPresent() // Unreachable. A Python `TypeError` must have been thrown. preconditionFailure() } return Iterator(pythonIterator: PythonObject(consuming: result)) } } extension PythonObject { public var count: Int { checking.count! } } //===----------------------------------------------------------------------===// // `ExpressibleByLiteral` conformances //===----------------------------------------------------------------------===// extension PythonObject : ExpressibleByBooleanLiteral, ExpressibleByIntegerLiteral, ExpressibleByFloatLiteral, ExpressibleByStringLiteral { public init(booleanLiteral value: Bool) { self.init(value) } public init(integerLiteral value: Int) { self.init(value) } public init(floatLiteral value: Double) { self.init(value) } public init(stringLiteral value: String) { self.init(value) } } extension PythonObject : ExpressibleByArrayLiteral, ExpressibleByDictionaryLiteral { public init(arrayLiteral elements: PythonObject...) { self.init(elements) } public typealias Key = PythonObject public typealias Value = PythonObject // Preserves element order in the final Python object, unlike // `Dictionary.pythonObject`. When keys are duplicated, throw the same // runtime error as `Swift.Dictionary.init(dictionaryLiteral:)`. This // differs from Python's key uniquing semantics, which silently override an // existing key with the next one it encounters. public init(dictionaryLiteral elements: (PythonObject, PythonObject)...) { _ = Python // Ensure Python is initialized. let dict = PyDict_New()! for (key, value) in elements { let k = key.ownedPyObject let v = value.ownedPyObject // Use Python's native key checking instead of querying whether // `elements` contains the key. Although this could theoretically // produce different results, it produces the Python object we want. switch PyDict_Contains(dict, k) { case 0: PyDict_SetItem(dict, k, v) case 1: fatalError("Dictionary literal contains duplicate keys") default: try! throwPythonErrorIfPresent() fatalError("No result or error checking whether \(elements) contains \(key)") } Py_DecRef(k) Py_DecRef(v) } self.init(consuming: dict) } } public struct PythonBytes : PythonConvertible, ConvertibleFromPython, Hashable { public private(set) var pythonObject: PythonObject public init?(_ pythonObject: PythonObject) { // We try to get the string/size pointers out. If it works, hooray, this is a bytes // otherwise it isn't. let pyObject = pythonObject.ownedPyObject defer { Py_DecRef(pyObject) } var length = 0 var buffer: UnsafeMutablePointer? = nil switch PyBytes_AsStringAndSize(pyObject, &buffer, &length) { case 0: self.pythonObject = pythonObject default: return nil } } @inlinable public init(_ bytes: Bytes) where Bytes.Element == UInt8 { let possibleSelf = bytes.withContiguousStorageIfAvailable { storagePtr in PythonBytes.fromBytePointer(storagePtr) } if let actualSelf = possibleSelf { self = actualSelf } else { let temporaryBuffer = Array(bytes) self = temporaryBuffer.withUnsafeBufferPointer { PythonBytes.fromBytePointer($0) } } } @inlinable public init(_ bytes: Bytes) where Bytes.Element == Int8 { let possibleSelf = bytes.withContiguousStorageIfAvailable { storagePtr in PythonBytes.fromBytePointer(storagePtr) } if let actualSelf = possibleSelf { self = actualSelf } else { let temporaryBuffer = Array(bytes) self = temporaryBuffer.withUnsafeBufferPointer { PythonBytes.fromBytePointer($0) } } } private init(bytesObject: PythonObject) { self.pythonObject = bytesObject } @usableFromInline static func fromBytePointer(_ bytes: UnsafeBufferPointer) -> PythonBytes { bytes.withMemoryRebound(to: Int8.self) { reboundPtr in PythonBytes.fromBytePointer(reboundPtr) } } @usableFromInline static func fromBytePointer(_ bytes: UnsafeBufferPointer) -> PythonBytes { let v = PyBytes_FromStringAndSize(bytes.baseAddress, bytes.count)! return PythonBytes(bytesObject: PythonObject(consuming: v)) } public func withUnsafeBytes( _ callback: (UnsafeRawBufferPointer) throws -> ReturnValue ) rethrows -> ReturnValue { let pyObject = self.pythonObject.ownedPyObject defer { Py_DecRef(pyObject) } var length = 0 var buffer: UnsafeMutablePointer? = nil switch PyBytes_AsStringAndSize(pyObject, &buffer, &length) { case 0: let buffer = UnsafeRawBufferPointer(start: buffer, count: length) return try callback(buffer) default: try! throwPythonErrorIfPresent() fatalError("No result or error getting interior buffer for bytes \(self)") } } } //===----------------------------------------------------------------------===// // PythonFunction - create functions in Swift that can be called from Python //===----------------------------------------------------------------------===// /// Create functions in Swift that can be called from Python /// /// Example: /// /// The Python code `map(lambda(x: x * 2), [10, 12, 14])` would be written as: /// /// Python.map(PythonFunction { x in x * 2 }, [10, 12, 14]) // [20, 24, 28] /// final class PyFunction { enum CallingConvention { case varArgs case varArgsWithKeywords } /// Allows `PyFunction` to store Python functions with more than one possible calling convention var callingConvention: CallingConvention /// `arguments` is a Python tuple. typealias VarArgsFunction = ( _ arguments: PythonObject) throws -> PythonConvertible /// `arguments` is a Python tuple. /// `keywordArguments` is an OrderedDict in Python 3.6 and later, or a dict otherwise. typealias VarArgsWithKeywordsFunction = ( _ arguments: PythonObject, _ keywordArguments: PythonObject) throws -> PythonConvertible /// Has the same memory layout as any other function with the Swift calling convention private typealias Storage = () throws -> PythonConvertible /// Stores all function pointers in the same stored property. `callAsFunction` casts this into the desired type. private var callSwiftFunction: Storage init(_ callSwiftFunction: @escaping VarArgsFunction) { self.callingConvention = .varArgs self.callSwiftFunction = unsafeBitCast(callSwiftFunction, to: Storage.self) } init(_ callSwiftFunction: @escaping VarArgsWithKeywordsFunction) { self.callingConvention = .varArgsWithKeywords self.callSwiftFunction = unsafeBitCast(callSwiftFunction, to: Storage.self) } private func checkConvention(_ calledConvention: CallingConvention) { precondition(callingConvention == calledConvention, "Called PyFunction with convention \(calledConvention), but expected \(callingConvention)") } func callAsFunction(_ argumentsTuple: PythonObject) throws -> PythonConvertible { checkConvention(.varArgs) let callSwiftFunction = unsafeBitCast(self.callSwiftFunction, to: VarArgsFunction.self) return try callSwiftFunction(argumentsTuple) } func callAsFunction(_ argumentsTuple: PythonObject, _ keywordArguments: PythonObject) throws -> PythonConvertible { checkConvention(.varArgsWithKeywords) let callSwiftFunction = unsafeBitCast(self.callSwiftFunction, to: VarArgsWithKeywordsFunction.self) return try callSwiftFunction(argumentsTuple, keywordArguments) } } public struct PythonFunction { /// Called directly by the Python C API private var function: PyFunction @_disfavoredOverload public init(_ fn: @escaping (PythonObject) throws -> PythonConvertible) { function = PyFunction { argumentsAsTuple in return try fn(argumentsAsTuple[0]) } } /// For cases where the Swift function should accept more (or less) than one parameter, accept an ordered array of all arguments instead. public init(_ fn: @escaping ([PythonObject]) throws -> PythonConvertible) { function = PyFunction { argumentsAsTuple in return try fn(argumentsAsTuple.map { $0 }) } } /// For cases where the Swift function should accept keyword arguments as `**kwargs` in Python. /// `**kwargs` must preserve order from Python 3.6 onward, similarly to /// Swift `KeyValuePairs` and unlike `Dictionary`. `KeyValuePairs` cannot be /// mutated, so the next best solution is to use `[KeyValuePairs.Element]`. public init(_ fn: @escaping ([PythonObject], [(key: String, value: PythonObject)]) throws -> PythonConvertible) { function = PyFunction { argumentsAsTuple, keywordArgumentsAsDictionary in var kwargs: [(String, PythonObject)] = [] for keyAndValue in keywordArgumentsAsDictionary.items() { let (key, value) = keyAndValue.tuple2 kwargs.append((String(key)!, value)) } return try fn(argumentsAsTuple.map { $0 }, kwargs) } } } extension PythonFunction : PythonConvertible { public var pythonObject: PythonObject { // Ensure Python is initialized, and check for version match. let versionMajor = Python.versionInfo.major let versionMinor = Python.versionInfo.minor guard (versionMajor == 3 && versionMinor >= 1) || versionMajor > 3 else { fatalError("PythonFunction only supports Python 3.1 and above.") } let destructor: @convention(c) (PyObjectPointer?) -> Void = { capsulePointer in let funcPointer = PyCapsule_GetPointer(capsulePointer, nil) Unmanaged.fromOpaque(funcPointer).release() } let funcPointer = Unmanaged.passRetained(function).toOpaque() let capsulePointer = PyCapsule_New( funcPointer, nil, unsafeBitCast(destructor, to: OpaquePointer.self) ) var methodDefinition: UnsafeMutablePointer switch function.callingConvention { case .varArgs: methodDefinition = PythonFunction.sharedMethodDefinition case .varArgsWithKeywords: methodDefinition = PythonFunction.sharedMethodWithKeywordsDefinition } let pyFuncPointer = PyCFunction_NewEx( methodDefinition, capsulePointer, nil ) return PythonObject(consuming: pyFuncPointer) } } fileprivate extension PythonFunction { static let sharedMethodDefinition: UnsafeMutablePointer = { let name: StaticString = "pythonkit_swift_function" // `utf8Start` is a property of StaticString, thus, it has a stable pointer. let namePointer = UnsafeRawPointer(name.utf8Start).assumingMemoryBound(to: Int8.self) let methodImplementationPointer = unsafeBitCast( PythonFunction.sharedMethodImplementation, to: OpaquePointer.self) /// The standard calling convention. See Python C API docs let METH_VARARGS = 0x0001 as Int32 let pointer = UnsafeMutablePointer.allocate(capacity: 1) pointer.pointee = PyMethodDef( ml_name: namePointer, ml_meth: methodImplementationPointer, ml_flags: METH_VARARGS, ml_doc: nil ) return pointer }() static let sharedMethodWithKeywordsDefinition: UnsafeMutablePointer = { let name: StaticString = "pythonkit_swift_function_with_keywords" // `utf8Start` is a property of StaticString, thus, it has a stable pointer. let namePointer = UnsafeRawPointer(name.utf8Start).assumingMemoryBound(to: Int8.self) let methodImplementationPointer = unsafeBitCast( PythonFunction.sharedMethodWithKeywordsImplementation, to: OpaquePointer.self) /// A combination of flags that supports `**kwargs`. See Python C API docs let METH_VARARGS = 0x0001 as Int32 let METH_KEYWORDS = 0x0002 as Int32 let pointer = UnsafeMutablePointer.allocate(capacity: 1) pointer.pointee = PyMethodDef( ml_name: namePointer, ml_meth: methodImplementationPointer, ml_flags: METH_VARARGS | METH_KEYWORDS, ml_doc: nil ) return pointer }() private static let sharedMethodImplementation: @convention(c) ( PyObjectPointer?, PyObjectPointer? ) -> PyObjectPointer? = { context, argumentsPointer in guard let argumentsPointer = argumentsPointer, let capsulePointer = context else { return nil } let funcPointer = PyCapsule_GetPointer(capsulePointer, nil) let function = Unmanaged.fromOpaque(funcPointer).takeUnretainedValue() do { let argumentsAsTuple = PythonObject(consuming: argumentsPointer) return try function(argumentsAsTuple).ownedPyObject } catch { PythonFunction.setPythonError(swiftError: error) return nil // This must only be `nil` if an exception has been set } } private static let sharedMethodWithKeywordsImplementation: @convention(c) ( PyObjectPointer?, PyObjectPointer?, PyObjectPointer? ) -> PyObjectPointer? = { context, argumentsPointer, keywordArgumentsPointer in guard let argumentsPointer = argumentsPointer, let capsulePointer = context else { return nil } let funcPointer = PyCapsule_GetPointer(capsulePointer, nil) let function = Unmanaged.fromOpaque(funcPointer).takeUnretainedValue() do { let argumentsAsTuple = PythonObject(consuming: argumentsPointer) var keywordArgumentsAsDictionary: PythonObject if let keywordArgumentsPointer = keywordArgumentsPointer { keywordArgumentsAsDictionary = PythonObject(consuming: keywordArgumentsPointer) } else { keywordArgumentsAsDictionary = [:] } return try function(argumentsAsTuple, keywordArgumentsAsDictionary).ownedPyObject } catch { PythonFunction.setPythonError(swiftError: error) return nil // This must only be `nil` if an exception has been set } } private static func setPythonError(swiftError: Error) { if let pythonObject = swiftError as? PythonObject { if Bool(Python.isinstance(pythonObject, Python.BaseException))! { // We are an instance of an Exception class type. Set the exception class to the object's type: PyErr_SetString(Python.type(pythonObject).ownedPyObject, pythonObject.description) } else { // Assume an actual class type was thrown (rather than an instance) // Crashes if it was neither a subclass of BaseException nor an instance of one. // // We *could* check to see whether `pythonObject` is a class here and fall back // to the default case of setting a generic Exception, below, but we also want // people to write valid code. PyErr_SetString(pythonObject.ownedPyObject, pythonObject.description) } } else { // Make a generic Python Exception based on the Swift Error: PyErr_SetString(Python.Exception.ownedPyObject, "\(type(of: swiftError)) raised in Swift: \(swiftError)") } } } extension PythonObject: Error {} // From Python's C Headers: struct PyMethodDef { /// The name of the built-in function/method var ml_name: UnsafePointer /// The C function that implements it. /// Since this accepts multiple function signatures, the Swift type must be opaque here. var ml_meth: OpaquePointer /// Combination of METH_xxx flags, which mostly describe the args expected by the C func var ml_flags: Int32 /// The __doc__ attribute, or NULL var ml_doc: UnsafePointer? } //===----------------------------------------------------------------------===// // PythonInstanceMethod - create functions that can be bound to a Python object //===----------------------------------------------------------------------===// public struct PythonInstanceMethod { private var function: PythonFunction @_disfavoredOverload public init(_ fn: @escaping (PythonObject) throws -> PythonConvertible) { function = PythonFunction(fn) } public init(_ fn: @escaping ([PythonObject]) throws -> PythonConvertible) { function = PythonFunction(fn) } public init(_ fn: @escaping ([PythonObject], [(key: String, value: PythonObject)]) throws -> PythonConvertible) { function = PythonFunction(fn) } } extension PythonInstanceMethod : PythonConvertible { public var pythonObject: PythonObject { let pyFuncPointer = function.pythonObject.ownedPyObject let methodPointer = PyInstanceMethod_New(pyFuncPointer) return PythonObject(consuming: methodPointer) } } //===----------------------------------------------------------------------===// // PythonClass - construct subclasses of a Python class //===----------------------------------------------------------------------===// public struct PythonClass { private var typeObject: PythonObject public struct Members: ExpressibleByDictionaryLiteral { public typealias Key = String public typealias Value = PythonConvertible var dictionary: [String: PythonObject] public init(dictionaryLiteral elements: (Key, Value)...) { let castedElements = elements.map { (key, value) in (key, value.pythonObject) } dictionary = Dictionary(castedElements, uniquingKeysWith: { _, _ in fatalError("Dictionary literal contains duplicate keys") }) } } public init(_ name: String, superclasses: [PythonObject] = [], members: Members = [:]) { self.init(name, superclasses: superclasses, members: members.dictionary) } @_disfavoredOverload public init(_ name: String, superclasses: [PythonObject] = [], members: [String: PythonObject] = [:]) { var trueSuperclasses = superclasses if !trueSuperclasses.contains(Python.object) { trueSuperclasses.append(Python.object) } let superclassesTuple = PythonObject(tupleContentsOf: trueSuperclasses) typeObject = Python.type(name, superclassesTuple, members.pythonObject) } } extension PythonClass : PythonConvertible { public var pythonObject: PythonObject { typeObject } }