Slide 1

Slide 1 text

A tale of two cellphones Python on Android and iOS Dr Russell Keith-Magee @freakboy3742 PyCon US 2016

Slide 2

Slide 2 text

A tale of two cellphones Python on Android and iOS Dr Russell Keith-Magee @freakboy3742 PyCon US 2016

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

No content

Slide 5

Slide 5 text

What is "Mobile Python"?

Slide 6

Slide 6 text

A native application delivered as a native application using native system APIs.

Slide 7

Slide 7 text

A native application...

Slide 8

Slide 8 text

... delivered as a native application ...

Slide 9

Slide 9 text

... using native system APIs.

Slide 10

Slide 10 text

What is currently possible?

Slide 11

Slide 11 text

What is currently possible? You can: Write a native iOS application Write a native Android application Write a cross-platform application

Slide 12

Slide 12 text

iOS

Slide 13

Slide 13 text

CPython Issue 23670

Slide 14

Slide 14 text

Using CPython on iOS

Slide 15

Slide 15 text

Python-iOS-template Cookiecutter http://github.com/pybee/Python-iOS-template

Slide 16

Slide 16 text

Accessing native libraries

Slide 17

Slide 17 text

Swift

Slide 18

Slide 18 text

Objective C NSURL *url = [NSURL URLWithString:@"http://pybee.org"];

Slide 19

Slide 19 text

Objective C (intermediate) NSString *str = [ [NSString alloc] initWithCharacters:"http://pybee.org" length:16 ]; NSURL *url = [NSURL URLWithString:str];

Slide 20

Slide 20 text

Objective C in C Class nsstring = objc_getClass("NSString"); SEL alloc = sel_registerName("alloc"); id str = objc_msgsend(nsstring, alloc); SEL init = sel_registerName( "initWithCharacters:length:"); str = objc_msgsend( str, init, "http://pybee.org", 16); Class nsurl = objc_getClass("NSURL"); SEL urlwithstring = sel_registerName( "URLWithString:"); id url = objc_msgsend( nsurl, urlwithstring, str);

Slide 21

Slide 21 text

ctypes from ctypes import * libc = cdll.LoadLibrary("libc.so.6") libc.strchr.argtypes = [c_char_p, c_char] libc.strchr.restype = c_char_p >>> print(libc.strchr(b"abcdef", b"d")) 'def'

Slide 22

Slide 22 text

ctypes and Objective C from ctypes import * lib = util.find_library(b'objc') objc = cdll.LoadLibrary(lib) objc.objc_getClass.argtypes = [c_char_p] objc.objc_getClass.restype = c_void_p NSString = objc.objc_getClass(b'NSString')

Slide 23

Slide 23 text

Descriptors class ObjCInstance: def __getattr__(self, name): print("Getting attribute", name) def __setattr__(self, name, value): print("Set", name, "to", value) >>> obj = ObjCInstance() >>> obj.spam Getting attribute spam >>> obj.pork = "ham" Set attribute pork to ham

Slide 24

Slide 24 text

Callables class Method: def __call__(self, *args) print("Invoke method with args", args) ... >>> method = Method() >>> method(1,2,3) Invoke method with args (1, 2, 3)

Slide 25

Slide 25 text

Object creation class Method: def __new__(cls, *args) ...

Slide 26

Slide 26 text

Rubicon

Slide 27

Slide 27 text

Rubicon from ctypes import cdll, util from rubicon.objc import ObjCClass cdll.LoadLibrary(util.find_library('Foundation')) NSURL = ObjCClass('NSURL'); NSURL.URLWithString_("http://pybee.org/")

Slide 28

Slide 28 text

Rubicon from rubicon.objc import * NSObject = ObjCClass(NSObject) class Handler(NSObject): @objc_method def initWithValue_(self, v: int): self.value = v return self @objc_method def pokeWithValue_(self, v: int) -> None: print ("Poking with", v)

Slide 29

Slide 29 text

An iOS application in Python class PythonAppDelegate(UIResponder): @objc_method def application_didFinishLaunchingWithOptions_( self, application, launchOptions) -> bool: ... class MyViewController(UIViewController): @objc_method def loadView(self) -> None: self.title = 'Add item'

