diff --git a/PythonKit/NumpyConversion.swift b/PythonKit/NumpyConversion.swift index fc73a33..c802694 100644 --- a/PythonKit/NumpyConversion.swift +++ b/PythonKit/NumpyConversion.swift @@ -110,18 +110,18 @@ where Element : NumpyScalarCompatible { guard Element.numpyScalarTypes.contains(numpyArray.dtype) else { return nil } - + // Only 1-D `ndarray` instances can be converted to `Array`. let pyShape = numpyArray.__array_interface__["shape"] guard let shape = Array(pyShape) else { return nil } guard shape.count == 1 else { return nil } - + // Make sure that the array is contiguous in memory. This does a copy if // the array is not already contiguous in memory. let contiguousNumpyArray = np.ascontiguousarray(numpyArray) - + guard let ptrVal = UInt(contiguousNumpyArray.__array_interface__["data"].tuple2.0) else { return nil diff --git a/PythonKit/Python.swift b/PythonKit/Python.swift index 841a147..35d2fcb 100644 --- a/PythonKit/Python.swift +++ b/PythonKit/Python.swift @@ -39,7 +39,7 @@ typealias OwnedPyObjectPointer = PyObjectPointer @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. @@ -47,21 +47,21 @@ final class PyReference { 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 @@ -90,26 +90,26 @@ final class PyReference { 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 } @@ -165,7 +165,7 @@ fileprivate extension PythonConvertible { var borrowedPyObject: PyObjectPointer { return pythonObject.borrowedPyObject } - + var ownedPyObject: OwnedPyObjectPointer { return pythonObject.ownedPyObject } @@ -189,7 +189,7 @@ extension PythonObject : PythonConvertible, ConvertibleFromPython { public init(_ object: PythonObject) { self.init(consuming: object.ownedPyObject) } - + public var pythonObject: PythonObject { return self } } @@ -210,7 +210,7 @@ public extension PythonObject { 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. @@ -218,7 +218,7 @@ public enum PythonError : Error, Equatable { /// object. /// - An invalid keyword argument was specified. case invalidCall(PythonObject) - + /// A module import error. case invalidModule(String) } @@ -248,14 +248,14 @@ extension PythonError : CustomStringConvertible { // 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) } @@ -271,11 +271,11 @@ private func throwPythonErrorIfPresent() throws { /// `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. @@ -285,7 +285,7 @@ public struct ThrowingPythonObject { 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. @@ -294,18 +294,18 @@ public struct ThrowingPythonObject { 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() @@ -313,7 +313,7 @@ public struct ThrowingPythonObject { } 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. @@ -324,7 +324,7 @@ public struct ThrowingPythonObject { 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 @@ -333,17 +333,17 @@ public struct ThrowingPythonObject { [(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) @@ -360,20 +360,20 @@ public struct ThrowingPythonObject { 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() @@ -381,7 +381,7 @@ public struct ThrowingPythonObject { } return PythonObject(consuming: result) } - + /// Converts to a 2-tuple, if possible. public var tuple2: (PythonObject, PythonObject)? { let ct = base.checking @@ -390,7 +390,7 @@ public struct ThrowingPythonObject { } return (elt0, elt1) } - + /// Converts to a 3-tuple, if possible. public var tuple3: (PythonObject, PythonObject, PythonObject)? { let ct = base.checking @@ -399,7 +399,7 @@ public struct ThrowingPythonObject { } return (elt0, elt1, elt2) } - + /// Converts to a 4-tuple, if possible. public var tuple4: (PythonObject, PythonObject, PythonObject, PythonObject)? { let ct = base.checking @@ -409,7 +409,7 @@ public struct ThrowingPythonObject { } return (elt0, elt1, elt2, elt3) } - + public var count: Int? { base.checking.count } @@ -434,11 +434,11 @@ public extension PythonObject { 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 @@ -451,7 +451,7 @@ public struct CheckingPythonObject { 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. @@ -463,7 +463,7 @@ public struct CheckingPythonObject { Py_DecRef(keyObject) Py_DecRef(selfObject) } - + // `PyObject_GetItem` returns +1 reference. if let result = PyObject_GetItem(selfObject, keyObject) { return PythonObject(consuming: result) @@ -478,7 +478,7 @@ public struct CheckingPythonObject { Py_DecRef(keyObject) Py_DecRef(selfObject) } - + if let newValue = newValue { let newValueObject = newValue.ownedPyObject PyObject_SetItem(selfObject, keyObject, newValueObject) @@ -489,7 +489,7 @@ public struct CheckingPythonObject { } } } - + /// Access the element corresponding to the specified `PythonConvertible` /// values representing a key. /// - Note: This is equivalent to `object[key]` in Python. @@ -501,7 +501,7 @@ public struct CheckingPythonObject { 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 { @@ -509,7 +509,7 @@ public struct CheckingPythonObject { } 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 { @@ -517,7 +517,7 @@ public struct CheckingPythonObject { } 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], @@ -526,7 +526,7 @@ public struct CheckingPythonObject { } return (elt0, elt1, elt2, elt3) } - + public var count: Int? { Int(Python.len(base)) } @@ -559,7 +559,7 @@ public extension PythonObject { defer { Py_DecRef(selfObject) } let valueObject = newValue.ownedPyObject defer { Py_DecRef(valueObject) } - + if PyObject_SetAttrString(selfObject, memberName, valueObject) == -1 { try! throwPythonErrorIfPresent() fatalError(""" @@ -569,7 +569,7 @@ public extension PythonObject { } } } - + /// Access the element corresponding to the specified `PythonConvertible` /// values representing a key. /// - Note: This is equivalent to `object[key]` in Python. @@ -587,7 +587,7 @@ public extension PythonObject { checking[key] = newValue } } - + /// Converts to a 2-tuple. var tuple2: (PythonObject, PythonObject) { guard let result = checking.tuple2 else { @@ -595,7 +595,7 @@ public extension PythonObject { } return result } - + /// Converts to a 3-tuple. var tuple3: (PythonObject, PythonObject, PythonObject) { guard let result = checking.tuple3 else { @@ -603,7 +603,7 @@ public extension PythonObject { } return result } - + /// Converts to a 4-tuple. var tuple4: (PythonObject, PythonObject, PythonObject, PythonObject) { guard let result = checking.tuple4 else { @@ -611,7 +611,7 @@ public extension PythonObject { } return result } - + /// Call `self` with the specified positional arguments. /// - Precondition: `self` must be a Python callable. /// - Parameter args: Positional arguments for the Python callable. @@ -620,7 +620,7 @@ public extension PythonObject { 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. @@ -630,7 +630,7 @@ public extension PythonObject { 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 @@ -673,16 +673,16 @@ public let Python = PythonInterface() 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 = [""] @@ -694,7 +694,7 @@ public struct PythonInterface { 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() @@ -702,27 +702,27 @@ public struct PythonInterface { } 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__()`. @@ -754,12 +754,12 @@ public extension PythonObject { 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)) @@ -775,14 +775,14 @@ public extension PythonObject { 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 } @@ -790,13 +790,13 @@ private func isType(_ object: PythonObject, 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)) @@ -807,14 +807,14 @@ 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 { @@ -834,17 +834,17 @@ fileprivate extension PythonObject { ) -> 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 - + } } @@ -858,7 +858,7 @@ extension Int : PythonConvertible, ConvertibleFromPython { } self = value } - + public var pythonObject: PythonObject { _ = Python // Ensure Python is initialized. return PythonObject(consuming: PyInt_FromLong(self)) @@ -876,7 +876,7 @@ extension UInt : PythonConvertible, ConvertibleFromPython { } self = value } - + public var pythonObject: PythonObject { _ = Python // Ensure Python is initialized. return PythonObject(consuming: PyInt_FromSize_t(self)) @@ -893,7 +893,7 @@ extension Double : PythonConvertible, ConvertibleFromPython { } self = value } - + public var pythonObject: PythonObject { _ = Python // Ensure Python is initialized. return PythonObject(consuming: PyFloat_FromDouble(self)) @@ -912,7 +912,7 @@ extension Int8 : PythonConvertible, ConvertibleFromPython { guard let i = Int(pythonObject) else { return nil } self.init(i) } - + public var pythonObject: PythonObject { return Int(self).pythonObject } @@ -923,7 +923,7 @@ extension Int16 : PythonConvertible, ConvertibleFromPython { guard let i = Int(pythonObject) else { return nil } self.init(i) } - + public var pythonObject: PythonObject { return Int(self).pythonObject } @@ -934,7 +934,7 @@ extension Int32 : PythonConvertible, ConvertibleFromPython { guard let i = Int(pythonObject) else { return nil } self.init(i) } - + public var pythonObject: PythonObject { return Int(self).pythonObject } @@ -945,7 +945,7 @@ extension Int64 : PythonConvertible, ConvertibleFromPython { guard let i = Int(pythonObject) else { return nil } self.init(i) } - + public var pythonObject: PythonObject { return Int(self).pythonObject } @@ -956,7 +956,7 @@ extension UInt8 : PythonConvertible, ConvertibleFromPython { guard let i = UInt(pythonObject) else { return nil } self.init(i) } - + public var pythonObject: PythonObject { return UInt(self).pythonObject } @@ -967,7 +967,7 @@ extension UInt16 : PythonConvertible, ConvertibleFromPython { guard let i = UInt(pythonObject) else { return nil } self.init(i) } - + public var pythonObject: PythonObject { return UInt(self).pythonObject } @@ -978,7 +978,7 @@ extension UInt32 : PythonConvertible, ConvertibleFromPython { guard let i = UInt(pythonObject) else { return nil } self.init(i) } - + public var pythonObject: PythonObject { return UInt(self).pythonObject } @@ -989,7 +989,7 @@ extension UInt64 : PythonConvertible, ConvertibleFromPython { guard let i = UInt(pythonObject) else { return nil } self.init(i) } - + public var pythonObject: PythonObject { return UInt(self).pythonObject } @@ -1002,7 +1002,7 @@ extension Float : PythonConvertible, ConvertibleFromPython { guard let v = Double(pythonObject) else { return nil } self.init(v) } - + public var pythonObject: PythonObject { return Double(self).pythonObject } @@ -1087,12 +1087,12 @@ 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 { @@ -1204,7 +1204,7 @@ 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) } @@ -1212,23 +1212,23 @@ public extension PythonObject { 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) } @@ -1268,9 +1268,9 @@ 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 } @@ -1284,11 +1284,11 @@ extension PythonObject : SignedNumeric { 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 } @@ -1318,7 +1318,7 @@ extension PythonObject : Equatable, Comparable { 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) } @@ -1330,11 +1330,11 @@ extension PythonObject : Equatable, Comparable { 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) } @@ -1357,27 +1357,27 @@ public extension PythonObject { } 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) } @@ -1395,19 +1395,19 @@ extension PythonObject : Hashable { 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] @@ -1421,7 +1421,7 @@ extension PythonObject : MutableCollection { 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() @@ -1430,7 +1430,7 @@ extension PythonObject : Sequence { return PythonObject(consuming: result) } } - + public func makeIterator() -> Iterator { guard let result = PyObject_GetIter(borrowedPyObject) else { try! throwPythonErrorIfPresent() @@ -1610,47 +1610,47 @@ final class PyFunction { 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) @@ -1661,21 +1661,21 @@ final class PyFunction { 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 @@ -1751,7 +1751,7 @@ fileprivate extension PythonFunction { 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. @@ -1793,7 +1793,7 @@ fileprivate extension PythonFunction { 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 @@ -1864,16 +1864,16 @@ struct PyMethodDef { 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) } @@ -1893,18 +1893,18 @@ extension PythonInstanceMethod : PythonConvertible { 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") }) @@ -1914,14 +1914,14 @@ public struct PythonClass { 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) } diff --git a/PythonKit/PythonLibrary.swift b/PythonKit/PythonLibrary.swift index 7befe64..6149478 100644 --- a/PythonKit/PythonLibrary.swift +++ b/PythonKit/PythonLibrary.swift @@ -32,7 +32,7 @@ import WinSDK public struct PythonLibrary { public enum Error: Swift.Error, Equatable, CustomStringConvertible { case pythonLibraryNotFound - + public var description: String { switch self { case .pythonLibraryNotFound: @@ -43,23 +43,23 @@ public struct PythonLibrary { } } } - + private static let pythonInitializeSymbolName = "Py_Initialize" private static let pythonLegacySymbolName = "PyString_AsString" - + #if canImport(Darwin) private static let defaultLibraryHandle = UnsafeMutableRawPointer(bitPattern: -2) // RTLD_DEFAULT #else private static let defaultLibraryHandle: UnsafeMutableRawPointer? = nil // RTLD_DEFAULT #endif - + private static var isPythonLibraryLoaded = false private static var _pythonLibraryHandle: UnsafeMutableRawPointer? private static var pythonLibraryHandle: UnsafeMutableRawPointer? { try! PythonLibrary.loadLibrary() return self._pythonLibraryHandle } - + /// Tries to load the Python library, will throw an error if no compatible library is found. public static func loadLibrary() throws { guard !self.isPythonLibraryLoaded else { return } @@ -70,7 +70,7 @@ public struct PythonLibrary { self.isPythonLibraryLoaded = true self._pythonLibraryHandle = pythonLibraryHandle } - + private static let isLegacyPython: Bool = { let isLegacyPython = PythonLibrary.loadSymbol(PythonLibrary.pythonLibraryHandle, PythonLibrary.pythonLegacySymbolName) != nil if isLegacyPython { @@ -78,14 +78,14 @@ public struct PythonLibrary { } return isLegacyPython }() - + internal static func loadSymbol( name: String, legacyName: String? = nil, type: T.Type = T.self) -> T { var name = name if let legacyName = legacyName, self.isLegacyPython { name = legacyName } - + log("Loading symbol '\(name)' from the Python library...") return unsafeBitCast(self.loadSymbol(self.pythonLibraryHandle, name), to: type) } @@ -95,9 +95,9 @@ public struct PythonLibrary { extension PythonLibrary { private static let supportedMajorVersions: [Int] = [3, 2] private static let supportedMinorVersions: [Int] = Array(0...30).reversed() - + private static let libraryPathVersionCharacter: Character = ":" - + #if canImport(Darwin) private static var libraryNames = ["Python.framework/Versions/:/Python"] private static var libraryPathExtensions = [""] @@ -114,7 +114,7 @@ extension PythonLibrary { private static var librarySearchPaths = [""] private static var libraryVersionSeparator = "" #endif - + private static let libraryPaths: [String] = { var libraryPaths: [String] = [] for librarySearchPath in librarySearchPaths { @@ -128,7 +128,7 @@ extension PythonLibrary { } return libraryPaths }() - + private static func loadSymbol( _ libraryHandle: UnsafeMutableRawPointer?, _ name: String) -> UnsafeMutableRawPointer? { #if os(Windows) @@ -141,12 +141,12 @@ extension PythonLibrary { return dlsym(libraryHandle, name) #endif } - + private static func isPythonLibraryLoaded(at pythonLibraryHandle: UnsafeMutableRawPointer? = nil) -> Bool { let pythonLibraryHandle = pythonLibraryHandle ?? self.defaultLibraryHandle return self.loadSymbol(pythonLibraryHandle, self.pythonInitializeSymbolName) != nil } - + private static func loadPythonLibrary() -> UnsafeMutableRawPointer? { let pythonLibraryHandle: UnsafeMutableRawPointer? if self.isPythonLibraryLoaded() { @@ -160,7 +160,7 @@ extension PythonLibrary { } return pythonLibraryHandle } - + private static func findAndLoadExternalPythonLibrary() -> UnsafeMutableRawPointer? { for majorVersion in supportedMajorVersions { for minorVersion in supportedMinorVersions { @@ -176,11 +176,11 @@ extension PythonLibrary { } return nil } - + private static func loadPythonLibrary( at path: String, version: PythonVersion) -> UnsafeMutableRawPointer? { let versionString = version.versionString - + if let requiredPythonVersion = Environment.version.value { let requiredMajorVersion = Int(requiredPythonVersion) if requiredPythonVersion != versionString, @@ -188,7 +188,7 @@ extension PythonLibrary { return nil } } - + let libraryVersionString = versionString .split(separator: PythonVersion.versionSeparator) .joined(separator: libraryVersionSeparator) @@ -196,7 +196,7 @@ extension PythonLibrary { .joined(separator: libraryVersionString) return self.loadPythonLibrary(at: path) } - + private static func loadPythonLibrary(at path: String) -> UnsafeMutableRawPointer? { self.log("Trying to load library at '\(path)'...") #if os(Windows) @@ -206,7 +206,7 @@ extension PythonLibrary { // modules may depend on this .so file. let pythonLibraryHandle = dlopen(path, RTLD_LAZY | RTLD_GLOBAL) #endif - + if pythonLibraryHandle != nil { self.log("Library at '\(path)' was successfully loaded.") } @@ -222,7 +222,7 @@ extension PythonLibrary { has already been loaded. """) } - + /// Use the Python library with the specified version. /// - Parameters: /// - major: Major version or nil to use any Python version. @@ -232,7 +232,7 @@ extension PythonLibrary { let version = PythonVersion(major: major, minor: minor) PythonLibrary.Environment.version.set(version.versionString) } - + /// Use the Python library at the specified path. /// - Parameter path: Path of the Python library to load or nil to use the default search path. public static func useLibrary(at path: String?) { @@ -246,9 +246,9 @@ extension PythonLibrary { private struct PythonVersion { let major: Int? let minor: Int? - + static let versionSeparator: Character = "." - + init(major: Int?, minor: Int?) { precondition(!(major == nil && minor != nil), """ Error: The Python library minor version cannot be specified \ @@ -257,7 +257,7 @@ extension PythonLibrary { self.major = major self.minor = minor } - + var versionString: String { guard let major = major else { return "" } var versionString = String(major) @@ -274,22 +274,22 @@ extension PythonLibrary { private enum Environment: String { private static let keyPrefix = "PYTHON" private static let keySeparator = "_" - + case library = "LIBRARY" case version = "VERSION" case loaderLogging = "LOADER_LOGGING" - + var key: String { return Environment.keyPrefix + Environment.keySeparator + rawValue } - + var value: String? { guard let cString = getenv(key) else { return nil } let value = String(cString: cString) guard !value.isEmpty else { return nil } return value } - + func set(_ value: String) { #if os(Windows) _putenv_s(key, value) diff --git a/Tests/PythonKitTests/NumpyConversionTests.swift b/Tests/PythonKitTests/NumpyConversionTests.swift index 7f47946..4189470 100644 --- a/Tests/PythonKitTests/NumpyConversionTests.swift +++ b/Tests/PythonKitTests/NumpyConversionTests.swift @@ -3,25 +3,25 @@ import PythonKit class NumpyConversionTests: XCTestCase { static var numpyModule = try? Python.attemptImport("numpy") - + func testArrayConversion() { guard let np = NumpyConversionTests.numpyModule else { return } - + let numpyArrayEmpty = np.array([] as [Float], dtype: np.float32) XCTAssertEqual([], Array(numpy: numpyArrayEmpty)) - + let numpyArrayBool = np.array([true, false, false, true]) XCTAssertEqual([true, false, false, true], Array(numpy: numpyArrayBool)) - + let numpyArrayFloat = np.ones([6], dtype: np.float32) XCTAssertEqual(Array(repeating: 1, count: 6), Array(numpy: numpyArrayFloat)) - + let numpyArrayInt32 = np.array([-1, 4, 25, 2018], dtype: np.int32) XCTAssertEqual([-1, 4, 25, 2018], Array(numpy: numpyArrayInt32)) - + let numpyArray2D = np.ones([2, 3]) XCTAssertNil(Array(numpy: numpyArray2D)) - + let numpyArrayStrided = np.array([[1, 2], [1, 2]], dtype: np.int32)[ Python.slice(Python.None), 1] // Assert that the array has a stride, so that we're certainly testing a diff --git a/Tests/PythonKitTests/PythonFunctionTests.swift b/Tests/PythonKitTests/PythonFunctionTests.swift index 7be1afa..db39d06 100644 --- a/Tests/PythonKitTests/PythonFunctionTests.swift +++ b/Tests/PythonKitTests/PythonFunctionTests.swift @@ -7,24 +7,24 @@ class PythonFunctionTests: XCTestCase { let versionMinor = Python.versionInfo.minor return (versionMajor == 3 && versionMinor >= 1) || versionMajor > 3 } - + func testPythonFunction() { guard canUsePythonFunction else { return } - + let pythonAdd = PythonFunction { args in let lhs = args[0] let rhs = args[1] return lhs + rhs }.pythonObject - + let pythonSum = pythonAdd(2, 3) XCTAssertNotNil(Double(pythonSum)) XCTAssertEqual(pythonSum, 5) - + // Test function with keyword arguments - + // Since there is no alternative function signature, `args` and `kwargs` // can be used without manually stating their type. This differs from // the behavior when there are no keywords. @@ -36,76 +36,76 @@ class PythonFunctionTests: XCTestCase { XCTAssertEqual(kwargs[0].value, 2) XCTAssertEqual(kwargs[1].key, "x") XCTAssertEqual(kwargs[1].value, 3) - + let conditional = Bool(args[0])! let xIndex = kwargs.firstIndex(where: { $0.key == "x" })! let yIndex = kwargs.firstIndex(where: { $0.key == "y" })! - + return kwargs[conditional ? xIndex : yIndex].value }.pythonObject - + let pythonSelectOutput = pythonSelect(true, y: 2, x: 3) XCTAssertEqual(pythonSelectOutput, 3) } - + // From https://www.geeksforgeeks.org/create-classes-dynamically-in-python func testPythonClassConstruction() { guard canUsePythonFunction else { return } - + let constructor = PythonInstanceMethod { args in let `self` = args[0] `self`.constructor_arg = args[1] return Python.None } - + // Instead of calling `print`, use this to test what would be output. var printOutput: String? - + // Example of function using an alternative syntax for `args`. let displayMethod = PythonInstanceMethod { (args: [PythonObject]) in // let `self` = args[0] printOutput = String(args[1]) return Python.None } - + let classMethodOriginal = PythonInstanceMethod { args in // let cls = args[0] printOutput = String(args[1]) return Python.None } - + // Did not explicitly convert `constructor` or `displayMethod` to // PythonObject. This is intentional, as the `PythonClass` initializer // should take any `PythonConvertible` and not just `PythonObject`. let classMethod = Python.classmethod(classMethodOriginal.pythonObject) - + let Geeks = PythonClass("Geeks", members: [ // Constructor "__init__": constructor, - + // Data members "string_attribute": "Geeks 4 geeks!", "int_attribute": 1706256, - + // Member functions "func_arg": displayMethod, "class_func": classMethod, ]).pythonObject - + let obj = Geeks("constructor argument") XCTAssertEqual(obj.constructor_arg, "constructor argument") XCTAssertEqual(obj.string_attribute, "Geeks 4 geeks!") XCTAssertEqual(obj.int_attribute, 1706256) - + obj.func_arg("Geeks for Geeks") XCTAssertEqual(printOutput, "Geeks for Geeks") - + Geeks.class_func("Class Dynamically Created!") XCTAssertEqual(printOutput, "Class Dynamically Created!") } - + // Previously, there was a build error where passing a simple // `PythonClass.Members` literal made the literal's type ambiguous. It was // confused with `[String: PythonObject]`. The solution was adding a @@ -114,7 +114,7 @@ class PythonFunctionTests: XCTestCase { guard canUsePythonFunction else { return } - + let MyClass = PythonClass( "MyClass", superclasses: [Python.object], @@ -122,76 +122,76 @@ class PythonFunctionTests: XCTestCase { "memberName": "memberValue", ] ).pythonObject - + let memberValue = MyClass().memberName XCTAssertEqual(String(memberValue), "memberValue") } - + func testPythonClassInheritance() { guard canUsePythonFunction else { return } - + var helloOutput: String? var helloWorldOutput: String? - + // Declare subclasses of `Python.Exception` - + let HelloException = PythonClass( "HelloException", superclasses: [Python.Exception], members: [ "str_prefix": "HelloException-prefix ", - + "__init__": PythonInstanceMethod { args in let `self` = args[0] let message = "hello \(args[1])" helloOutput = String(message) - + // Conventional `super` syntax does not work; use this instead. Python.Exception.__init__(`self`, message) return Python.None }, - + // Example of function using the `self` convention instead of `args`. "__str__": PythonInstanceMethod { (`self`: PythonObject) in return `self`.str_prefix + Python.repr(`self`) } ] ).pythonObject - + let HelloWorldException = PythonClass( "HelloWorldException", superclasses: [HelloException], members: [ "str_prefix": "HelloWorldException-prefix ", - + "__init__": PythonInstanceMethod { args in let `self` = args[0] let message = "world \(args[1])" helloWorldOutput = String(message) - + `self`.int_param = args[2] - + // Conventional `super` syntax does not work; use this instead. HelloException.__init__(`self`, message) return Python.None }, - + // Example of function using the `self` convention instead of `args`. "custom_method": PythonInstanceMethod { (`self`: PythonObject) in return `self`.int_param } ] ).pythonObject - + // Test that inheritance works as expected - + let error1 = HelloException("test 1") XCTAssertEqual(helloOutput, "hello test 1") XCTAssertEqual(Python.str(error1), "HelloException-prefix HelloException('hello test 1')") XCTAssertEqual(Python.repr(error1), "HelloException('hello test 1')") - + let error2 = HelloWorldException("test 1", 123) XCTAssertEqual(helloOutput, "hello world test 1") XCTAssertEqual(helloWorldOutput, "world test 1") @@ -199,15 +199,15 @@ class PythonFunctionTests: XCTestCase { XCTAssertEqual(Python.repr(error2), "HelloWorldException('hello world test 1')") XCTAssertEqual(error2.custom_method(), 123) XCTAssertNotEqual(error2.custom_method(), "123") - + // Test that subclasses behave like Python exceptions - + // Example of function with no named parameters, which can be stated // ergonomically using an underscore. The ignored input is a [PythonObject]. let testFunction = PythonFunction { _ in throw HelloWorldException("EXAMPLE ERROR MESSAGE", 2) }.pythonObject - + /* do { try testFunction.throwing.dynamicallyCall(withArguments: []) @@ -217,7 +217,7 @@ class PythonFunctionTests: XCTestCase { XCTFail("A string could not be created from a HelloWorldException.") return } - + XCTAssertTrue(description.contains("EXAMPLE ERROR MESSAGE")) XCTAssertTrue(description.contains("HelloWorldException")) } catch { @@ -225,19 +225,19 @@ class PythonFunctionTests: XCTestCase { } */ } - + // Tests the ability to dynamically construct an argument list with keywords // and instantiate a `PythonInstanceMethod` with keywords. func testPythonClassInheritanceWithKeywords() { guard canUsePythonFunction else { return } - + func getValue(key: String, kwargs: [(String, PythonObject)]) -> PythonObject { let index = kwargs.firstIndex(where: { $0.0 == key })! return kwargs[index].1 } - + // Base class has the following arguments: // __init__(): // - 1 unnamed argument @@ -247,7 +247,7 @@ class PythonFunctionTests: XCTestCase { // test_method(): // - param1 // - param2 - + let BaseClass = PythonClass( "BaseClass", superclasses: [], @@ -259,7 +259,7 @@ class PythonFunctionTests: XCTestCase { `self`.param2 = getValue(key: "param2", kwargs: kwargs) return Python.None }, - + "test_method": PythonInstanceMethod { args, kwargs in let `self` = args[0] `self`.param1 += getValue(key: "param1", kwargs: kwargs) @@ -268,7 +268,7 @@ class PythonFunctionTests: XCTestCase { } ] ).pythonObject - + // Derived class accepts the following arguments: // __init__(): // - param2 @@ -278,7 +278,7 @@ class PythonFunctionTests: XCTestCase { // - param1 // - param2 // - param3 - + let DerivedClass = PythonClass( "DerivedClass", superclasses: [], @@ -286,7 +286,7 @@ class PythonFunctionTests: XCTestCase { "__init__": PythonInstanceMethod { args, kwargs in let `self` = args[0] `self`.param3 = getValue(key: "param3", kwargs: kwargs) - + // Lists the arguments in an order different than they are // specified (self, param2, param3, param1, arg1). The // correct order is (self, arg1, param1, param2, param3). @@ -296,44 +296,44 @@ class PythonFunctionTests: XCTestCase { ("param1", 1), ("", 0) ] - + BaseClass.__init__.dynamicallyCall( withKeywordArguments: newKeywordArguments) return Python.None }, - + "test_method": PythonInstanceMethod { args, kwargs in let `self` = args[0] `self`.param3 += getValue(key: "param3", kwargs: kwargs) - + BaseClass.test_method.dynamicallyCall( withKeywordArguments: args.map { ("", $0) } + kwargs) return Python.None } ] ).pythonObject - + let derivedInstance = DerivedClass(param2: 2, param3: 3) XCTAssertEqual(derivedInstance.arg1, 0) XCTAssertEqual(derivedInstance.param1, 1) XCTAssertEqual(derivedInstance.param2, 2) XCTAssertEqual(derivedInstance.checking.param3, 3) - + derivedInstance.test_method(param1: 1, param2: 2, param3: 3) XCTAssertEqual(derivedInstance.arg1, 0) XCTAssertEqual(derivedInstance.param1, 2) XCTAssertEqual(derivedInstance.param2, 4) XCTAssertEqual(derivedInstance.checking.param3, 6) - + // Validate that subclassing and instantiating the derived class does // not affect behavior of the parent class. - + let baseInstance = BaseClass(0, param1: 10, param2: 20) XCTAssertEqual(baseInstance.arg1, 0) XCTAssertEqual(baseInstance.param1, 10) XCTAssertEqual(baseInstance.param2, 20) XCTAssertEqual(baseInstance.checking.param3, nil) - + baseInstance.test_method(param1: 10, param2: 20) XCTAssertEqual(baseInstance.arg1, 0) XCTAssertEqual(baseInstance.param1, 20) diff --git a/Tests/PythonKitTests/PythonRuntimeTests.swift b/Tests/PythonKitTests/PythonRuntimeTests.swift index 5ee4381..90f1418 100644 --- a/Tests/PythonKitTests/PythonRuntimeTests.swift +++ b/Tests/PythonKitTests/PythonRuntimeTests.swift @@ -6,39 +6,39 @@ class PythonRuntimeTests: XCTestCase { XCTAssertGreaterThanOrEqual(Python.versionInfo.major, 2) XCTAssertGreaterThanOrEqual(Python.versionInfo.minor, 0) } - + func testPythonList() { let list: PythonObject = [0, 1, 2] XCTAssertEqual("[0, 1, 2]", list.description) XCTAssertEqual(3, Python.len(list)) XCTAssertEqual("[0, 1, 2]", Python.str(list)) - + let polymorphicList = PythonObject(["a", 2, true, 1.5]) XCTAssertEqual("a", polymorphicList[0]) XCTAssertEqual(2, polymorphicList[1]) XCTAssertEqual(true, polymorphicList[2]) XCTAssertEqual(1.5, polymorphicList[3]) XCTAssertEqual(1.5, polymorphicList[-1]) - + XCTAssertEqual(4, polymorphicList.count as Int) XCTAssertEqual(4, polymorphicList.checking.count!) XCTAssertEqual(4, polymorphicList.throwing.count!) - + polymorphicList[2] = 2 XCTAssertEqual(2, polymorphicList[2]) } - + #if !os(Windows) func testPythonDict() { let dict: PythonObject = ["a": 1, 1: 0.5] XCTAssertEqual(2, Python.len(dict)) XCTAssertEqual(1, dict["a"]) XCTAssertEqual(0.5, dict[1]) - + XCTAssertEqual(2, dict.count as Int) XCTAssertEqual(2, dict.checking.count!) XCTAssertEqual(2, dict.throwing.count!) - + dict["b"] = "c" XCTAssertEqual("c", dict["b"]) dict["b"] = "d" @@ -61,7 +61,7 @@ class PythonRuntimeTests: XCTestCase { let records_description = "[{'col 1': 3, 'col 2': 5}, {'col 1': 8, 'col 2': 2}]" XCTAssertEqual(String(describing: records), records_description) - + let records_alt: [PythonObject] = [ ["col 1": 3, "col 2": 5, "col 3": 4], ["col 1": 8, "col 2": 2, "col 3": 4] @@ -71,71 +71,71 @@ class PythonRuntimeTests: XCTestCase { XCTAssertEqual(String(describing: records_alt), records_alt_description) } } - + func testRange() { let slice = PythonObject(5..<10) XCTAssertEqual(Python.slice(5, 10), slice) XCTAssertEqual(5, slice.start) XCTAssertEqual(10, slice.stop) - + let range = Range(slice) XCTAssertNotNil(range) XCTAssertEqual(5, range?.lowerBound) XCTAssertEqual(10, range?.upperBound) - + XCTAssertNil(Range(PythonObject(5...))) } - + func testPartialRangeFrom() { let slice = PythonObject(5...) XCTAssertEqual(Python.slice(5, Python.None), slice) XCTAssertEqual(5, slice.start) - + let range = PartialRangeFrom(slice) XCTAssertNotNil(range) XCTAssertEqual(5, range?.lowerBound) - + XCTAssertNil(PartialRangeFrom(PythonObject(..<5))) } - + func testPartialRangeUpTo() { let slice = PythonObject(..<5) XCTAssertEqual(Python.slice(5), slice) XCTAssertEqual(5, slice.stop) - + let range = PartialRangeUpTo(slice) XCTAssertNotNil(range) XCTAssertEqual(5, range?.upperBound) - + XCTAssertNil(PartialRangeUpTo(PythonObject(5...))) } #endif - + func testStrideable() { let strideTo = stride(from: PythonObject(0), to: 100, by: 2) XCTAssertEqual(0, strideTo.min()!) XCTAssertEqual(98, strideTo.max()!) XCTAssertEqual([0, 2, 4, 6, 8], Array(strideTo.prefix(5))) XCTAssertEqual([90, 92, 94, 96, 98], Array(strideTo.suffix(5))) - + let strideThrough = stride(from: PythonObject(0), through: 100, by: 2) XCTAssertEqual(0, strideThrough.min()!) XCTAssertEqual(100, strideThrough.max()!) XCTAssertEqual([0, 2, 4, 6, 8], Array(strideThrough.prefix(5))) XCTAssertEqual([92, 94, 96, 98, 100], Array(strideThrough.suffix(5))) } - + func testBinaryOps() { XCTAssertEqual(42, PythonObject(42)) XCTAssertEqual(42, PythonObject(2) + PythonObject(40)) XCTAssertEqual(2, PythonObject(2) * PythonObject(3) + PythonObject(-4)) - + XCTAssertEqual("abcdef", PythonObject("ab") + PythonObject("cde") + PythonObject("") + PythonObject("f")) XCTAssertEqual("ababab", PythonObject("ab") * 3) - + var x = PythonObject(2) x += 3 XCTAssertEqual(5, x) @@ -164,7 +164,7 @@ class PythonRuntimeTests: XCTestCase { let list: PythonObject = [-1, 10, 1, 0, 0] XCTAssertEqual([-1, 0, 0, 1, 10], list.sorted()) } - + #if !os(Windows) func testHashable() { func compareHashValues(_ x: PythonConvertible) { @@ -172,20 +172,20 @@ class PythonRuntimeTests: XCTestCase { let b = x.pythonObject XCTAssertEqual(a.hashValue, b.hashValue) } - + compareHashValues(1) compareHashValues(3.14) compareHashValues("asdf") compareHashValues(PythonObject(tupleOf: 1, 2, 3)) } #endif - + func testRangeIteration() { for (index, val) in Python.range(5).enumerated() { XCTAssertEqual(PythonObject(index), val) } } - + func testErrors() { XCTAssertThrowsError( try PythonObject(1).__truediv__.throwing.dynamicallyCall(withArguments: 0) @@ -197,7 +197,7 @@ class PythonRuntimeTests: XCTestCase { XCTAssertEqual(exception.__class__.__name__, "ZeroDivisionError") } } - + #if !os(Windows) func testTuple() { let element1: PythonObject = 0 @@ -208,34 +208,34 @@ class PythonRuntimeTests: XCTestCase { let (pair1, pair2) = pair.tuple2 XCTAssertEqual(element1, pair1) XCTAssertEqual(element2, pair2) - + let triple = PythonObject(tupleOf: element1, element2, element3) let (triple1, triple2, triple3) = triple.tuple3 XCTAssertEqual(element1, triple1) XCTAssertEqual(element2, triple2) XCTAssertEqual(element3, triple3) - + let quadruple = PythonObject(tupleOf: element1, element2, element3, element4) let (quadruple1, quadruple2, quadruple3, quadruple4) = quadruple.tuple4 XCTAssertEqual(element1, quadruple1) XCTAssertEqual(element2, quadruple2) XCTAssertEqual(element3, quadruple3) XCTAssertEqual(element4, quadruple4) - + XCTAssertEqual(element2, quadruple[1]) } #endif - + func testMethodCalling() { let list: PythonObject = [1, 2] list.append(3) XCTAssertEqual([1, 2, 3], list) - + // Check method binding. let append = list.append append(4) XCTAssertEqual([1, 2, 3, 4], list) - + // Check *args/**kwargs behavior: `str.format(*args, **kwargs)`. let greeting: PythonObject = "{0} {first} {last}!" XCTAssertEqual("Hi John Smith!", @@ -243,7 +243,7 @@ class PythonRuntimeTests: XCTestCase { XCTAssertEqual("Hey Jane Doe!", greeting.format("Hey", first: "Jane", last: "Doe")) } - + func testConvertibleFromPython() { // Ensure that we cover the -1 case as this is used by Python // to signal conversion errors. @@ -252,7 +252,7 @@ class PythonRuntimeTests: XCTestCase { let five: PythonObject = 5 let half: PythonObject = 0.5 let string: PythonObject = "abc" - + #if !os(Windows) XCTAssertEqual(-1, Int(minusOne)) XCTAssertEqual(-1, Int8(minusOne)) @@ -262,10 +262,10 @@ class PythonRuntimeTests: XCTestCase { #endif XCTAssertEqual(-1.0, Float(minusOne)) XCTAssertEqual(-1.0, Double(minusOne)) - + XCTAssertEqual(0, Int(zero)) XCTAssertEqual(0.0, Double(zero)) - + XCTAssertEqual(5, UInt(five)) XCTAssertEqual(5, UInt8(five)) XCTAssertEqual(5, UInt16(five)) @@ -273,25 +273,25 @@ class PythonRuntimeTests: XCTestCase { XCTAssertEqual(5, UInt64(five)) XCTAssertEqual(5.0, Float(five)) XCTAssertEqual(5.0, Double(five)) - + XCTAssertEqual(0.5, Float(half)) XCTAssertEqual(0.5, Double(half)) // Python rounds down in this case. // XCTAssertEqual(0, Int(half)) - + XCTAssertEqual("abc", String(string)) - + XCTAssertNil(String(zero)) #if !os(Windows) XCTAssertNil(Int(string)) #endif XCTAssertNil(Double(string)) } - + func testPythonConvertible() { let minusOne: PythonObject = -1 let five: PythonObject = 5 - + XCTAssertEqual(minusOne, Int(-1).pythonObject) XCTAssertEqual(minusOne, Int8(-1).pythonObject) XCTAssertEqual(minusOne, Int16(-1).pythonObject) @@ -299,7 +299,7 @@ class PythonRuntimeTests: XCTestCase { XCTAssertEqual(minusOne, Int64(-1).pythonObject) XCTAssertEqual(minusOne, Float(-1).pythonObject) XCTAssertEqual(minusOne, Double(-1).pythonObject) - + XCTAssertEqual(five, UInt(5).pythonObject) XCTAssertEqual(five, UInt8(5).pythonObject) XCTAssertEqual(five, UInt16(5).pythonObject) @@ -308,13 +308,13 @@ class PythonRuntimeTests: XCTestCase { XCTAssertEqual(five, Float(5).pythonObject) XCTAssertEqual(five, Double(5).pythonObject) } - + // SR-9230: https://bugs.swift.org/browse/SR-9230 func testSR9230() { let pythonDict = Python.dict(a: "a", b: "b") XCTAssertEqual(Python.len(pythonDict), 2) } - + // TF-78: isType() consumed refcount for type objects like `PyBool_Type`. func testPythonRefCount() { let b: PythonObject = true @@ -361,7 +361,7 @@ class PythonRuntimeTests: XCTestCase { } XCTAssertEqual(bytes, otherBytes) } - + /// Tests an emulation of the Python `with` statement. /// /// Mirrors: