diff --git a/Makefile b/Makefile
index b379286b85fc33af3e5c1751bfe4bf7536df068a..b4d809e1f7b4bc80d2ff1d00e42e44a5ef281232 100644
--- a/Makefile
+++ b/Makefile
@@ -11,15 +11,17 @@ COVER_PACKAGES=hbp_nrp_virtual_coach
 DOC_MODULES=hbp_nrp_virtual_coach/doc
 DOC_REPO=--doc-repo ssh://bbpcode.epfl.ch/infra/jekylltest
 
-PYTHON_PIP_VERSION?=pip==9.0.3
+PYTHON_PIP_VERSION?='pip>=19'
 
 ##### DO NOT MODIFY BELOW #####################
 
+python_version_full := $(shell python -c "import sys; maj, min = sys.version_info[:2]; print('{}.{}'.format(maj, min))" 2>&1)
+
 ifeq ($(NRP_INSTALL_MODE),user)
-        $(shell cp -af $(HBP)/user-scripts/config_files/platform_venv/* $(VIRTUAL_ENV)/lib/python2.7/site-packages/ )
+        $(shell cp -af $(HBP)/user-scripts/config_files/platform_venv/* $(VIRTUAL_ENV)/lib/python$(python_version_full)/site-packages/ )
         include user_makefile
 else
-        $(shell cp -af $(HBP)/user-scripts/config_files/platform_venv/* $(VIRTUAL_ENV)/lib/python2.7/site-packages/ )
+        $(shell cp -af $(HBP)/user-scripts/config_files/platform_venv/* $(VIRTUAL_ENV)/lib/python$(python_version_full)/site-packages/ )
         CI_REPO?=git@bitbucket.org:hbpneurorobotics/admin-scripts.git
         CI_DIR?=$(HBP)/admin-scripts/ContinuousIntegration
         THIS_DIR:=$(PWD)
diff --git a/bitbucket-pipelines.yml b/bitbucket-pipelines.yml
index 54ea51d92095a82547eb541054ef2a81c53477aa..109868b344b3883a9cc1407ca314617806b1a09d 100644
--- a/bitbucket-pipelines.yml
+++ b/bitbucket-pipelines.yml
@@ -28,6 +28,9 @@ pipelines:
           # Copy bbp-client from user-scripts (before make devinstall)
           - cp -af $HBP/user-scripts/config_files/platform_venv/* $VIRTUAL_ENV_PATH/lib/python2.7/site-packages/
 
+          # Delete pip lock in VIRTUAL_ENV so to force checking for pip upgrade
+          - rm $VIRTUAL_ENV/new-pip.txt
+
           # Build
           - export IGNORE_LINT='platform_venv|config_files|examples/integration_test/test_experiment_folder'
           - make verify_base || { if [ -f pylint.txt ]; then echo "----------"; echo "PYLINT.TXT"; echo "----------";cat pylint.txt; fi; if [ -f pep8.txt ]; then echo "----------"; echo "PEP8.TXT"; echo "----------";cat pep8.txt; fi; exit 1; }
diff --git a/examples/integration_test/it.py b/examples/integration_test/it.py
index acdac7be2cbec26ac6c00da4694208f3843de835..6f159e02d8209c11cd1d096555e032c183cb7630 100644
--- a/examples/integration_test/it.py
+++ b/examples/integration_test/it.py
@@ -25,12 +25,20 @@ platform (e.g. specific text values, timings, etc.), but SHOULD interact with al
 interfaces/APIs that are supported.
 """
 
+# pylint: disable=W0622
+
+from __future__ import print_function
+
+from builtins import str
+from builtins import zip
+from builtins import object
 import argparse
 import logging
 import json
 import sys
 import time
 import traceback
+import os
 
 # virtualcoach and platform_venv specific packages, ensure environment is correct
 try:
@@ -38,14 +46,14 @@ try:
     import progressbar
 
 except ImportError:
-    print
+    print()
     print('Failed to import the VirtualCoach or access packages in the platform_venv! Aborting.')
-    print
+    print()
     print('Please make sure you have:')
-    print
+    print()
     print('\t1. run "make devinstall" in the VirtualCoach repo')
     print('\t2. use "cle-virtual-coach it.py" to run this script')
-    print
+    print()
     sys.exit(-1)
 
 ##
@@ -117,7 +125,7 @@ def run(oidc_username, storage_username):
 
         # the simulation we will launch, defined here so we have a cleanup reference at any point
         sim = None
-
+        path = os.path.dirname(os.path.abspath(__file__))
         ##
         ## Server Information and Experiment List Interaction
         ##
@@ -137,7 +145,7 @@ def run(oidc_username, storage_username):
         # ensure there is a running server that is not currently running an experiment
         results.start('Checking For Available Backend')
         available_servers = vc._VirtualCoach__get_available_server_list()
-        if len(available_servers) == 0:
+        if not available_servers:
             raise TestCaseError('No available backends to run test on.')
         results.done(True)
 
@@ -145,7 +153,7 @@ def run(oidc_username, storage_username):
         # the experiment first if not.
         results.start('Cloning a new Empty Template Husky Experiment from the Storage Server')
 
-        if 'ExDTemplateHusky' not in [s[0] for s in server_info.iteritems()]:
+        if 'ExDTemplateHusky' not in [s[0] for s in server_info.items()]:
             raise TestCaseError('Husky Template Experiment is not available on the server to be'
                                 ' cloned.')
         experiment_id = vc.clone_experiment_to_storage('ExDTemplateHusky')
@@ -179,12 +187,11 @@ def run(oidc_username, storage_username):
         ##
 
         results.start('Importing an experiment folder')
-        response = vc.import_experiment('test_experiment_folder')
+        response = vc.import_experiment(path + '/test_experiment_folder')
         new_experiment_id = json.loads(response.text)['destFolderName']
         if new_experiment_id not in vc._VirtualCoach__get_experiment_list(cloned=True):
             raise TestCaseError('Importing an experiment folder failed')
-        else:
-            vc.delete_cloned_experiment(new_experiment_id)
+        vc.delete_cloned_experiment(new_experiment_id)
         results.done(True)
 
         ##
@@ -192,12 +199,11 @@ def run(oidc_username, storage_username):
         ##
 
         results.start('Importing a zipped experiment folder')
-        response = vc.import_experiment('test_experiment_folder.zip')
+        response = vc.import_experiment(path + '/test_experiment_folder.zip')
         new_experiment_id = json.loads(response.text)['destFolderName']
         if new_experiment_id not in vc._VirtualCoach__get_experiment_list(cloned=True):
             raise TestCaseError('Importing an experiment folder failed')
-        else:
-            vc.delete_cloned_experiment(new_experiment_id)
+        vc.delete_cloned_experiment(new_experiment_id)
         results.done(True)
 
         ##
@@ -267,12 +273,12 @@ def run(oidc_username, storage_username):
         # retrieve transfer functions
         results.start('Retrieving Transfer Functions')
         tfs = sim._Simulation__get_simulation_scripts('transfer-function')['data']
-        if len(tfs) == 0:
+        if not tfs:
             raise TestCaseError('No transfer functions returned for experiment.')
         results.done(True)
 
         # create a valid tf with a modified import and an invalid tf for testing
-        tf_name = str(tfs.keys()[0])
+        tf_name = str(list(tfs.keys())[0])
         valid_tf = 'import os\n' + tfs[tf_name]
         invalid_tf = 'this is invalid!\n' + tfs[tf_name]
 
@@ -448,23 +454,23 @@ def tf(t):
         results.done(True)
 
     # handle test case or unexpected failures, attempt to cleanup and terminate
-    except Exception, e:
+    except Exception as e:
 
         # fail the last test case
         results.abort()
 
         # check if the test case has just failed
-        print
+        print()
         if isinstance(e, TestCaseError):
-            print 'Test case failed: %s - attempting to cleanup.' % e
+            print('Test case failed: %s - attempting to cleanup.' % e)
 
         # unhandled exception means a virtual coach failure, log it
         else:
-            print
-            print 'Unexpected exception encountered during execution, attempting to cleanup.'
-            print
+            print()
+            print('Unexpected exception encountered during execution, attempting to cleanup.')
+            print()
             traceback.print_exc(e)
-        print
+        print()
 
         # if a sim is running, try to shut it down cleanly, we can't do much more
         if sim:
@@ -508,29 +514,29 @@ if __name__ == '__main__':
         logging.disable(logging.CRITICAL)
 
     # banner with user warnings / prompts
-    print ''.center(80, '=')
-    print '|%s|' % ''.center(78, ' ')
-    print '|%s|' % 'VirtualCoach Integration Test Script'.center(78, ' ')
-    print '|%s|' % ''.center(78, ' ')
-    print '|%s|' % ''.center(78, ' ')
+    print(''.center(80, '='))
+    print('|%s|' % ''.center(78, ' '))
+    print('|%s|' % 'VirtualCoach Integration Test Script'.center(78, ' '))
+    print('|%s|' % ''.center(78, ' '))
+    print('|%s|' % ''.center(78, ' '))
     if args.oidc_username:
-        print '|%s|' % ('OIDC is enabled, user: %s' % args.oidc_username).center(78, ' ')
-        print '|%s|' % 'Note: you may be prompted for a password.'.center(78, ' ')
+        print('|%s|' % ('OIDC is enabled, user: %s' % args.oidc_username).center(78, ' '))
+        print('|%s|' % 'Note: you may be prompted for a password.'.center(78, ' '))
     elif args.storage_username:
-        print '|%s|' % ('Local Storage is enabled, user: %s' % args.storage_username).\
-            center(78, ' ')
-        print '|%s|' % 'Note: you may be prompted for a password.'.center(78, ' ')
+        print('|%s|' % ('Local Storage is enabled, user: %s' % args.storage_username)
+              .center(78, ' '))
+        print('|%s|' % 'Note: you may be prompted for a password.'.center(78, ' '))
     else:
         raise ValueError('|%s|' % 'No OIDC or local Storage credentials were provided. Please run '
                          'the script with either oidc_username or storage_username as arguments'.
                          center(78, ' '))
-    print '|%s|' % ''.center(78, ' ')
-    print '=%s=' % ''.center(78, '=')
+    print('|%s|' % ''.center(78, ' '))
+    print('=%s=' % ''.center(78, '='))
 
     # run all of the sequential tests, results are returned upon success or first failure
-    print
-    print 'Running Test Cases, This May Take A Few Moments...'.center(80, ' ')
-    print
+    print()
+    print('Running Test Cases, This May Take A Few Moments...'.center(80, ' '))
+    print()
     cumulative = TestCase('Summary'.center(52, ' '))
     results = run(args.oidc_username, args.storage_username)
     cumulative.done(all([r.success for r in results]))
@@ -540,9 +546,9 @@ if __name__ == '__main__':
     # so we just do it manually here instead, not the most readable but functional
     widths = [75, 12, 12]
     headers = ['Description', 'Duration', 'Result']
-    print '\n\n+{0}+{1}+{2}+'.format(*(''.center(w, '-') for w in widths))
-    print '|{0}|{1}|{2}|'.format(*(h.center(w, ' ') for h, w in zip(headers, widths)))
-    print '+{0}+{1}+{2}+'.format(*(''.center(w, '=') for w in widths))
+    print('\n\n+{0}+{1}+{2}+'.format(*(''.center(w, '-') for w in widths)))
+    print('|{0}|{1}|{2}|'.format(*(h.center(w, ' ') for h, w in zip(headers, widths))))
+    print('+{0}+{1}+{2}+'.format(*(''.center(w, '=') for w in widths)))
 
     # print all of the data rows in easy to visually scan colors (since no one will look at the
     # individual test cases and output otherwise
@@ -563,8 +569,8 @@ if __name__ == '__main__':
                      formatted[2].center(widths[2] + 9, ' ')]
 
         # print the row and next table separator
-        print '|{0}|{1}|{2}|'.format(*formatted)
-        print '+{0}+{1}+{2}+'.format(*(''.center(w, '-') for w in widths))
+        print('|{0}|{1}|{2}|'.format(*formatted))
+        print('+{0}+{1}+{2}+'.format(*(''.center(w, '-') for w in widths)))
 
     # change the exit code so it can be checked automatically if needed
     sys.exit(0 if results[-1].success else -1)
diff --git a/hbp_nrp_virtual_coach/hbp_nrp_virtual_coach/config.py b/hbp_nrp_virtual_coach/hbp_nrp_virtual_coach/config.py
index 118d9a7ed40e9438494a80e640f244f5a9726189..268adb598cb95d73edf8934da1c38311df8620a0 100644
--- a/hbp_nrp_virtual_coach/hbp_nrp_virtual_coach/config.py
+++ b/hbp_nrp_virtual_coach/hbp_nrp_virtual_coach/config.py
@@ -25,12 +25,17 @@
 A strongly validated configuration implementation for the VirtualCoach.
 """
 
-from hbp_nrp_virtual_coach.version import VERSION
+# pylint: disable=W0622
+
+from builtins import str
 
 import json
 import logging
 import os
 
+from hbp_nrp_virtual_coach.version import VERSION
+
+
 logger = logging.getLogger('Configuration')
 
 
@@ -61,7 +66,7 @@ class Config(dict):
         path = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'config.json')
         if not os.path.isfile(path):
             raise IOError('[config] config.json not found, terminating.')
-        elif not os.access(path, os.R_OK):
+        if not os.access(path, os.R_OK):
             raise IOError('[config] config.json is not readable, terminating.')
 
         # parse the config.json and store all values in this object
@@ -95,7 +100,7 @@ class Config(dict):
 
         # convenience, prepend the proxy url to all proxy services, we cannot do this
         # for the simulation services because they are backend/experiment id specific
-        for k, v in self['proxy-services'].iteritems():
+        for k, v in self['proxy-services'].items():
             self['proxy-services'][k] = '%s/%s' % (self['proxy'][environment], v)
 
     def __validate(self, key, values):
diff --git a/hbp_nrp_virtual_coach/hbp_nrp_virtual_coach/http_client.py b/hbp_nrp_virtual_coach/hbp_nrp_virtual_coach/http_client.py
index e37ce2fe9560d50025eced7605d030d1000de1e8..d6d1e4814ffa8a96a96b5497e4582ef9b101b608 100644
--- a/hbp_nrp_virtual_coach/hbp_nrp_virtual_coach/http_client.py
+++ b/hbp_nrp_virtual_coach/hbp_nrp_virtual_coach/http_client.py
@@ -3,6 +3,10 @@ Base HTTP Client class used so that no matter what http client is used
 the interface to do the request will always be the same
 """
 
+# pylint: disable=W0622
+
+from builtins import object
+
 
 class HTTPClient(object):
     """ Base HTTP Client class """
diff --git a/hbp_nrp_virtual_coach/hbp_nrp_virtual_coach/oidc_http_client.py b/hbp_nrp_virtual_coach/hbp_nrp_virtual_coach/oidc_http_client.py
index 4539d77c1403b75c4e84ef267196cd9838df20d7..c8f9570758b34573cd95e93dfd650dcad380f686 100644
--- a/hbp_nrp_virtual_coach/hbp_nrp_virtual_coach/oidc_http_client.py
+++ b/hbp_nrp_virtual_coach/hbp_nrp_virtual_coach/oidc_http_client.py
@@ -1,7 +1,7 @@
 """ Class which uses the BBP oidc client to do http calls """
 
-from hbp_nrp_virtual_coach.http_client import HTTPClient
 import json
+from hbp_nrp_virtual_coach.http_client import HTTPClient
 
 
 class OIDCHTTPClient(HTTPClient):
@@ -41,14 +41,11 @@ class OIDCHTTPClient(HTTPClient):
         :return: the status of the post request and the content
         :rtype: integer, string
         """
-        if (type(body) == dict):
-            response, content = self.__oidc_client.request(
-                url, method='POST', body=json.dumps(body), headers=self.__headers
-            )
-        else:
-            response, content = self.__oidc_client.request(
-                url, method='POST', body=body, headers=self.__headers
-            )
+        body_ = body if isinstance(body, dict) else json.dumps(body)
+
+        response, content = self.__oidc_client.request(
+            url, method='POST', body=body_, headers=self.__headers
+        )
         return int(response['status']), content
 
     def put(self, url, body):
@@ -58,14 +55,11 @@ class OIDCHTTPClient(HTTPClient):
         :return: the status of the put request and the content
         :rtype: integer, string
         """
-        if (type(body) == dict):
-            response, content = self.__oidc_client.request(
-                url, method='PUT', body=json.dumps(body), headers=self.__headers
-            )
-        else:
-            response, content = self.__oidc_client.request(
-                url, method='PUT', body=body, headers=self.__headers
-            )
+        body_ = body if isinstance(body, dict) else json.dumps(body)
+
+        response, content = self.__oidc_client.request(
+            url, method='PUT', body=body_, headers=self.__headers
+        )
         return int(response['status']), content
 
     def delete(self, url, body):
diff --git a/hbp_nrp_virtual_coach/hbp_nrp_virtual_coach/requests_client.py b/hbp_nrp_virtual_coach/hbp_nrp_virtual_coach/requests_client.py
index 90084942fa2164e98bf4d260ed252e15b7a73c44..a5ee3089d61318ccd4fee30e821279ffc0817baf 100644
--- a/hbp_nrp_virtual_coach/hbp_nrp_virtual_coach/requests_client.py
+++ b/hbp_nrp_virtual_coach/hbp_nrp_virtual_coach/requests_client.py
@@ -1,7 +1,7 @@
 """ Class which uses requests to do http calls """
 
-from hbp_nrp_virtual_coach.http_client import HTTPClient
 import requests
+from hbp_nrp_virtual_coach.http_client import HTTPClient
 
 
 class RequestsClient(HTTPClient):
@@ -25,7 +25,11 @@ class RequestsClient(HTTPClient):
         :param url: The url to do a post to
         :param body: The content to post to the url
         """
-        if (type(body) != dict):
+        try:
+            typeB = isinstance(body)
+        except TypeError:
+            typeB = type(body)
+        if typeB != dict:
             response = requests.post(url, headers=self.__headers, data=body)
         else:
             response = requests.post(url, headers=self.__headers, json=body)
@@ -36,7 +40,11 @@ class RequestsClient(HTTPClient):
         :param url: The url to do a request to
         :param body: The content to put to
         """
-        if (type(body) != dict):
+        try:
+            typeB = isinstance(body)
+        except TypeError:
+            typeB = type(body)
+        if typeB != dict:
             response = requests.put(url, headers=self.__headers, data=body)
         else:
             response = requests.put(url, headers=self.__headers, json=body)
diff --git a/hbp_nrp_virtual_coach/hbp_nrp_virtual_coach/simulation.py b/hbp_nrp_virtual_coach/hbp_nrp_virtual_coach/simulation.py
index 6bba85b4cc461ac431c44dcd6cb7ba94a538d01a..e9043bab997d31640f77a82e27f5699ded5ff802 100644
--- a/hbp_nrp_virtual_coach/hbp_nrp_virtual_coach/simulation.py
+++ b/hbp_nrp_virtual_coach/hbp_nrp_virtual_coach/simulation.py
@@ -25,15 +25,31 @@
 An interface to launch or control a simulation instance.
 """
 
-from hbp_nrp_virtual_coach.config import Config
-from hbp_nrp_virtual_coach.http_client import HTTPClient
+# pylint: disable=W0622
+
+
+from __future__ import print_function
+
+from builtins import str
+from builtins import object
 
-import httplib
-import json
-import logging
-from urllib2 import HTTPError
 import traceback
 import os
+import http.client
+import json
+import logging
+
+from six import string_types
+from future import standard_library
+
+from hbp_nrp_virtual_coach.http_client import HTTPClient
+from hbp_nrp_virtual_coach.config import Config
+try:
+    from urllib2 import HTTPError
+except ImportError:
+    from urllib.error import HTTPError # pylint: disable=F0401
+
+standard_library.install_aliases()
 
 
 class Simulation(object):
@@ -84,10 +100,10 @@ class Simulation(object):
         :param server: A string representing the name of the server to try to launch on.
         :param reservation: A string representing a cluster resource reservation (if any).
         """
-        assert isinstance(experiment_id, str)
-        assert isinstance(experiment_conf, str)
-        assert isinstance(server, str)
-        assert isinstance(reservation, (str, type(None)))
+        assert isinstance(experiment_id, string_types)
+        assert isinstance(experiment_conf, string_types)
+        assert isinstance(server, string_types)
+        assert isinstance(reservation, (string_types, type(None)))
 
         # do not allow reuse of this instance if a simulation has been launched
         if self.__server:
@@ -123,11 +139,11 @@ class Simulation(object):
 
             # check to see if the launch was successful, any other failure return codes
             # such as 404 will trigger an exception by the OIDCClient itself
-            if status_code == httplib.CONFLICT:
+            if status_code == http.client.CONFLICT:
                 raise Exception(
                     'Simulation server is launching another experiment.')
 
-            elif status_code != httplib.CREATED:
+            if status_code != http.client.CREATED:
                 raise Exception(
                     "Simulation responded with HTTP status %s" % status_code)
 
@@ -237,7 +253,7 @@ class Simulation(object):
                       ExDBackend for valid states.
         """
 
-        assert isinstance(state, str)
+        assert isinstance(state, string_types)
 
         # ensure the simulation is started and valid
         if not self.__server or not self.__sim_url:
@@ -251,7 +267,7 @@ class Simulation(object):
         status_code, _ = self.__http_client.put(url, body={'state': state})
 
         # check the return code, this will return OK if the REST call succeeds
-        if status_code != httplib.OK:
+        if status_code != http.client.OK:
             raise Exception(
                 "Unable to set simulation state, HTTP status %s" % status_code)
         self.__logger.info('Simulation state: %s', state)
@@ -283,7 +299,7 @@ class Simulation(object):
             return
 
         # a loading or shutdown message from the simulation, log it to console, but don't propagate
-        elif 'progress' in status:
+        if 'progress' in status:
 
             # ignore duplicate done messages that are used to clear progress bars on the frontend
             if 'subtask' not in status['progress']:
@@ -335,7 +351,7 @@ class Simulation(object):
                          self.__config['simulation-services']['state'])
         status_code, content = self.__http_client.get(url)
 
-        if status_code != httplib.OK:
+        if status_code != http.client.OK:
             raise Exception("Unable to get current simulation state, HTTP status %s"
                             % status_code)
 
@@ -349,7 +365,7 @@ class Simulation(object):
         :param script_type: The script type to be retrieved. Either transfer-functions,
                             state-machines or the brain
         """
-        assert isinstance(script_type, (str, unicode))
+        assert isinstance(string_types, string_types)
 
         if not self.__server or not self.__sim_url:
             raise Exception(
@@ -359,14 +375,14 @@ class Simulation(object):
             raise ValueError("Script type %s not defined. Possible values are: %s"
                              % (script_type, ", ".join(self.__config['simulation-scripts'])))
 
-        self.__logger.info("Attempting to retrieve %s" % script_type)
+        self.__logger.info("Attempting to retrieve %s", script_type)
 
         url = '%s/%s' % (self.__sim_url,
                          self.__config['simulation-scripts'][script_type])
 
         status_code, content = self.__http_client.get(url)
 
-        if status_code != httplib.OK:
+        if status_code != http.client.OK:
             raise Exception("Unable to get simulation %s, HTTP status %s"
                             % (script_type, status_code))
         return json.loads(content)
@@ -379,11 +395,11 @@ class Simulation(object):
         :param script_type: The type of the script to be retrieved (transfer-function, brain,
                             state-machine)
         """
-        assert isinstance(script_name, str)
+        assert isinstance(script_name, string_types)
         script_type_print_format = ' '.join(script_type.split('-')).title()
 
-        self.__logger.info('Attempting to retrieve %s: %s' % (script_type_print_format,
-                                                              script_name))
+        self.__logger.info('Attempting to retrieve %s: %s', script_type_print_format,
+                           script_name)
 
         if not self.__server or not self.__sim_url:
             raise Exception("Simulation has not been created, cannot get %s!"
@@ -393,7 +409,7 @@ class Simulation(object):
         if script_name not in defined_scripts:
             raise ValueError('%s: "%s" does not exist. Please check the %ss ids: \n%s'
                              % (script_type_print_format, script_name, script_type_print_format,
-                                str('\n').join(defined_scripts.keys())))
+                                str('\n').join(list(defined_scripts.keys()))))
 
         return defined_scripts[script_name]
 
@@ -410,8 +426,8 @@ class Simulation(object):
         :param script: A string containing the code of the new script.
         :param new: A flag indicating whether the script is being newly added or modified.
         """
-        assert isinstance(script_name, str)
-        assert isinstance(script, (str, unicode))
+        assert isinstance(script_name, string_types)
+        assert isinstance(script, string_types)
 
         script_type_display = ' '.join(script_type.split('-')).title()
 
@@ -421,8 +437,7 @@ class Simulation(object):
 
         defined_scripts = self.__get_simulation_scripts(script_type)['data']
 
-        self.__logger.info('Attempting to set %s %s' %
-                           (script_type_display, script_name))
+        self.__logger.info('Attempting to set %s %s', script_type_display, script_name)
         url = '%s/%s/%s' % (self.__sim_url, self.__config['simulation-scripts'][script_type],
                             script_name)
         started = False
@@ -451,19 +466,18 @@ class Simulation(object):
                 url = '%s/%s/%s' % (self.__sim_url,
                                     self.__config['simulation-scripts'][script_type], script_name)
                 status_code, _ = self.__http_client.put(url, body=script)
-            if status_code != httplib.OK:
+            if status_code != http.client.OK:
                 raise Exception("Unable to set %s, HTTP status %s"
                                 % (script_type_display, status_code))
-            self.__logger.info("%s '%s' successfully updated" %
-                               (script_type_display, script_name))
+            self.__logger.info("%s '%s' successfully updated", script_type_display, script_name)
         except HTTPError as err:
             self.__logger.info(err)
             if not new:
                 self.__logger.info(
-                    'Attempting to restore the old %s.' % script_type_display)
+                    'Attempting to restore the old %s.', script_type_display)
                 status_code, _ = self.__http_client.put(
                     url, body=script_original)
-                if status_code != httplib.OK:
+                if status_code != http.client.OK:
                     raise Exception("Unable to restore %s, HTTP status %s"
                                     % (script_type_display, status_code))
 
@@ -480,7 +494,7 @@ class Simulation(object):
         :param script_type: A string containing the type of the script to be deleted.
         :param script_name: A string containing the name of the script to be deleted.
         """
-        assert isinstance(script_name, str)
+        assert isinstance(script_name, string_types)
 
         script_type_display = ' '.join(script_type.split('-')).title()
 
@@ -495,31 +509,37 @@ class Simulation(object):
                              % (script_type_display, script_name, script_type_display,
                                 str('\n'.join(script_list))))
 
-        self.__logger.info('Attempting to delete %s %s' %
-                           (script_type_display, script_name))
+        self.__logger.info('Attempting to delete %s %s', script_type_display, script_name)
         url = '%s/%s/%s' % (self.__sim_url, self.__config['simulation-scripts'][script_type],
                             script_name)
         status_code = self.__http_client.delete(url, body=script_name)
 
-        if status_code != httplib.OK:
+        if status_code != http.client.OK:
             raise Exception("Unable to delete %s, HTTP status %s"
                             % (script_type_display, status_code))
-        self.__logger.info("%s '%s' successfully deleted" %
-                           (script_type_display, script_name))
+        self.__logger.info("%s '%s' successfully deleted", script_type_display, script_name)
 
     def print_transfer_functions(self):
         """
         Prints a list of the transfer-function names defined in the experiment.
         """
-        print "\n".join(self.__get_simulation_scripts(
-            'transfer-function')['data'].keys())
+        printStr = '\n'.join(list(self.__get_simulation_scripts(
+            'transfer-function')['data'].keys()))
+        try:
+            print(unicode(printStr))
+        except NameError:
+            print(printStr)
 
     def print_state_machines(self):
         """
         Prints a list of the state-machine names defined in the experiment.
         """
-        print "\n".join(self.__get_simulation_scripts(
-            'state-machine')['data'].keys())
+        printStr = "\n".join(list(self.__get_simulation_scripts(
+            'state-machine')['data'].keys()))
+        try:
+            print(unicode(printStr))
+        except NameError:
+            print(printStr)
 
     def get_transfer_function(self, transfer_function_name):
         """
@@ -575,15 +595,15 @@ class Simulation(object):
                              self.__config['simulation-scripts'][experiment_data_type])
         else:
             url = '%s/%s/%s' % (self.__config['proxy-services']['save-data'],
-                                           self.__experiment_id,
-                                           self.__config['proxy-save'][experiment_data_type])
+                                self.__experiment_id,
+                                self.__config['proxy-save'][experiment_data_type])
         try:
             method_fun = getattr(self.__http_client, method)
             status_code, _ = method_fun(url, body=experiment_data)
 
-            if status_code != httplib.OK:
+            if status_code != http.client.OK:
                 raise Exception('Error status code %s' % status_code)
-            self.__logger.info("Saved %s." % experiment_data_type)
+            self.__logger.info("Saved %s.", experiment_data_type)
         except Exception as err:
             self.__logger.info(err)
             raise Exception("Failed to save %s." % experiment_data_type)
@@ -592,8 +612,8 @@ class Simulation(object):
         """
         Saves the current transfer functions to the storage
         """
-        transfer_functions = self.__get_simulation_scripts(
-            'transfer-function')['data'].keys()
+        transfer_functions = list(self.__get_simulation_scripts(
+            'transfer-function')['data'].keys())
         transfer_functions_list = []
         for tf in transfer_functions:
             transfer_functions_list.append(self.get_transfer_function(str(tf)))
@@ -607,7 +627,7 @@ class Simulation(object):
         Saves the current state machines to the storage
         """
         state_machines_dict = {}
-        for sm in self.__get_simulation_scripts('state-machine')['data'].keys():
+        for sm in list(self.__get_simulation_scripts('state-machine')['data'].keys()):
             state_machines_dict[sm] = self.get_state_machine(str(sm))
 
         self.__save_experiment_data('state-machine', {'experiment': self.__experiment_id,
@@ -623,7 +643,7 @@ class Simulation(object):
         # Remove the regex entry from the populations dictionary if it is available. The regex
         # entry is provided by the frontend to check for duplicate population names and should be
         # refactored.
-        if 'regex' in populations.values()[0]:
+        if 'regex' in list(populations.values())[0]:
             for pop in populations:
                 del populations[pop]['regex']
 
@@ -714,7 +734,7 @@ class Simulation(object):
             started = True
         try:
             status_code, _ = self.__http_client.put(url, body=body)
-            if status_code != httplib.OK:
+            if status_code != http.client.OK:
                 raise Exception(
                     "Unable to set Populations, HTTP status %s" % status_code)
             self.__logger.info("Populations successfully updated.")
@@ -726,7 +746,7 @@ class Simulation(object):
             body['brain_type'] = old_brain_type
 
             status_code, _ = self.__http_client.put(url, body=body)
-            if status_code != httplib.OK:
+            if status_code != http.client.OK:
                 raise Exception(
                     "Unable to restore Brain, HTTP status %s" % status_code)
 
@@ -746,7 +766,7 @@ class Simulation(object):
                                    integers, lists of integers or python slices.
         """
 
-        assert isinstance(brain_script, (str, unicode))
+        assert isinstance(brain_script, (string_types, string_types))
         assert isinstance(neural_population, dict)
 
         if not self.__server or not self.__sim_url:
@@ -776,7 +796,7 @@ class Simulation(object):
         # check for current simulation state and pause simulation if it's running
         try:
             status_code, _ = self.__http_client.put(url, body=body)
-            if status_code != httplib.OK:
+            if status_code != http.client.OK:
                 raise Exception(
                     "Unable to set Brain, HTTP status %s" % status_code)
             self.__logger.info("Brain successfully updated.")
@@ -786,7 +806,7 @@ class Simulation(object):
             body['data'] = old_brain
             body['populations'] = old_populations
             status_code, _ = self.__http_client.put(url, body=body)
-            if status_code != httplib.OK:
+            if status_code != http.client.OK:
                 raise Exception(
                     "Unable to restore Brain, HTTP status %s" % status_code)
 
@@ -842,27 +862,27 @@ class Simulation(object):
                            config file.
         """
 
-        assert isinstance(reset_type, (str, unicode))
+        assert isinstance(reset_type, (string_types, string_types))
 
         # NRRPLT-7855
         if reset_type in ['full', 'world']:
             raise ValueError('Reset %s temporarily disabled due to known Gazebo issue' % reset_type)
 
-        if reset_type not in self.__config['reset-services'].keys():
+        if reset_type not in self.__config['reset-services']:
             raise ValueError('Undefined reset type. Possible values are: %s'
-                             % ", ".join(self.__config['reset-services'].keys()))
+                             % ", ".join(list(self.__config['reset-services'].keys())))
 
         # pausing simulation before attempting to reset
         self.pause()
 
-        self.__logger.info("Attempting to reset %s" % reset_type)
+        self.__logger.info("Attempting to reset %s", reset_type)
         url = '%s/%s/%s' % (self.__sim_url, self.__experiment_id,
                             self.__config['simulation-services']['reset'])
         body = {'resetType': self.__config['reset-services'][reset_type]}
         status_code, _ = self.__http_client.put(url, body=body)
 
         # check the return code, this will return OK if the REST call succeeds
-        if status_code != httplib.OK:
+        if status_code != http.client.OK:
             self.start()
             raise Exception(
                 "Unable to reset simulation, HTTP status %s" % status_code)
@@ -885,7 +905,7 @@ class Simulation(object):
 
         """
 
-        assert isinstance(cmd, str)
+        assert isinstance(cmd, string_types)
 
         # ensure the simulation is started and valid
         if not self.__server or not self.__sim_url:
@@ -901,7 +921,7 @@ class Simulation(object):
         status_code, result = self.__http_client.post(url, body=cmd)
 
         # check the return code, this will return OK if the REST call succeeds
-        if status_code != httplib.OK:
+        if status_code != http.client.OK:
             raise Exception(
                 "Unable to send command to recorder, HTTP status %s" % status_code)
 
@@ -932,9 +952,9 @@ class Simulation(object):
                 filename = os.path.splitext(filename)[0]
                 filename = filename + '.txt'
                 self.__vc.set_experiment_file(self.__experiment_id,
-                                                os.path.join('recordings',
-                                                             filename),
-                                                description)
+                                              os.path.join('recordings',
+                                                           filename),
+                                              description)
 
         self.__send_recorder_cmd('reset')
 
diff --git a/hbp_nrp_virtual_coach/hbp_nrp_virtual_coach/tests/test_config.py b/hbp_nrp_virtual_coach/hbp_nrp_virtual_coach/tests/test_config.py
index 6f13d2116d8d12457ef37d1e407bcd50cc91a463..2bb14bdc7d30addafee2903c6a778c2c5e7c65d1 100644
--- a/hbp_nrp_virtual_coach/hbp_nrp_virtual_coach/tests/test_config.py
+++ b/hbp_nrp_virtual_coach/hbp_nrp_virtual_coach/tests/test_config.py
@@ -64,7 +64,7 @@ class TestConfig(unittest.TestCase):
     def test_update_proxy_services(self):
 
         config = Config('local')
-        for k, v in config['proxy-services'].iteritems():
+        for k, v in config['proxy-services'].items():
             self.assertEqual(v, '%s/%s' % (self._conf['proxy']['local'], self._conf['proxy-services'][k]))
 
     def test_environment_from_version(self):
@@ -73,7 +73,7 @@ class TestConfig(unittest.TestCase):
         environment = 'dev' if 'dev' in VERSION else 'staging'
 
         config = Config(None)
-        for k, v in config['proxy-services'].iteritems():
+        for k, v in config['proxy-services'].items():
             self.assertEqual(v, '%s/%s' % (self._conf['proxy'][environment], self._conf['proxy-services'][k]))
 
     @patch('hbp_nrp_virtual_coach.config.json.load')
diff --git a/hbp_nrp_virtual_coach/hbp_nrp_virtual_coach/tests/test_simulation.py b/hbp_nrp_virtual_coach/hbp_nrp_virtual_coach/tests/test_simulation.py
index a69ff870915c85a8d6a836205e203612707ffe32..e4747101fe511194939d29e1bb763af04f1c0aee 100644
--- a/hbp_nrp_virtual_coach/hbp_nrp_virtual_coach/tests/test_simulation.py
+++ b/hbp_nrp_virtual_coach/hbp_nrp_virtual_coach/tests/test_simulation.py
@@ -25,6 +25,9 @@
 Unit tests for the Virtual Coach simulation interface.
 """
 
+from future import standard_library
+standard_library.install_aliases()
+from builtins import object
 from hbp_nrp_virtual_coach.simulation import Simulation
 
 from hbp_nrp_virtual_coach.requests_client import RequestsClient
@@ -33,10 +36,10 @@ from hbp_nrp_virtual_coach.config import Config
 from mock import Mock, patch, call
 import unittest
 
-import httplib
-from urllib2 import HTTPError
+import http.client
+from urllib.error import HTTPError
 import json
-from StringIO import StringIO
+from io import StringIO
 
 
 class TestSimulation(unittest.TestCase):
@@ -48,7 +51,7 @@ class TestSimulation(unittest.TestCase):
         get_response = (None,
             '{"serverJobLocation": "mock-location",' \
             '"gzweb": {"nrp-services": "mock-services"}}')
-        post_response = (httplib.CREATED, '{"simulationID": "12"}')
+        post_response = (http.client.CREATED, '{"simulationID": "12"}')
         self._sim._Simulation__http_client.get = Mock(return_value=get_response)
         self._sim._Simulation__http_client.post = Mock(return_value=post_response)
 
@@ -78,7 +81,7 @@ class TestSimulation(unittest.TestCase):
     @patch('hbp_nrp_virtual_coach.simulation.traceback')
     def test_failed_create_conflict(self, mocked_traceback):
         self.setUpForLaunch()
-        self._sim._Simulation__http_client.post = Mock(return_value=(httplib.CONFLICT, '{}'))
+        self._sim._Simulation__http_client.post = Mock(return_value=(http.client.CONFLICT, '{}'))
 
         self.assertEqual(self._sim.launch('id', 'conf', 'server-name', None), False)
         self._sim._Simulation__http_client.post.assert_called_once()
@@ -87,7 +90,7 @@ class TestSimulation(unittest.TestCase):
     @patch('hbp_nrp_virtual_coach.simulation.traceback')
     def test_failed_create_other(self, mocked_traceback):
         self.setUpForLaunch()
-        self._sim._Simulation__http_client.post = Mock(return_value=(httplib.NOT_FOUND, '{}'))
+        self._sim._Simulation__http_client.post = Mock(return_value=(http.client.NOT_FOUND, '{}'))
 
         self.assertEqual(self._sim.launch('id', 'conf', 'server-name', None), False)
         self._sim._Simulation__http_client.post.assert_called_once()
@@ -127,15 +130,25 @@ class TestSimulation(unittest.TestCase):
             if name == 'rospy' or name == 'std_msgs.msg' or name == 'cle_ros_msgs.msg':
                 return mock_rospy
             return real_import(name, *args)
-
-        with patch('__builtin__.__import__', side_effect=mock_import):
-            self.assertEqual(self._sim.launch('id', 'conf', 'server-name', 'reservation'), True)
-            mock_rospy.Subscriber.assert_has_calls([call('/ros_cle_simulation/status',
-                                                         mock_rospy.String,
-                                                         self._sim._Simulation__on_status),
-                                                   call('/ros_cle_simulation/cle_error',
-                                                        mock_rospy.CLEError,
-                                                        self._sim._Simulation__on_error)])
+       
+        try:
+            with patch('__builtin__.__import__', side_effect=mock_import):
+                self.assertEqual(self._sim.launch('id', 'conf', 'server-name', 'reservation'), True)
+                mock_rospy.Subscriber.assert_has_calls([call('/ros_cle_simulation/status',
+                                                             mock_rospy.String,
+                                                             self._sim._Simulation__on_status),
+                                                       call('/ros_cle_simulation/cle_error',
+                                                            mock_rospy.CLEError,
+                                                            self._sim._Simulation__on_error)])
+        except ModuleNotFoundError:
+            with patch('builtins.__import__', side_effect=mock_import):
+                self.assertEqual(self._sim.launch('id', 'conf', 'server-name', 'reservation'), True)
+                mock_rospy.Subscriber.assert_has_calls([call('/ros_cle_simulation/status',
+                                                             mock_rospy.String,
+                                                             self._sim._Simulation__on_status),
+                                                       call('/ros_cle_simulation/cle_error',
+                                                            mock_rospy.CLEError,
+                                                            self._sim._Simulation__on_error)])
 
     def test_create_without_rospy(self):
 
@@ -159,9 +172,14 @@ class TestSimulation(unittest.TestCase):
 
         self._sim._Simulation__logger = Mock()
 
-        with patch('__builtin__.__import__', side_effect=mock_import_fail):
-            self.assertEqual(self._sim.launch('id', 'conf', 'server-name', 'reservation'), True)
-            mock_rospy.assert_not_called()
+        try:
+            with patch('__builtin__.__import__', side_effect=mock_import_fail):
+                self.assertEqual(self._sim.launch('id', 'conf', 'server-name', 'reservation'), True)
+                mock_rospy.assert_not_called()
+        except ModuleNotFoundError:
+            with patch('builtins.__import__', side_effect=mock_import_fail):
+                self.assertEqual(self._sim.launch('id', 'conf', 'server-name', 'reservation'), True)
+                mock_rospy.assert_not_called()
 
     def test_set_state_asserts(self):
         self.assertRaises(AssertionError, self._sim._Simulation__set_state, None)
@@ -172,7 +190,7 @@ class TestSimulation(unittest.TestCase):
         self._sim._Simulation__sim_url = 'url'
 
         # mock the HTTP call
-        self._sim._Simulation__http_client.put = Mock(return_value=(httplib.OK, None))
+        self._sim._Simulation__http_client.put = Mock(return_value=(http.client.OK, None))
 
         self._sim._Simulation__set_state('initialized')
         self._sim._Simulation__http_client.put.assert_called_once()
@@ -182,7 +200,7 @@ class TestSimulation(unittest.TestCase):
         self._sim._Simulation__sim_url = 'url'
 
         # mock the HTTP call
-        self._sim._Simulation__http_client.put = Mock(return_value=(httplib.NOT_FOUND, None))
+        self._sim._Simulation__http_client.put = Mock(return_value=(http.client.NOT_FOUND, None))
 
         self.assertRaises(Exception, self._sim._Simulation__set_state, 'started')
         self._sim._Simulation__http_client.put.assert_called_once()
@@ -215,7 +233,7 @@ class TestSimulation(unittest.TestCase):
         self._sim._Simulation__sim_url = 'url'
 
         # mock the OIDC call
-        self._sim._Simulation__http_client.get = Mock(return_value=(httplib.OK,
+        self._sim._Simulation__http_client.get = Mock(return_value=(http.client.OK,
                                                              '{"state": "{}"}'))
 
         self._sim.get_state()
@@ -228,7 +246,7 @@ class TestSimulation(unittest.TestCase):
         self._sim._Simulation__sim_url = 'url'
 
         # mock the OIDC call
-        self._sim._Simulation__http_client.get = Mock(return_value=(httplib.NOT_FOUND,
+        self._sim._Simulation__http_client.get = Mock(return_value=(http.client.NOT_FOUND,
                                                              None))
         self.assertRaises(Exception, self._sim.get_state)
         self._sim._Simulation__http_client.get.assert_called_once()
@@ -248,7 +266,7 @@ class TestSimulation(unittest.TestCase):
 
         # mock the oidc call, the get_all_transfer_function call, the start call, the pause call
         # and the get_state call
-        self._sim._Simulation__http_client.get = Mock(return_value=(httplib.NOT_FOUND,
+        self._sim._Simulation__http_client.get = Mock(return_value=(http.client.NOT_FOUND,
                                                              None))
 
         self.assertRaises(Exception, self._sim._Simulation__get_simulation_scripts, 'foo')
@@ -277,16 +295,16 @@ class TestSimulation(unittest.TestCase):
 
         self.assertRaises(Exception, self._sim.add_transfer_function, 'foo')
 
-        http_error = HTTPError("url", 404, "message", {}, file)
+        http_error = HTTPError("url", 404, "message", {}, None)
 
         self._sim._Simulation__http_client.put.side_effect = [http_error,
-                                                            (httplib.NOT_FOUND,
+                                                            (http.client.NOT_FOUND,
                                                              None)]
 
         self.assertRaises(Exception, self._sim.edit_state_machine, 'foo', 'import os\n')
 
         self._sim._Simulation__http_client.put.side_effect = [http_error,
-                                                            (httplib.OK, None)]
+                                                            (http.client.OK, None)]
         self.assertRaises(Exception, self._sim.add_state_machine, 'bar', 'import os\n')
 
     def test_get_transfer_function_asserts(self):
@@ -332,7 +350,7 @@ class TestSimulation(unittest.TestCase):
         self._sim._Simulation__get_simulation_scripts.return_value = {'brain_type': 'py',
                                                                 'data_type': 'text'}
         self._sim._Simulation__http_client.put = Mock()
-        self._sim._Simulation__http_client.put.return_value = (httplib.OK,
+        self._sim._Simulation__http_client.put.return_value = (http.client.OK,
                                                              '{"data": {"foo": ""}}')
         self._sim.get_state = Mock()
         self._sim.get_state.return_value = 'started'
@@ -352,15 +370,15 @@ class TestSimulation(unittest.TestCase):
         self._sim._Simulation__http_client.put.return_value = (HTTPError, None)
         self.assertRaises(Exception, self._sim.edit_brain, 'import os\n')
 
-        http_error = HTTPError("url", 404, "message", {}, file)
+        http_error = HTTPError("url", 404, "message", {}, None)
 
         self._sim._Simulation__http_client.put.side_effect = [http_error,
-                                                            (httplib.NOT_FOUND,
+                                                            (http.client.NOT_FOUND,
                                                              None)]
         self.assertRaises(Exception, self._sim.edit_brain, 'foo')
 
         self._sim._Simulation__http_client.put.side_effect = [http_error,
-                                                            (httplib.OK, None)]
+                                                            (http.client.OK, None)]
         self.assertRaises(Exception, self._sim.edit_brain, 'foo')
 
     def test_edit_populations(self):
@@ -376,7 +394,7 @@ class TestSimulation(unittest.TestCase):
         self._sim._Simulation__get_simulation_scripts = Mock(return_value={'data_type': 'foo',
                                                                            'brain_type': 'foo',
                                                                            'brain_populations': 'bar'})
-        self._sim._Simulation__http_client.put = Mock(return_value=(httplib.OK,
+        self._sim._Simulation__http_client.put = Mock(return_value=(http.client.OK,
                                                                     {}))
 
         self._sim.edit_populations({'foo': 'bar'})
@@ -401,9 +419,9 @@ class TestSimulation(unittest.TestCase):
 
         # mock the http call, the get_all_transfer_function call, the start call, the pause call
         # and the get_state call
-        self._sim._Simulation__http_client.get = Mock(return_value=(httplib.OK,
+        self._sim._Simulation__http_client.get = Mock(return_value=(http.client.OK,
                                                              '{"data": {"foo": ""}}'))
-        self._sim._Simulation__http_client.put = Mock(return_value=(httplib.OK,None))
+        self._sim._Simulation__http_client.put = Mock(return_value=(http.client.OK,None))
 
         self._sim._Simulation__get_all_transfer_functions = Mock()
         self._sim._Simulation__get_all_transfer_functions.return_value = {'foo': ''}
@@ -432,7 +450,7 @@ class TestSimulation(unittest.TestCase):
 
         # mock the http call, the get_all_transfer_function call, the start call, the pause call
         # and the get_state call
-        self._sim._Simulation__http_client.delete = Mock(return_value=httplib.OK)
+        self._sim._Simulation__http_client.delete = Mock(return_value=http.client.OK)
 
         self._sim._Simulation__get_simulation_scripts = Mock()
         self._sim._Simulation__get_simulation_scripts.return_value = {'data': {'foo': '', 'bar': ''}}
@@ -445,7 +463,7 @@ class TestSimulation(unittest.TestCase):
 
         self.assertRaises(ValueError, self._sim.delete_state_machine, 'nonExistentScript')
 
-        self._sim._Simulation__http_client.delete.return_value = (httplib.NOT_FOUND,
+        self._sim._Simulation__http_client.delete.return_value = (http.client.NOT_FOUND,
                                                              None)
         self.assertRaises(Exception, self._sim.delete_state_machine, 'foo')
 
@@ -475,7 +493,7 @@ class TestSimulation(unittest.TestCase):
 
         self._sim._Simulation__headers = {}
 
-        self._sim._Simulation__http_client.put = Mock(return_value=(httplib.OK, None))
+        self._sim._Simulation__http_client.put = Mock(return_value=(http.client.OK, None))
 
         self._sim.save_transfer_functions()
         self._sim._Simulation__http_client.put.assert_called_once_with(
@@ -504,7 +522,7 @@ class TestSimulation(unittest.TestCase):
             '%s/%s/brain' % (proxyurl, exp_id),
             body={'populations': populations,'brain': 'some brain code'})
 
-        self._sim._Simulation__http_client.post = Mock(return_value=(httplib.OK, None))
+        self._sim._Simulation__http_client.post = Mock(return_value=(http.client.OK, None))
         self._sim.save_world()
         self._sim._Simulation__http_client.post.assert_called_once_with(
             '%s/sdf_world' % (self._sim._Simulation__sim_url),
@@ -521,7 +539,18 @@ class TestSimulation(unittest.TestCase):
 
         self._sim.print_transfer_functions()
         self._sim.print_state_machines()
-        self.assertEqual(mock_stdout.getvalue().strip(), 'foobar\nfoo\nbar\nfoobar\nfoo\nbar')
+        # The order in which 'foobar', 'foo' and 'bar' appear is no longer guaranteed in python 3.3+
+        printStr = mock_stdout.getvalue().strip()
+        self.assertEqual(len(printStr), 29)
+        n = printStr.count('\n')
+        self.assertEqual(n, 5)
+        n = printStr.count('foobar')
+        self.assertEqual(n, 2)
+        n = printStr.count('foo')
+        self.assertEqual(n, 4)
+        n = printStr.count('bar')
+        self.assertEqual(n, 4)
+        #self.assertEqual(mock_stdout.getvalue().strip(), 'foobar\nfoo\nbar\nfoobar\nfoo\nbar')
         self._sim._Simulation__get_simulation_scripts.assert_called_twice()
 
     def test_register_status_callback(self):
@@ -621,14 +650,14 @@ class TestSimulation(unittest.TestCase):
         self._sim.pause = Mock()
 
         self._sim._Simulation__http_client.put = Mock()
-        self._sim._Simulation__http_client.put.return_value = (httplib.OK, None)
+        self._sim._Simulation__http_client.put.return_value = (http.client.OK, None)
 
         # NRRPLT-7855
         self.assertRaises(ValueError, self._sim.reset, 'world')
         # self._sim.reset('world')
         # self._sim._Simulation__http_client.put.assert_called_once()
 
-        self._sim._Simulation__http_client.put.return_value = (httplib.NOT_FOUND,
+        self._sim._Simulation__http_client.put.return_value = (http.client.NOT_FOUND,
                                                              None)
         self._sim.start = Mock()
 
@@ -653,7 +682,7 @@ class TestSimulation(unittest.TestCase):
         self._sim._Simulation__sim_url = 'url'
         self._sim._Simulation__vc = Mock()
         self._sim._Simulation__http_client.post = Mock()
-        self._sim._Simulation__http_client.post.return_value = (httplib.OK, '{"filename":"name.txt"}')
+        self._sim._Simulation__http_client.post.return_value = (http.client.OK, '{"filename":"name.txt"}')
         self._sim.start_recording()
         self._sim._Simulation__http_client.post.assert_called_with(u'url/recorder/start', body='start')
         self._sim.pause_recording()
diff --git a/hbp_nrp_virtual_coach/hbp_nrp_virtual_coach/tests/test_virtual_coach.py b/hbp_nrp_virtual_coach/hbp_nrp_virtual_coach/tests/test_virtual_coach.py
index 5a98e3a1f98fa89440af298d1245c78cd12c2c6e..48fa333e0af15d1d4a9b20ab489d81199b81cb96 100644
--- a/hbp_nrp_virtual_coach/hbp_nrp_virtual_coach/tests/test_virtual_coach.py
+++ b/hbp_nrp_virtual_coach/hbp_nrp_virtual_coach/tests/test_virtual_coach.py
@@ -25,9 +25,28 @@
 Unit tests for the Virtual Coach main interface.
 """
 
+from future import standard_library
+standard_library.install_aliases()
+from builtins import object
 from hbp_nrp_virtual_coach.virtual_coach import VirtualCoach
 
-from bbp_client.oidc.client import BBPOIDCClient
+try:
+    from bbp_client.oidc.client import BBPOIDCClient
+except ImportError: 
+    # Currently, bbp_client is still Python2
+    import urllib.parse, urllib.error, sys, os, builtins
+    from bbp_client.oidc.openidconnect import error
+    from bbp_client.oidc.oauth2client import clientsecrets
+    # Python3 only allows absolute paths. In order to allow bbp_client to import 
+    # using relative paths, the following two folders have to be added to the import path:
+    sys.path.append(os.path.dirname(error.__file__))
+    sys.path.append(os.path.dirname(clientsecrets.__file__))
+    # Rename the following Python2-packages such that they refer to their Python3 counterparts:
+    sys.modules['urlparse'] = urllib.parse
+    sys.modules['urllib2'] = urllib.error
+    sys.modules['__builtin__'] = builtins
+    from bbp_client.oidc.client import BBPOIDCClient
+    
 
 from mock import Mock, patch, MagicMock
 import unittest
@@ -38,7 +57,7 @@ import logging
 import copy
 from dateutil import parser
 import json
-from StringIO import StringIO
+from io import StringIO
 import os
 
 
@@ -56,7 +75,11 @@ class TestVirtualCoach(unittest.TestCase):
         def rospy_import_fail(name, globals=globals(), locals=locals(), fromlist=[], level=-1):
             if name == 'rospy':
                 raise ImportError('no ROS for tests')
-            return realimport(name, globals, locals, fromlist, level)
+            try:
+                return realimport(name, globals, locals, fromlist, level)
+            except:
+                pass
+            return realimport(name, globals, locals, fromlist, 0)
         builtins.__import__ = rospy_import_fail
 
         self._tests_path = os.path.dirname(os.path.realpath(__file__))
diff --git a/hbp_nrp_virtual_coach/hbp_nrp_virtual_coach/virtual_coach.py b/hbp_nrp_virtual_coach/hbp_nrp_virtual_coach/virtual_coach.py
index b5a5e896e258fd9485c85fc58409ae2c20644823..b59b32bf2439bfc854cbc9e899db12ddc5ad32ba 100644
--- a/hbp_nrp_virtual_coach/hbp_nrp_virtual_coach/virtual_coach.py
+++ b/hbp_nrp_virtual_coach/hbp_nrp_virtual_coach/virtual_coach.py
@@ -25,25 +25,42 @@
 Virtual Coach main entry point for interacting with the experiment list and launching simulations.
 """
 
-from hbp_nrp_virtual_coach.config import Config
-from hbp_nrp_virtual_coach.simulation import Simulation
-from hbp_nrp_virtual_coach.requests_client import RequestsClient
-from hbp_nrp_virtual_coach.oidc_http_client import OIDCHTTPClient
+# pylint: disable=W0622
+
+from __future__ import print_function
+
+from builtins import str
+from builtins import object
 
-from datetime import datetime, timedelta
-from collections import defaultdict
-from dateutil import parser, tz
 import json
-import requests
 import logging
 import getpass
-import httplib
+import http.client
 import os
 import zipfile
 import tempfile
-from texttable import Texttable
+
 from copy import copy
 
+from datetime import datetime, timedelta
+from collections import defaultdict
+from dateutil import parser, tz
+
+import requests
+
+from texttable import Texttable
+
+from six import string_types
+
+from future import standard_library
+
+from hbp_nrp_virtual_coach.config import Config
+from hbp_nrp_virtual_coach.simulation import Simulation
+from hbp_nrp_virtual_coach.requests_client import RequestsClient
+from hbp_nrp_virtual_coach.oidc_http_client import OIDCHTTPClient
+
+standard_library.install_aliases()
+
 logger_format = '%(levelname)s: [%(asctime)s - %(name)s] %(message)s'
 logging.basicConfig(format=logger_format, level=logging.INFO)
 logger = logging.getLogger('VirtualCoach')
@@ -82,10 +99,10 @@ class VirtualCoach(object):
         :param storage_password: (optional) A string representing the Storage Server password. If
                                  not supplied, the user will be prompted to enter a password.
         """
-        assert isinstance(environment, (str, type(None)))
-        assert isinstance(oidc_username, (str, type(None)))
-        assert isinstance(oidc_token, (str, type(None)))
-        assert isinstance(storage_username, (str, type(None)))
+        assert isinstance(environment, (string_types, type(None)))
+        assert isinstance(oidc_username, (string_types, type(None)))
+        assert isinstance(oidc_token, (string_types, type(None)))
+        assert isinstance(storage_username, (string_types, type(None)))
 
         # ROS node and logger configuration only if rospy is available
         # pylint: disable=import-error
@@ -124,7 +141,7 @@ class VirtualCoach(object):
             # Set self.__http_headers: it is also used
             # as an argument of requests.post() and requests.get()
             self.__http_headers = {'Content-Type': 'application/json',
-                                    'Authorization': authorization}
+                                   'Authorization': authorization}
             self.__http_client.set_headers(self.__http_headers)
         # if a Storage Server username is provided, attempt to login
         elif storage_username:
@@ -138,7 +155,7 @@ class VirtualCoach(object):
                 storage_username, storage_password
             )
             self.__http_headers = {'Content-Type': 'application/json',
-                                    'Authorization': authorization}
+                                   'Authorization': authorization}
             self.__http_client = RequestsClient(self.__http_headers)
         else:
             raise Exception('Virtual Coach instantiated without storage server credentials or oidc'
@@ -165,7 +182,7 @@ class VirtualCoach(object):
         # construct the table of experiments with only minimal useful information
         table = Texttable()
         table.header(['Configuration', 'Name', 'Configuration Path', 'Description'])
-        for name, v in sorted(exp_list.iteritems(), key=lambda x: x[1]['configuration']['name']):
+        for name, v in sorted(iter(exp_list.items()), key=lambda x: x[1]['configuration']['name']):
             if v['configuration']['maturity'] != 'production' and not dev:
                 continue
             if dev:
@@ -176,7 +193,7 @@ class VirtualCoach(object):
 
         # display the table
         logger.info('List of production%s experiments:', '' if not dev else ' and development')
-        print table.draw()
+        print(table.draw())
 
     def print_running_experiments(self, cloned=False):
         """
@@ -189,7 +206,7 @@ class VirtualCoach(object):
         # construct a table with minimal useful information
         table = Texttable()
         table.header(['Configuration', 'Owner', 'Time', 'Timeout', 'State', 'Server'])
-        for name, v in sorted(exp_list.iteritems(), key=lambda x: x[1]['configuration']['name']):
+        for name, v in sorted(iter(exp_list.items()), key=lambda x: x[1]['configuration']['name']):
 
             # for any running experiments of this type
             for server in v['joinableServers']:
@@ -219,7 +236,7 @@ class VirtualCoach(object):
 
         # display the table
         logger.info('All running experiments:')
-        print table.draw()
+        print(table.draw())
 
     def print_available_servers(self):
         """
@@ -232,12 +249,16 @@ class VirtualCoach(object):
         servers = [server['id'] for server in available_servers]
 
         # add a display value if there are no available servers
-        if len(servers) == 0:
+        if not servers:
             servers = ['No available servers.']
 
         # print the list of available servers
         logger.info('Available servers:')
-        print '\n'.join(servers)
+        printStr = '\n'.join(servers)
+        try:
+            print(unicode(printStr))
+        except NameError:
+            print(printStr)
         return servers
 
     def launch_experiment(self, experiment_id, server=None, reservation=None, cloned=True):
@@ -255,9 +276,9 @@ class VirtualCoach(object):
         :param cloned: (optional) True or False depending on if the launched
                        is a cloned experiment or not.
         """
-        assert isinstance(experiment_id, str)
-        assert isinstance(server, (str, type(None)))
-        assert isinstance(reservation, (str, type(None)))
+        assert isinstance(experiment_id, string_types)
+        assert isinstance(server, (string_types, type(None)))
+        assert isinstance(reservation, (string_types, type(None)))
 
         # retrieve the list of cloned experiments to verify that the given id is valid for the
         # backend
@@ -286,15 +307,15 @@ class VirtualCoach(object):
             servers = [server]
 
         # if there are no available servers, abort
-        if len(servers) == 0:
+        if not servers:
             raise ValueError('No available servers for %s, try again later.' % experiment_id)
 
         # attempt to launch the simulation on all server targets, on success return an interface
         # to the simulation
         sim = Simulation(self.__http_client, self.__config, self)
-        for server in servers:
+        for server_i in servers:
             try:
-                if sim.launch(experiment_id, str(experiment_conf), str(server),
+                if sim.launch(experiment_id, str(experiment_conf), str(server_i),
                               reservation, cloned):
                     return sim
 
@@ -314,11 +335,11 @@ class VirtualCoach(object):
         :param exp_id: The id of the experiment to be cloned
         :returns: The ID of the cloned experiment
         """
-        assert isinstance(exp_id, str)
+        assert isinstance(exp_id, string_types)
         exp = self.__get_experiment_list()
-        if exp_id not in exp.keys():
+        if exp_id not in list(exp.keys()):
             raise ValueError('Experiment ID: "%s" is invalid, please check the list of all '
-                             'experiments: \n%s' % (exp_id, '\n'.join(exp.keys())))
+                             'experiments: \n%s' % (exp_id, '\n'.join(list(exp.keys()))))
 
         exp_config_path = exp[exp_id]['configuration']['experimentConfiguration']
         body = {'expPath': exp_config_path}
@@ -326,9 +347,8 @@ class VirtualCoach(object):
             self.__config['proxy-services']['experiment-clone'], body=body)
         if status_code != 200:
             raise Exception('Cloning Experiment failed, Status Code: %s' % status_code)
-        else:
-            logger.info('Experiment "%s" cloned successfully', exp_id)
-            return content
+        logger.info('Experiment "%s" cloned successfully', exp_id)
+        return content
 
     def delete_cloned_experiment(self, exp_id):
         """
@@ -353,7 +373,7 @@ class VirtualCoach(object):
         :returns: A dict containing the ID of the cloned experiment and the ID of the original
                   experiment. Dict Keys are: 'clonedExp' and 'originalExp'
         """
-        assert isinstance(experiment_id, str)
+        assert isinstance(experiment_id, string_types)
 
         exp_list = self.__get_experiment_list(cloned=True)
         if experiment_id not in exp_list:
@@ -373,9 +393,8 @@ class VirtualCoach(object):
                             headers=self.__http_headers)
         if res.status_code != 200:
             raise Exception('Cloning Experiment failed, Status Code: %s' % res.status_code)
-        else:
-            logger.info('Experiment "%s" cloned successfully', experiment_id)
-            return res.content
+        logger.info('Experiment "%s" cloned successfully', experiment_id)
+        return res.content
 
     def print_cloned_experiments(self):
         """
@@ -387,7 +406,7 @@ class VirtualCoach(object):
         table.header(['Name'])
         for experiment in exp_list:
             table.add_row([experiment])
-        print table.draw()
+        print(table.draw())
 
     def __get_storage_token(self, user_name, password):
         """
@@ -396,16 +415,15 @@ class VirtualCoach(object):
         :param user_name:  string representing the Storage Server username
         :param password:  string representing the Storage Server password
         """
-        assert isinstance(user_name, str)
-        assert isinstance(password, str)
+        assert isinstance(user_name, string_types)
+        assert isinstance(password, string_types)
 
         response = requests.post(self.__config['proxy-services']['storage-authentication'],
                                  json={'user': user_name, 'password': password})
         if response.status_code != 200:
             raise Exception('Storage Server authentication failed, Status Code: %d'
                             % response.status_code)
-        else:
-            return response.content
+        return response.content
 
     def __get_experiment_list(self, cloned=False):
         """
@@ -422,10 +440,9 @@ class VirtualCoach(object):
             # return a simple list containing only experiment names since this is the only
             # information in the dictionary anyway
             return {experiment['name']: experiment for experiment in json.loads(response.content)}
-        else:
-            _, response = self.__http_client.get(
-                self.__config['proxy-services']['experiment-list'])
-            return json.loads(response)
+        _, response = self.__http_client.get(
+            self.__config['proxy-services']['experiment-list'])
+        return json.loads(response)
 
     def __get_available_server_list(self):
         """
@@ -435,7 +452,7 @@ class VirtualCoach(object):
         status_code, response = self.__http_client.get(
             self.__config['proxy-services']['available-servers'])
 
-        if str(status_code) != str(httplib.OK):
+        if str(status_code) != str(http.client.OK):
             raise Exception('Error when getting server list, Status Code: %d. Error: %s'
                             % (status_code, response))
         return json.loads(response)
@@ -449,7 +466,7 @@ class VirtualCoach(object):
         response = requests.get(self.__config['proxy-services']['csv-files'] % (experiment_id,),
                                 headers=self.__http_headers)
 
-        if response.status_code != httplib.OK:
+        if response.status_code != http.client.OK:
             raise Exception('Error when getting CSV files Status Code: %d. Error: %s'
                             % (response.status_code, response))
         csv_files = json.loads(response.content)
@@ -471,11 +488,11 @@ class VirtualCoach(object):
         table.set_cols_align(['r', 'c', 'r'])
 
         for i, run_date in enumerate(sorted(csv_files.keys())):
-            run_size = sum(file['size'] for file in csv_files[run_date].values())
+            run_size = sum(file['size'] for file in list(csv_files[run_date].values()))
             table.add_row([i, run_date, run_size])
 
         logger.info('List of simulation runs')
-        print table.draw()
+        print(table.draw())
 
     def print_run_csv_files(self, exp_id, run_id):
         """
@@ -495,11 +512,11 @@ class VirtualCoach(object):
             raise Exception('Could not find run %i, %i runs were found' %
                             (run_id, len(sorted_runs)))
 
-        for csv_file in csv_files[sorted_runs[run_id]].values():
+        for csv_file in list(csv_files[sorted_runs[run_id]].values()):
             table.add_row([csv_file['name'], csv_file['size']])
 
         logger.info('Run %i list of files.', run_id)
-        print table.draw()
+        print(table.draw())
 
     def print_last_run_csv_files(self, exp_id):
         """
@@ -517,11 +534,11 @@ class VirtualCoach(object):
         if not sorted_runs:
             raise Exception('Could not find any run')
 
-        for csv_file in csv_files[sorted_runs[-1]].values():
+        for csv_file in list(csv_files[sorted_runs[-1]].values()):
             table.add_row([csv_file['name'], csv_file['size']])
 
         logger.info('Last run list of files')
-        print table.draw()
+        print(table.draw())
 
     def __get_csv_file_content(self, exp_id, file_uuid):
         """
@@ -533,10 +550,10 @@ class VirtualCoach(object):
         logger.info('Retrieving CSV file.')
 
         response = requests.get(self.__config['proxy-services']['experiment-file'] %
-                                    (exp_id, file_uuid),
+                                (exp_id, file_uuid),
                                 headers=self.__http_headers)
 
-        if response.status_code != httplib.OK:
+        if response.status_code != http.client.OK:
             raise Exception('Error when getting CSV file Status Code: %d. Error: %s'
                             % (response.status_code, response))
 
@@ -557,7 +574,7 @@ class VirtualCoach(object):
                             (run_id, len(sorted_runs)))
 
         if file_name not in csv_files[sorted_runs[run_id]]:
-            file_names = ', '.join(f['name'] for f in csv_files[sorted_runs[run_id]].values())
+            file_names = ', '.join(f['name'] for f in list(csv_files[sorted_runs[run_id]].values()))
             raise Exception('Could not find file \'%s\' in run %i, available file names are: %s' %
                             (file_name, run_id, file_names))
 
@@ -578,7 +595,8 @@ class VirtualCoach(object):
             raise Exception('Could not find any run')
 
         if file_name not in csv_files[sorted_runs[-1]]:
-            file_names = ', '.join(file['name'] for file in csv_files[sorted_runs[-1]].values())
+            file_names = ', '.join(
+                file['name'] for file in list(csv_files[sorted_runs[-1]].values()))
             raise Exception('Could not find file \'%s\' in last run, available file names are: %s' %
                             (file_name, file_names))
 
@@ -601,7 +619,7 @@ class VirtualCoach(object):
         file_headers['Content-Type'] = 'text/plain'
         response = requests.post(url, data=file_content, headers=file_headers)
 
-        if response.status_code != httplib.OK:
+        if response.status_code != http.client.OK:
             raise Exception('Error when setting file: %d. Error: %s'
                             % (response.status_code, response))
 
@@ -640,7 +658,7 @@ class VirtualCoach(object):
             logger.error('The folder %s could not be zipped', dirpath)
             raise e
         zip_file.close()
-        content = open(temp, 'r').read()
+        content = open(temp, 'rb').read()
         os.remove(temp)
         return content
 
@@ -651,7 +669,7 @@ class VirtualCoach(object):
         :param path: path to the experiment folder or to the zip file to be imported
         :type path: str
         """
-        if not isinstance(path, str):
+        if not isinstance(path, string_types):
             raise TypeError('The provided argument is not a string.')
         if not os.path.isfile(path) and not os.path.isdir(path):
             raise ValueError('The file or folder named %(path)s does not exist.' % {'path': path})
diff --git a/hbp_nrp_virtual_coach/requirements.txt b/hbp_nrp_virtual_coach/requirements.txt
index 4da393e62a30f471f3da583d58e6243a31c50da9..2f5d8d543b4584c50951821e012f25224336fa38 100644
--- a/hbp_nrp_virtual_coach/requirements.txt
+++ b/hbp_nrp_virtual_coach/requirements.txt
@@ -2,14 +2,12 @@
 bbp-client==0.4.4  # EPFL_SPECIFIC
 
 # third-party dependencies
+future==0.18.2
 texttable==0.8.7
 progressbar2==3.34.0
 httplib2==0.12.1
 pandas==0.22.0
-ipywidgets==7.5.1
+ipywidgets<=7.5.1; python_version<"3.0"  # v8.0.0 incompatible with python 2
 jupyter==1.0.0
 requests
 ipykernel==4.6.0
-
-# unit test dependencies
-mock==1.0.1
diff --git a/hbp_nrp_virtual_coach/requirements_extension_tests.txt b/hbp_nrp_virtual_coach/requirements_extension_tests.txt
new file mode 100644
index 0000000000000000000000000000000000000000..a8a6bd0ac91e29d306bcbd31604dfc7c1fdb4aaa
--- /dev/null
+++ b/hbp_nrp_virtual_coach/requirements_extension_tests.txt
@@ -0,0 +1,2 @@
+#the following is required for the unit testing
+mock==1.0.1
\ No newline at end of file
diff --git a/hbp_nrp_virtual_coach/setup.py b/hbp_nrp_virtual_coach/setup.py
index 1bab64006a93d94deda77718f2ef5e8dc5ce28bb..0aaba7119a263dc7e5bfeb99981000c1606e9752 100644
--- a/hbp_nrp_virtual_coach/setup.py
+++ b/hbp_nrp_virtual_coach/setup.py
@@ -1,31 +1,30 @@
 '''setup.py'''
 
-# pylint: disable=F0401,E0611,W0142
+# pylint: disable=F0401,E0611,W0622,E0012,W0142,W0402
 
+from builtins import str
 try:
     from setuptools import setup
 except ImportError:
     from distutils.core import setup
 
-import hbp_nrp_virtual_coach
+from optparse import Option  # pylint:disable=deprecated-module
 import pip
+import hbp_nrp_virtual_coach
 
-from optparse import Option
 options = Option('--workaround')
 options.skip_requirements_regex = None
 reqs_file = './requirements.txt'
+
+pip_version_major = int(pip.__version__.split('.')[0])
 # Hack for old pip versions
-if pip.__version__.startswith('10.'):
-    # Versions greater or equal to 10.x don't rely on pip.req.parse_requirements
-    install_reqs = list(val.strip() for val in open(reqs_file))
-    reqs = install_reqs
-elif pip.__version__.startswith('1.'):
+if pip_version_major == 1:
     # Versions 1.x rely on pip.req.parse_requirements
     # but don't require a "session" parameter
     from pip.req import parse_requirements # pylint:disable=no-name-in-module, import-error
     install_reqs = parse_requirements(reqs_file, options=options)
     reqs = [str(ir.req) for ir in install_reqs]
-else:
+elif 10 > pip_version_major > 1:
     # Versions greater than 1.x but smaller than 10.x rely on pip.req.parse_requirements
     # and requires a "session" parameter
     from pip.req import parse_requirements # pylint:disable=no-name-in-module, import-error
@@ -37,7 +36,10 @@ else:
         options=options
     )
     reqs = [str(ir.req) for ir in install_reqs]
-
+elif pip_version_major >= 10:
+    # Versions greater or equal to 10.x don't rely on pip.req.parse_requirements
+    install_reqs = list(val.strip() for val in open(reqs_file))
+    reqs = install_reqs
 config = {
     'description': 'Virtual Coach command-line Neurorobotics Platform interface for HBP SP10',
     'author': 'HBP Neurorobotics',
@@ -49,6 +51,8 @@ config = {
     'package_data': {
         'hbp_nrp_virtual_coach': ['config.json']
     },
+    'classifiers': ['Programming Language :: Python :: 3',
+                    "Programming Language :: Python :: 2.7"],
     'scripts': [],
     'name': 'hbp-nrp-virtual-coach',
     'include_package_data': True,