Slide 30

Slide 30 text

Android

Slide 31

Slide 31 text

CPython Issue 23496 https://bitbucket.org/xdegaye/pyona/src

Slide 32

Slide 32 text

Jython

Slide 33

Slide 33 text

Rethinking the problem

Slide 34

Slide 34 text

Inside Python def sing(): for i in range(100, 0, -1): print("%d bottles of beer on the wall" %i) print()

Slide 35

Slide 35 text

Python Bytecode import dis dis.dis(sing)

Slide 36

Slide 36 text

Bytecode 2 0 SETUP_LOOP 47 (to 50) 3 LOAD_GLOBAL 0 (range) 6 LOAD_CONST 1 (100) 9 LOAD_CONST 2 (0) 12 LOAD_CONST 5 (-1) 15 CALL_FUNCTION 3 (3 positional, 0 keyword pair) 18 GET_ITER >> 19 FOR_ITER 27 (to 49) 22 STORE_FAST 0 (i) 3 25 LOAD_GLOBAL 1 (print) 28 LOAD_CONST 4 ('%d bottles of beer on the wall') ...

Slide 37

Slide 37 text

Java package com.example; public class Sing { public static void main(String [] args) { for (int i = 100; i < 0; i--) { System.out.println(i + " bottles of beer on the wall"); } } }

Slide 38

Slide 38 text

Bytecode Max stack: 3 Max locals: 2 Bytecode: (39 bytes) 0: BIPUSH 100 2: ISTORE_1 3: ILOAD_1 4: IFGE 34 7: GETSTATIC java/lang/System.out 10: NEW java/lang/StringBuilder 13: DUP 14: INVOKESPECIAL java/lang/StringBuilder. 17: ILOAD_1 ...

Slide 39

Slide 39 text

VOC

Slide 40

Slide 40 text

An Android application in Python from android.widget import ListView class MainActivity( extends=android.app.Activity): def onCreate(self, state: android.os.Bundle ) -> void: super().onCreate(state) Log.i("MyApp", "Create new app") listview = ListView(self) ...

Slide 41

Slide 41 text

Python-Android-template Cookiecutter http://github.com/pybee/Python-Android-template

Slide 42

Slide 42 text

Cross platform

Slide 43

Slide 43 text

Kivy

Slide 44

Slide 44 text

Toga

Slide 45

Slide 45 text

A Toga App import toga class TodoApp(toga.App): def startup(self): self.list = toga.List( data=[...], on_delete=self.remove_entry, on_refresh=self.refresh ) ...

Slide 46

Slide 46 text

A Toga App (pt II) ... container = toga.NavigationView( title="Todo List", content=self.list, on_action=self.show_add_dialog ) self.main_window.content = container

Slide 47

Slide 47 text

A Toga App (pt III) ... self.input = toga.TextInput(placeholder="thing to do...") self.add_item_dialog = toga.Dialog( title="Add item", content=toga.Container( self.input ), on_accept=self.add_entry ) ...

Slide 48

Slide 48 text

A Toga App (pt IV) def show_add_dialog(self, widget): self.input.clear() self.show_dialog(self.add_item_dialog) def add_entry(self, widget): if self.input.value: self.list.add({'description': self.input.value}) def remove_entry(self, widget): ... def refresh(self, list_widget): ...

Slide 49

Slide 49 text

bit.ly/toga-mobile-demo

Slide 50

Slide 50 text

Packaging

Slide 51

Slide 51 text

pip install briefcase python setup.py ios python setup.py android

Slide 52

Slide 52 text

The future

Slide 53

Slide 53 text

No content

Slide 54

Slide 54 text

The threat

Slide 55

Slide 55 text

The opportunity

Slide 56

Slide 56 text

It is the best of times, it is the worst of times, it is the age of wisdom, it is the age of foolishness, it is the epoch of belief, it is the epoch of incredulity...

Slide 57

Slide 57 text

Thank you! [email protected] @freakboy3742 pybee.org @PyBeeWare Python Mobile-SIG