python support
diff --git a/client/deps/python/requirements.txt b/client/deps/python/requirements.txt
new file mode 100644
index 0000000..7f3351c
--- /dev/null
+++ b/client/deps/python/requirements.txt
@@ -0,0 +1,18 @@
+# taken from openwhisk/core/pythonAction/Dockerfile
+
+#
+# note: on MacOS, you may need to do the following:
+#   % xcode-select --install
+#
+# and if you get a permission denied error while upgrading requests, please issue
+#   % sudo pip install -r requirements.txt
+#
+
+beautifulsoup4==4.5.1
+httplib2==0.9.2
+lxml==3.6.4
+python-dateutil==2.5.3
+requests==2.11.1
+scrapy==1.1.2
+simplejson==3.8.2
+twisted==16.4.0
diff --git a/client/lib/debug-bootstrap/python/bootstrap.py b/client/lib/debug-bootstrap/python/bootstrap.py
new file mode 100644
index 0000000..445f209
--- /dev/null
+++ b/client/lib/debug-bootstrap/python/bootstrap.py
@@ -0,0 +1,48 @@
+#
+# Copyright 2015-2016 IBM Corporation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+import base64
+import httplib2
+import json
+import urllib
+
+# returns a name escaped so it can be used in a url.
+def getSafeName(name):
+    safeChars = '@:./'
+    return urllib.quote(name, safeChars)
+
+def bootstrap(key, namespace, triggerName, main, actualParameters):
+    try:
+        result = main(actualParameters)
+        
+        http = httplib2.Http()
+        
+        url = 'https://openwhisk.ng.bluemix.net/api/v1/namespaces/%(namespace)s/triggers/%(name)s' % {
+            'namespace': urllib.quote(namespace),
+            'name': getSafeName(triggerName)
+        }
+        
+        headers = {'Content-type': 'application/json' }
+        
+        auth = base64.encodestring(key).replace('\n', '')
+        headers['Authorization'] = 'Basic %s' % auth
+        
+        payload = json.dumps(result)
+        
+        response, content = http.request(url, 'POST', headers=headers, body=payload)
+
+    except:
+        pass
diff --git a/client/lib/debug-bootstrap/python/bootstrap.pyc b/client/lib/debug-bootstrap/python/bootstrap.pyc
new file mode 100644
index 0000000..63d5a4b
--- /dev/null
+++ b/client/lib/debug-bootstrap/python/bootstrap.pyc
Binary files differ
diff --git a/client/lib/debug-python.js b/client/lib/debug-python.js
new file mode 100644
index 0000000..98068a1
--- /dev/null
+++ b/client/lib/debug-python.js
@@ -0,0 +1,141 @@
+/*
+ * Copyright 2015-2016 IBM Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+var fs = require('fs'),
+    ok_ = require('./repl-messages').ok_,
+    tmp = require('tmp'),
+    diff = require('./diff'),
+    kill = require('tree-kill'),
+    open = require('open'),
+    path = require('path'),
+    spawn = require('child_process').spawn,
+    options = require('./options').options;
+
+exports.debug = function debug(message, ws, echoChamberNames, done, commandLineOptions, eventBus) {
+    try {
+	exports._debug(message, ws, echoChamberNames, done, commandLineOptions, eventBus);
+    } catch (e) {
+	console.error(e);
+    }
+};
+exports._debug = function debug(message, ws, echoChamberNames, done, commandLineOptions, eventBus) {
+    var code = message.action.exec.code;
+
+    var r = new RegExp(/def main[\s]*\([^\)]*\):/);
+    var startOfMethodBody = code.search(r);
+    if (startOfMethodBody >= 0) {
+	var colon = code.indexOf(':', startOfMethodBody);
+	code =
+	    code.substring(0, colon + 1)
+	    + '\n    # Welcome to your main method\n'
+	    + '    from pdb import set_trace as debugger\n'
+	    + '    debugger()\n'
+	    + code.substring(colon + 1);
+    }
+
+    code += '\n\n\nfrom bootstrap import bootstrap\n';
+    code += 'try:\n';
+    code += '    bootstrap(\'' + message.key + '\', \'' + message.action.namespace + '\', \'' + echoChamberNames.trigger + '\', main, ' + JSON.stringify(message.actualParameters || {}) + ');\n';
+    code += 'except:\n';
+    code += '    pass\n';
+    code += 'finally:\n';
+    code += '    sys.exit(0)\n';
+
+    tmp.dir({ prefix: 'wskdb-', unsafeCleanup: true}, function onTempDirCreation(err, tmpDirPath, tmpdirCleanupCallback) {
+	var tmpdirPathRegExp = new RegExp(tmpDirPath + '/');
+	var tmpFilePath = path.join(tmpDirPath, message.action.name + '.py');
+
+	try {
+	    fs.writeFile(tmpFilePath, code, 0, 'utf8', function onFileWriteCompletion(err, written, string) {
+
+		// we need to update the NODE_PATH env var, to add our local modules
+		var env = Object.assign({}, process.env);
+		env.PYTHONPATH = path.join(process.cwd(), 'lib', 'debug-bootstrap', 'python');
+		env.PYTHONUNBUFFERED = '1';
+
+		function spawnWithCLI() {
+		    try {
+			var spawnOpts = {
+			    cwd: process.cwd(),
+			    stdio: ['inherit', 'pipe', 'pipe'],
+			    env: env
+			};
+			var child = spawn('python',
+					  ['-u',
+					   '-m', 'pdb',
+					   tmpFilePath],
+					  spawnOpts);
+			
+			//
+			// the activation that we are debugging has
+			// finished. kill the child debugger process
+			//   SIGKILL is required to avoid killing wskdb, too
+			eventBus.on('invocation-done', () => child.kill('SIGKILL'));
+			
+			child.stderr.on('data', message => console.error(message.toString().red));
+
+			var baked = false, raw = true, first = true;
+			child.stdout.on('data', function(message) {
+			    try {
+				if (raw) {
+				    raw = false;
+				    console.log();
+				    console.log('Hello from the Python debugger. Enter '
+						+ 'c'.red + ' or ' + 'continue'.red + ' to enter your main method.');
+				    console.log('Hint: enter ' + 'l'.red + ' or ' + 'list'.red + ' to see a longer program listing');
+				    console.log();
+				}
+
+				if (!baked) {
+				    if (message.indexOf('The program exited via sys.exit') >= 0) {
+					baked = true;
+				    } else {
+					process.stdout.write(message.toString().replace(tmpdirPathRegExp, '').blue.dim);
+				    }
+				}
+			    } catch (err) {
+				console.error(err);
+			    }
+			});
+			
+			//
+			// the child debugger process has terminated, clean things up
+			//
+			child.on('exit', code => {
+			    //if (code !== 0) {
+			    //console.error('The Python debugger exited abnormally with code ' + code);
+			    //}
+			    
+			    //try { tmpdirCleanupCallback(); } catch (e) { }
+			    done(); // we don't need to "ok" here, as the invoker will do that for us
+			});
+		    } catch (e) {
+			console.error('Error spawning debugger', e);
+			console.error(e.stack);
+			done();
+		    }
+		}
+		
+		spawnWithCLI();
+	    });
+	} catch (e) {
+	    console.error(e);
+	    console.error(e.stack);
+	    //try { tmpdirCleanupCallback(); } catch (e) { }
+	    done();
+	}
+    });
+};
diff --git a/client/lib/init.js b/client/lib/init.js
index f223ebb..95bed95 100644
--- a/client/lib/init.js
+++ b/client/lib/init.js
@@ -55,15 +55,21 @@
 		if (err) {
 		    return reject(err);
 		}
-		
-		touch();
-		clearInterval(dots);
 
-		console.log();
-		console.log('>>> Great! The one-time initialization has completed.');
-		console.log();
+		exec('pip install -r deps/python/requirements.txt', { cwd: process.cwd() }, (err) => {
+		    if (err) {
+			return reject(err);
+		    }
 
-		resolve();
+		    touch();
+		    clearInterval(dots);
+
+		    console.log();
+		    console.log('>>> Great! The one-time initialization has completed.');
+		    console.log();
+
+		    resolve();
+		});
 	    });
 	});
     });
diff --git a/client/lib/main.js b/client/lib/main.js
index ca238c9..877e5d0 100644
--- a/client/lib/main.js
+++ b/client/lib/main.js
@@ -23,6 +23,7 @@
     WebSocket = require('ws'),
     debugNodeJS = require('./debug-nodejs').debug,
     debugSwift = require('./debug-swift').debug,
+    debugPython = require('./debug-python').debug,
     expandHomeDir = require('expand-home-dir'),
     propertiesParser = require('properties-parser'),
 
@@ -75,6 +76,7 @@
 		type: 'keep-alive'
 	    }));
 	} catch (e) {
+	    console.error();
 	    console.error('It looks like your network went offline. Please restart wskdb when your network is live.');
 	    process.exit(1);
 	}
@@ -113,7 +115,7 @@
 });
 
 ws.on('close', function() {
-    console.log('Remote connection closed');
+    //console.log('Remote connection closed');
 });
  
 ws.on('message', function(data, flags) {
@@ -160,6 +162,8 @@
 			debugHandler = debugNodeJS;
 		    } else if (kind.indexOf('swift') >= 0) {
 			debugHandler = debugSwift;
+		    } else if (kind.indexOf('python') >= 0) {
+			debugHandler = debugPython;
 		    }
 
 		    if (debugHandler) {
diff --git a/client/test/helpers/driver.js b/client/test/helpers/driver.js
index 5d003b1..68e40d9 100644
--- a/client/test/helpers/driver.js
+++ b/client/test/helpers/driver.js
@@ -90,6 +90,7 @@
 		} else if (data.indexOf('ok') == 0
 			   || data.indexOf('\nok\n') >= 0
 			   || data.indexOf('break in') >= 0
+			   || data.indexOf('(Pdb)') >= 0
 			   || data.indexOf('stopped') >= 0) {
 		    goody = true;
 			
diff --git a/client/test/rewriter/createAttachInvokeDetachDeleteForPython.js b/client/test/rewriter/createAttachInvokeDetachDeleteForPython.js
new file mode 100644
index 0000000..4f752ab
--- /dev/null
+++ b/client/test/rewriter/createAttachInvokeDetachDeleteForPython.js
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2015-2016 IBM Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import it from '../helpers/driver'
+
+it.should('create a python action, then attach, invoke, detach, delete, and finally quit without error', (name) => [
+    `create ${name} python import sys\\n\\ndef main(dict):\\n    return { \'message\': \'Hello world\' }`,
+    `attach ${name} -a`,
+    `invoke ${name}`,
+    `c`,
+    `c`,
+    `detach ${name}`,
+    `delete ${name}`
+], ['-c']); // use the cli debugger