Skip to content

johnno1962/SwiftPython

Repository files navigation

SwiftPython - Experiments in bridging Swift to Python.

Icon

Contains a simple playground that uses a class "Complex" that is implemented in Python from Swift. There are two Auxiliary files contained in the playground that perform the bridging: "PythonSupport.swift" (most easily viewed using the workspace) that contains a small set of generic support code for bridging and "Complex.swift" which performs the actual bridging and would normally be generated automatically by a python code generator script introspecting the module. The underlying python implementation "complex.py" is included in the playground as a resource.

//: Playground - noun: a place where people can play

import Foundation
var str = "Hello, python integration"

// This gives a stack trace when something out of the ordinary occurs
let _pythonWarn = pythonWarn
pythonWarn = {
    (_ message: String) in
    _pythonWarn(message)
    print(dumpStrackTrace())
}

// The basic class underlying the integration is PythonObject
PythonObject(any: "123").asString

// PythonObject can accept most Swift types specifically:
// Arrays, Dictionaries, Strings, Ints, Doubles, Data & Bool
let pythonObject = PythonObject(any: ["list": Array(0 ..< 100)])

// this is converted back to a concrete Swift type using "asAny(of:)"
let arrayDictionary = pythonObject.asAny(of: [String: [Int]].self)
print(arrayDictionary["list"]![4])

// Leave the Object as Python structures to access it more efficiently
print(PythonDict<[Int]>(any: ["list": Array(0 ..< 100)])["list"]![4])
print(PythonDict<PythonList<Int>>(any: ["list": Array(0 ..< 100)])["list"]![4])

// PythonObject are passed to python methods and functions and returned.
// This Complex class is as wrapper generated by the script "bridgegen.py"
let cplx = Complex(realpart: 11.0, imagpart: 12.0)

// There are named and unnamed versions of initialisers and methods generated
cplx.add(c: Complex(1.0, 2.0))

// __doc__ comments in the Python class can specify the return type for "asAny"
print(cplx.toDictionary())

// global functions in the module are also exported
print(newComplex(real: 123, imag: 456).toString(extra: cplx.toDictionary()))
print(newComplex(123, 456).toString(extra: cplx.toDictionary()))

// Basic operators work when defined
var c = Complex(1, 2)
c = c + c
c += c

// A closure or function can be used to have Python call back to Swift
cplx.callme(closure: {
    (args: [PythonObject]) -> PythonObject? in
    print(args[0].asString)
    return cplx
}, str: "Swift closure called from Python called from Swift")

func callback(args: [PythonObject]) -> PythonObject? {
    print(args[0].asString)
    return Complex(11.0, 22.0)
}

PythonObject(any: [cplx, cplx]).asAny(of: [Complex].self)[1]
    .callme(callback, "Swift function called from Python called from Swift")

// PythonList and PythonDict conform to Sequence
let list = PythonList<String>()
list.append("123")
list.append("234")
list.append("345")

// Setting an element at the end of a list is an implicit append
list[3] = "456"

for item in list {
    print(item)
}

let dict = PythonDict<Int>()
dict["ABC"] = 123
dict["DEF"] = 456

for (key, value) in dict {
    print("\(key): \(value)")
}

// Oddly, a PythonList can be initialised from PythonDict and vice versa
let dictAsList = PythonList<Any>(dictionary: dict).asTypeArray
PythonDict<Int>(array: dictAsList)

// Wherever possible conversion between types is available
let a1 = PythonObject(any: [1, 2, 3]).asArray(of: Int.self)
let a2 = PythonObject(any: [1, 2, 3]).asArray(of: Double.self)
let a3 = PythonObject(any: [1.5, 2.5, 3.5]).asArray(of: Int.self)
let a4 = PythonObject(any: [1.5, 2.5, 3.5]).asArray(of: Double.self)
let a5 = PythonObject(any: a4).asArray(of: String.self)
let a6 = PythonObject(any: a5).asArray(of: Double.self)
let d1 = PythonObject(any: ["a": 123, "b": 456]).asAny(of: [String: Double].self)

// "PythonAny" is SwiftyJSON-like omni-type useful for processing recursive data
PythonAny(any: ["a": 1.0, "b": 2.0, "c": [1,2,3]])["c"]?[1].asInt

// Processing large arrays of primitive types is optimised
let start = Date()
cplx.echoArray(value: ["data": Array(0 ..< 1_000_000)])
    .asAny(of: [String: [Int]].self)["data"]![1000]
print(Date().timeIntervalSince(start))

// With Anaconda 2.7 installed and a small patch to the Python framework
// (only required on Sierra) you can use 3d plots. Details in myplot.py:
//let myplotModule = PythonModule(named: "myplot")

// Finally, Python's plot routines can be made available manually
let matplotlib_pyplotModule = PythonModule(named: "matplotlib.pyplot")
let imshowFunction = matplotlib_pyplotModule.function(named: "imshow")
let showFunction = matplotlib_pyplotModule.function(named: "show")

// This is the code normally generated by the script bridgegen.py
func imshow(_ X: Any? = nil, _ cmap: Any? = nil, _ norm: Any? = nil, _ aspect: Any? = nil, _ interpolation: Any? = nil, _ alpha: Any? = nil, _ vmin: Any? = nil, _ vmax: Any? = nil, _ origin: Any? = nil, _ extent: Any? = nil, _ shape: Any? = nil, _ filternorm: Any = 1, _ filterrad: Any = 4.0, _ imlim: Any? = nil, _ resample: Any? = nil, _ url: Any? = nil, _ hold: Any? = nil, kw: [String: Any]? = nil) -> PythonObject {
    return imshowFunction.call(args: [X, cmap, norm, aspect, interpolation, alpha, vmin, vmax, origin, extent, shape, filternorm, filterrad, imlim, resample, url, hold], kw: kw)
}

func show(_ kw: [String: Any]? = nil) -> PythonObject {
    return showFunction.call(args: [], kw: kw)
}

print("Mandelbrot window may appear behind the playground/workspace")
imshow(mandelbrot(400,400))
show(["block": false])

import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true

Code generator

An example code generator bridgegen.py is included. Its first argument is the module to be generated then an optional path to the module so python can find it and Swift code will be printed to stdout. Use doc comments as shown in complex.py to control the return type of functions or specify instance variables.

About

Experiments in bridging Swift to Python

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages