Support graceful script termination
diff --git a/appurify/client.py b/appurify/client.py
index 5ce6729..5c3332e 100644
--- a/appurify/client.py
+++ b/appurify/client.py
@@ -16,6 +16,7 @@
 import pprint
 import inspect
 
+from . import signals
 from . import constants
 from .utils import log, get, post, wget
 
@@ -99,6 +100,9 @@
 def tests_check_result(access_token, test_run_id):
     return get('tests/check', {'access_token':access_token, 'test_run_id': test_run_id})
 
+def tests_abort(access_token, test_run_id):
+    return post('tests/abort', {'access_token':access_token, 'test_run_id': test_run_id})
+
 ###################
 ## Config file API
 ###################
@@ -287,26 +291,36 @@
         runtime = 0
 
         while test_status != 'complete' and runtime < self.timeout:
-            time.sleep(self.poll_every)
-            r = tests_check_result(self.access_token, test_run_id)
-            test_status_response = r.json()['response']
-            test_status = test_status_response['status']
-            if test_status == 'complete':
-                test_response = test_status_response['results']
-                log("**** COMPLETE - JSON SUMMARY FOLLOWS ****")
-                log(json.dumps(test_response))
-                log("**** COMPLETE - JSON SUMMARY ENDS ****")
-                return test_status_response
-            else:
-                log("%s sec elapsed" % str(runtime))
-                if 'message' in test_status_response:
-                    log(test_status_response['message'])
-                log("Test progress: {}".format(test_status_response.get('detailed_status', 'status-unavailable')))
-            runtime = runtime + self.poll_every
+            try:
+                time.sleep(self.poll_every)
+                r = tests_check_result(self.access_token, test_run_id)
+                test_status_response = r.json()['response']
+                test_status = test_status_response['status']
+                if test_status == 'complete':
+                    test_response = test_status_response['results']
+                    log("**** COMPLETE - JSON SUMMARY FOLLOWS ****")
+                    log(json.dumps(test_response))
+                    log("**** COMPLETE - JSON SUMMARY ENDS ****")
+                    return test_status_response
+                else:
+                    log("%s sec elapsed" % str(runtime))
+                    if 'message' in test_status_response:
+                        log(test_status_response['message'])
+                    log("Test progress: {}".format(test_status_response.get('detailed_status', 'status-unavailable')))
+                runtime = runtime + self.poll_every
+            except signals.QuitException:
+                log("Quitting script")
+                raise
+            except signals.AbortException:
+                r = tests_abort(self.access_token, test_run_id)
+                log("Aborting test run...")
+                log(r.json()['response'])
+                raise
+            except signals.ContinueException:
+                pass
 
         raise AppurifyClientError("Test result poll timed out after %s seconds" % self.timeout)
 
-
     def reportTestResult(self, test_status_response):
         test_response = test_status_response['results']
         result_dir = self.args.get('result_dir', None)
diff --git a/appurify/signals.py b/appurify/signals.py
new file mode 100644
index 0000000..325b0b2
--- /dev/null
+++ b/appurify/signals.py
@@ -0,0 +1,42 @@
+'''
+Created on Oct 15, 2013
+
+@author: twang
+'''
+import signal
+
+class SigintException(Exception):
+    pass
+
+class AbortException(SigintException):
+    message = "Test run aborted at user request"
+    pass
+
+class ContinueException(SigintException):
+    message = "Continuing test execution"
+    pass
+
+class QuitException(SigintException):
+    message = "Script quit at user request without aborting test run"
+    pass
+
+def signal_handler(signal, frame):
+    response = None
+    while not response or response not in ['a', 'A', 'q', 'Q', 'c', 'C']:
+        response = read_input("Interrupted: (a)bort test run, (q)uit script without aborting or (c)ontinue script? [a/q/c]")
+    if response in ['a', 'A']:
+        raise AbortException
+    elif response in ['c', 'C']:
+        raise ContinueException
+    else:
+        raise QuitException
+
+def read_input(prompt):
+    try:
+        response = raw_input(prompt)
+    except:
+        response = input(prompt)
+    return response
+
+signal.signal(signal.SIGINT, signal_handler)
+
diff --git a/appurify/utils.py b/appurify/utils.py
index bfff5ef..bf3eab4 100644
--- a/appurify/utils.py
+++ b/appurify/utils.py
@@ -203,3 +203,4 @@
     """Download a file to specified path"""
     with open(path, 'wb') as f:
         f.write(requests.get(url, verify=verify).content)
+