diff --git a/Gaudi/python/Gaudi/CommonGaudiConfigurables.py b/Gaudi/python/Gaudi/CommonGaudiConfigurables.py
index 7ef419319f1cd7efb99602b57fcdc5a371254d08..2825caa98e372c5d82ede9471b67dff26b428bea 100644
--- a/Gaudi/python/Gaudi/CommonGaudiConfigurables.py
+++ b/Gaudi/python/Gaudi/CommonGaudiConfigurables.py
@@ -62,4 +62,10 @@ for new in aliases:
 _gbl.update(aliases)
 __all__.extend(aliases)
 # remove temporaries
-del _gbl, new
+del _gbl
+# The `new` var is only scoped in its `for` loop in Python 3, so we only need
+# to 'worry' about cleanup in Python 2
+try:
+    del new
+except NameError:
+    pass
diff --git a/Gaudi/python/Gaudi/Configuration.py b/Gaudi/python/Gaudi/Configuration.py
index df8d51c456306056375e09b0814abbc64a3bae53..f3c53b4c860701cef0e15ec909cd4db0ea905cd8 100644
--- a/Gaudi/python/Gaudi/Configuration.py
+++ b/Gaudi/python/Gaudi/Configuration.py
@@ -1,11 +1,12 @@
 # File: Gaudi/python/Gaudi/Configuration.py
 # Author: Pere Mato (pere.mato@cern.ch)
+from __future__ import absolute_import
 
 from GaudiKernel.Constants import *
 from GaudiKernel.Configurable import *
 from GaudiKernel.ConfigurableDb import loadConfigurableDb, cfgDb
 from GaudiKernel.ConfigurableDb import getConfigurable as confDbGetConfigurable
-from CommonGaudiConfigurables import *
+from Gaudi.CommonGaudiConfigurables import *
 from GaudiKernel.ProcessJobOptions import importOptions, importUnits
 from GaudiKernel.ProcessJobOptions import InstallRootLoggingHandler as _InstallRootLoggingHandler
 
@@ -82,10 +83,11 @@ def configurationDict(all=False):
             conf_dict[n][p] = v
     # purge empty configurables
     keys = conf_dict.keys()
+    ret_dict = {}
     for n in keys:
-        if not conf_dict[n]:
-            del conf_dict[n]
-    return conf_dict
+        if conf_dict[n]:
+            ret_dict[n] = conf_dict[n]
+    return ret_dict
 
 
 def getConfigurable(name, defaultType=None):
diff --git a/Gaudi/python/Gaudi/Main.py b/Gaudi/python/Gaudi/Main.py
index 85b6884636598f08703f1ca865f87c5be6c4f472..3095c28b90a489946b53b6026ee4127f93b01f13 100644
--- a/Gaudi/python/Gaudi/Main.py
+++ b/Gaudi/python/Gaudi/Main.py
@@ -1,9 +1,12 @@
+from __future__ import print_function
 import sys
 import os
 from time import time
 from Gaudi import Configuration
 import logging
 
+import six
+
 log = logging.getLogger(__name__)
 
 
@@ -30,7 +33,7 @@ class BootstrapHelper(object):
             self.value = value
 
         def __str__(self):
-            return str(self.value)
+            return bytes(self.value).decode('utf-8')
         toString = __str__
 
     class AppMgr(object):
@@ -61,13 +64,13 @@ class BootstrapHelper(object):
             return BootstrapHelper.StatusCode(self.lib.py_bootstrap_fsm_terminate(self.ptr))
 
         def getService(self, name):
-            return self.lib.py_bootstrap_getService(self.ptr, name)
+            return self.lib.py_bootstrap_getService(self.ptr, name.encode('utf-8'))
 
         def setProperty(self, name, value):
-            return BootstrapHelper.StatusCode(self.lib.py_bootstrap_setProperty(self.ptr, name, value))
+            return BootstrapHelper.StatusCode(self.lib.py_bootstrap_setProperty(self.ptr, name.encode('utf-8'), value.encode('utf-8')))
 
         def getProperty(self, name):
-            return BootstrapHelper.Property(self.lib.py_bootstrap_getProperty(self.ptr, name))
+            return BootstrapHelper.Property(self.lib.py_bootstrap_getProperty(self.ptr, name.encode('utf-8')))
 
         def printAlgsSequences(self):
             return self.lib.py_helper_printAlgsSequences(self.ptr)
@@ -142,19 +145,19 @@ def toOpt(value):
     '''
     Helper to convert values to old .opts format.
 
-    >>> print toOpt('some "text"')
+    >>> print(toOpt('some "text"'))
     "some \\"text\\""
-    >>> print toOpt('first\\nsecond')
+    >>> print(toOpt('first\\nsecond'))
     "first
     second"
-    >>> print toOpt({'a': [1, 2, '3']})
+    >>> print(toOpt({'a': [1, 2, '3']}))
     {"a": [1, 2, "3"]}
     '''
-    if isinstance(value, basestring):
+    if isinstance(value, six.string_types):
         return '"{0}"'.format(value.replace('"', '\\"'))
     elif isinstance(value, dict):
         return '{{{0}}}'.format(', '.join('{0}: {1}'.format(toOpt(k), toOpt(v))
-                                          for k, v in value.iteritems()))
+                                          for k, v in value.items()))
     elif hasattr(value, '__iter__'):
         return '[{0}]'.format(', '.join(map(toOpt, value)))
     else:
@@ -219,10 +222,10 @@ class gaudimain(object):
         from pprint import pformat
         conf_dict = Configuration.configurationDict(all)
         out = []
-        names = conf_dict.keys()
+        names = list(conf_dict.keys())
         names.sort()
         for n in names:
-            props = conf_dict[n].keys()
+            props = list(conf_dict[n].keys())
             props.sort()
             for p in props:
                 out.append('%s.%s = %s;' % (n, p, toOpt(conf_dict[n][p])))
@@ -247,9 +250,9 @@ class gaudimain(object):
         log.info(msg)
         conf_dict = Configuration.configurationDict(all)
         if old_format:
-            print self.generateOptsOutput(all)
+            print(self.generateOptsOutput(all), flush=True)
         else:
-            print self.generatePyOutput(all)
+            print(self.generatePyOutput(all), flush=True)
 
     def writeconfig(self, filename, all=False):
         write = {".pkl": lambda filename, all: self._writepickle(filename),
@@ -314,10 +317,13 @@ class gaudimain(object):
 
         # set ApplicationMgr properties
         comp = 'ApplicationMgr'
-        props = Configurable.allConfigurables.get(comp, {})
-        if props:
-            props = expandvars(props.getValuedProperties())
-        for p, v in props.items() + [('JobOptionsType', 'NONE')]:
+        try:
+            appMgr = Configurable.allConfigurables[comp]
+            props = expandvars(appMgr.getValuedProperties())
+        except KeyError:
+            props = {}
+        prop_items = list(props.items()) + [('JobOptionsType', 'NONE')]
+        for p, v in prop_items:
             if not self.g.setProperty(p, str(v)):
                 self.log.error('Cannot set property %s.%s to %s', comp, p, v)
                 sys.exit(10)
@@ -334,11 +340,15 @@ class gaudimain(object):
         if not msp:
             self.log.error('Cannot get service %s', comp)
             sys.exit(10)
-        props = Configurable.allConfigurables.get(comp, {})
-        if props:
-            props = expandvars(props.getValuedProperties())
+        try:
+            msgSvc = Configurable.allConfigurables[comp]
+            props = expandvars(msgSvc.getValuedProperties())
+        except KeyError:
+            props = {}
         for p, v in props.items():
-            if not _bootstrap.setProperty(msp, p, str(v)):
+            if not _bootstrap.setProperty(msp,
+                                          p.encode('utf-8'),
+                                          str(v).encode('utf-8')):
                 self.log.error('Cannot set property %s.%s to %s', comp, p, v)
                 sys.exit(10)
 
@@ -361,9 +371,12 @@ class gaudimain(object):
                     v = v.__resolve__()
                 if type(v) == str:
                     v = '"%s"' % v  # need double quotes
-                elif type(v) == long:
+                elif type(v) == six.integer_types:
                     v = '%d' % v  # prevent pending 'L'
-                _bootstrap.addPropertyToCatalogue(jos, n, p, str(v))
+                _bootstrap.addPropertyToCatalogue(jos,
+                                                  n.encode('utf-8'),
+                                                  p.encode('utf-8'),
+                                                  str(v).encode('utf-8'))
         if hasattr(Configurable, "_configurationLocked"):
             Configurable._configurationLocked = True
         self.log.debug('basicInit: done')
@@ -430,8 +443,8 @@ class gaudimain(object):
             # It may not be 100% correct, but usually it means a segfault in C++
             self.ip.setProperty('ReturnCode', str(128 + 11))
             statuscode = False
-        except Exception, x:
-            print 'Exception:', x
+        except Exception as x:
+            print('Exception:', x)
             # for other exceptions, just set a generic error code
             self.ip.setProperty('ReturnCode', '1')
             statuscode = False
diff --git a/Gaudi/scripts/gaudirun.py b/Gaudi/scripts/gaudirun.py
index 3661413484b2e2d4c652068aa7651ad96eeb3175..7937f1955a593c86f1196373f552181753515084 100755
--- a/Gaudi/scripts/gaudirun.py
+++ b/Gaudi/scripts/gaudirun.py
@@ -1,5 +1,5 @@
 #!/usr/bin/env python
-
+from __future__ import print_function
 import os
 import sys
 from tempfile import mkstemp
@@ -95,7 +95,7 @@ def getArgsFromQmt(qmtfile):
             tmp_opts = NamedTemporaryFile(suffix='.py')
         else:
             tmp_opts = NamedTemporaryFile(suffix='.opts')
-        tmp_opts.write(options.text)
+        tmp_opts.write(options.text.encode('utf-8'))
         tmp_opts.flush()
         args.append(tmp_opts.name)
         _qmt_tmp_opt_files.append(tmp_opts)
@@ -123,7 +123,7 @@ def getArgsFromQmt(qmtfile):
 if __name__ == "__main__":
     # ensure that we (and the subprocesses) use the C standard localization
     if os.environ.get('LC_ALL') != 'C':
-        print '# setting LC_ALL to "C"'
+        print('# setting LC_ALL to "C"', flush=True)
         os.environ['LC_ALL'] = 'C'
 
     from optparse import OptionParser
@@ -239,7 +239,7 @@ if __name__ == "__main__":
         else:
             argv.append(a)
     if argv != sys.argv[1:]:
-        print '# Running', sys.argv[0], 'with arguments', argv
+        print('# Running', sys.argv[0], 'with arguments', argv)
 
     opts, args = parser.parse_args(args=argv)
 
@@ -396,9 +396,9 @@ if __name__ == "__main__":
             # now we have all the ingredients to prepare our command
             arglist = [profilerPath] + profilerOptions.split() + args
             arglist = [a for a in arglist if a != '']
-            # print profilerPath
+            # print(profilerPath)
             # for arg in arglist:
-            # print arg
+            # print(arg)
             os.execv(profilerPath, arglist)
         else:
             arglist = [a for a in sys.argv if not a.startswith("--profiler")]
@@ -451,10 +451,10 @@ if __name__ == "__main__":
     if options:
         g = {}
         l = {}
-        exec "from Gaudi.Configuration import *" in g, l
+        exec("from Gaudi.Configuration import *", g, l)
         for o in options:
             logging.debug(o)
-            exec o in g, l
+            exec(o, g, l)
 
     import GaudiKernel.Proxy.Configurable
     if opts.no_conf_user_apply:
@@ -473,10 +473,10 @@ if __name__ == "__main__":
     if opts.post_options:
         g = {}
         l = {}
-        exec "from Gaudi.Configuration import *" in g, l
+        exec("from Gaudi.Configuration import *", g, l)
         for o in opts.post_options:
             logging.debug(o)
-            exec o in g, l
+            exec(o, g, l)
 
     if 'GAUDI_TEMP_OPTS_FILE' in os.environ:
         os.remove(os.environ['GAUDI_TEMP_OPTS_FILE'])
diff --git a/Gaudi/tests/python/Test_confDb.py b/Gaudi/tests/python/Test_confDb.py
index bdb8508058c38c1c2b00274eba1f41859eb53f62..a8bfb4a6c6ef89a7bde847b8d6effbabf2f2a913 100644
--- a/Gaudi/tests/python/Test_confDb.py
+++ b/Gaudi/tests/python/Test_confDb.py
@@ -1,6 +1,7 @@
 """
 Hand-written confDb file for tests.
 """
+from __future__ import print_function
 
 
 def _fillCfgDb():
@@ -29,7 +30,7 @@ try:
     _fillCfgDb()
     # house cleaning...
     del _fillCfgDb
-except Exception, err:
-    print "Py:ConfigurableDb   ERROR Problem with [%s] content!" % __name__
-    print "Py:ConfigurableDb   ERROR", err
-    print "Py:ConfigurableDb   ERROR   ==> culprit is package [Test] !"
+except Exception as err:
+    print("Py:ConfigurableDb   ERROR Problem with [%s] content!" % __name__)
+    print("Py:ConfigurableDb   ERROR", err)
+    print("Py:ConfigurableDb   ERROR   ==> culprit is package [Test] !")
diff --git a/Gaudi/tests/qmtest/gaudi.qms/bug_61144.qmt b/Gaudi/tests/qmtest/gaudi.qms/bug_61144.qmt
index 1e2db6e0ac74c77d2c51a01569c5e26a761e8a72..a4cf7797ed5cc08e2f2739d4b85e06e8dd1bae11 100644
--- a/Gaudi/tests/qmtest/gaudi.qms/bug_61144.qmt
+++ b/Gaudi/tests/qmtest/gaudi.qms/bug_61144.qmt
@@ -13,7 +13,7 @@ import Test_confDb # only the _merge_confDb.py in InstallArea/python/* are used
 #------------------------------------------------------------------------------\
 # https://savannah.cern.ch/bugs/?61144
 from Configurables import MyTestTool
-print MyTestTool()
+print(MyTestTool())
 #------------------------------------------------------------------------------/
 
 </text></argument>
diff --git a/Gaudi/tests/qmtest/gaudi.qms/custom_main_loop.qmt b/Gaudi/tests/qmtest/gaudi.qms/custom_main_loop.qmt
index e9b596e8fa772a7f6e489b4b788ac448052d8f3b..1279162aaeb9ad06f7e7ffadff7578cbe66c5465 100644
--- a/Gaudi/tests/qmtest/gaudi.qms/custom_main_loop.qmt
+++ b/Gaudi/tests/qmtest/gaudi.qms/custom_main_loop.qmt
@@ -5,11 +5,12 @@
   <text>-v</text>
 </set></argument>
 <argument name="options"><text>
+from __future__ import print_function
 # Options of the test job
 from Gaudi.Configuration import *
 
 def myMainLoop(app, nevt):
-    print "=== Custom Main Loop ==="
+    print("=== Custom Main Loop ===")
     from GaudiPython import gbl
     return gbl.StatusCode(gbl.StatusCode.SUCCESS)
 
diff --git a/Gaudi/tests/qmtest/gaudi.qms/custom_main_loop_parallel.qmt b/Gaudi/tests/qmtest/gaudi.qms/custom_main_loop_parallel.qmt
index 7307251aabb1b94b0472c407cef02a9e6885e471..4e4590fd787d8fd16e2223c2e5b83566f06e3158 100644
--- a/Gaudi/tests/qmtest/gaudi.qms/custom_main_loop_parallel.qmt
+++ b/Gaudi/tests/qmtest/gaudi.qms/custom_main_loop_parallel.qmt
@@ -7,11 +7,12 @@
   <text>1</text>
 </set></argument>
 <argument name="options"><text>
+from __future__ import print_function
 # Options of the test job
 from Gaudi.Configuration import *
 
 def myMainLoop(app, nevt):
-    print "=== Custom Main Loop ==="
+    print("=== Custom Main Loop ===")
     from GaudiPython import gbl
     return True
 
diff --git a/Gaudi/tests/scripts/test_configurable_users.py b/Gaudi/tests/scripts/test_configurable_users.py
index ac485718e8f14d6a63bbea6ccbfd4eb50511496b..d7a5824cd2c5b6be25d54cb24c06fb532c11929c 100755
--- a/Gaudi/tests/scripts/test_configurable_users.py
+++ b/Gaudi/tests/scripts/test_configurable_users.py
@@ -1,3 +1,4 @@
+from __future__ import print_function
 from Gaudi.Configuration import *
 
 appliedConf = []
@@ -7,8 +8,8 @@ class ConfigurableUserTest(ConfigurableUser):
     __slots__ = {}
 
     def __apply_configuration__(self):
-        print "Applying", self.getName()
-        print self
+        print("Applying", self.getName())
+        print(self)
         appliedConf.append(self.getName())
 
 
@@ -67,12 +68,12 @@ class Action(object):
 
     def __call__(self):
         calledActions.append(self.msg)
-        print self.msg
+        print(self.msg)
 
 
 def ActionFunction():
     calledActions.append("Action Function")
-    print "Action Function"
+    print("Action Function")
 
 
 appendPostConfigAction(Action("Action Object One"))
@@ -121,4 +122,4 @@ for name, (prop, value) in expected.items():
     assert allConfs[name].getProp(
         prop) == value, "%s.%s != %s" % (name, prop, value)
 
-print "Success."
+print("Success.")
diff --git a/Gaudi/tests/scripts/test_cu_delayed_creation.py b/Gaudi/tests/scripts/test_cu_delayed_creation.py
index c5ca5d9f217b95884865f21f5a394239ce4504a4..43224873889c1cfe3acec7cbae26f859a9b201ad 100755
--- a/Gaudi/tests/scripts/test_cu_delayed_creation.py
+++ b/Gaudi/tests/scripts/test_cu_delayed_creation.py
@@ -1,3 +1,4 @@
+from __future__ import print_function
 from Gaudi.Configuration import *
 
 
@@ -6,8 +7,8 @@ class DelayedInstance(ConfigurableUser):
                  "Applied": False}
 
     def __apply_configuration__(self):
-        print "Applying", self.getName()
-        print self
+        print("Applying", self.getName())
+        print(self)
         self.Applied = True
 
 
@@ -17,8 +18,8 @@ class Application(ConfigurableUser):
     __used_configurables__ = []
 
     def __apply_configuration__(self):
-        print "Applying", self.getName()
-        print self
+        print("Applying", self.getName())
+        print(self)
         self.Applied = True
         # This is instantiated late
         DelayedInstance()
@@ -34,4 +35,4 @@ applyConfigurableUsers()
 assert Application().Applied
 assert DelayedInstance().Applied
 
-print "Done."
+print("Done.")
diff --git a/Gaudi/tests/scripts/test_cu_passive_dep.py b/Gaudi/tests/scripts/test_cu_passive_dep.py
index 651936a10761d698e5c776e467f71863e4b07962..facfaada205a55bcba535367f1a95c6caf4d9b14 100755
--- a/Gaudi/tests/scripts/test_cu_passive_dep.py
+++ b/Gaudi/tests/scripts/test_cu_passive_dep.py
@@ -1,3 +1,4 @@
+from __future__ import print_function
 from GaudiKernel.ProcessJobOptions import InstallRootLoggingHandler
 import logging
 InstallRootLoggingHandler("# ", level=logging.INFO)
@@ -32,7 +33,7 @@ CommonConf()
 # apply all ConfigurableUser instances
 from GaudiKernel.Configurable import applyConfigurableUsers
 applyConfigurableUsers()
-print "Done."
+print("Done.")
 
 expected_order = ["CommonConf", "Application"]
 if applied_order != expected_order:
diff --git a/Gaudi/tests/scripts/test_export_oldopts.py b/Gaudi/tests/scripts/test_export_oldopts.py
index 8e3a1bf2053fe76de8acb6f774bcd951c46a138b..577569f7d687bbb88f9740b62586a985accaefc4 100755
--- a/Gaudi/tests/scripts/test_export_oldopts.py
+++ b/Gaudi/tests/scripts/test_export_oldopts.py
@@ -1,5 +1,5 @@
 #!/usr/bin/env python
-
+from __future__ import print_function
 import os
 import sys
 import tempfile
@@ -26,59 +26,60 @@ try:
         optfiles = sys.argv[1:]
     else:
         optfiles = ["main.py"]
-    outname = 'out-{0}'.format(sha1(str(optfiles)).hexdigest()[:8])
+    hash = sha1(str(optfiles).encode('utf-8')).hexdigest()
+    outname = 'out-{0}'.format(hash[:8])
 
     # parse the option file and cache the configuration (python only)
     cmd = ["python", which("gaudirun.py"),
            "-n", "-v", "--output", outname + ".1.py"] + optfiles
     proc = Popen(cmd, stdout=PIPE)
-    print "=========================================="
-    print "======== First pass (python only) ========"
-    print "=========================================="
-    print "= cmd:", " ".join(cmd)
+    print("==========================================")
+    print("======== First pass (python only) ========")
+    print("==========================================")
+    print("= cmd:", " ".join(cmd))
     out, err = proc.communicate()
-    print out
+    print(out)
     if err:
-        print "=== stderr: ==="
-        print err
+        print("=== stderr: ===")
+        print(err)
     expected = eval(open(outname + ".1.py").read())
 
     # parse the option file, export old options, parse again
     cmd = ["python", which("gaudirun.py"),
            "-n", "-v", "--old-opts", "--output", outname + '.opts'] + optfiles
     proc = Popen(cmd, stdout=PIPE, stderr=PIPE)
-    print ""
-    print "=========================================="
-    print "========== Second pass (export) =========="
-    print "=========================================="
-    print "= cmd:", " ".join(cmd)
+    print("")
+    print("==========================================")
+    print("========== Second pass (export) ==========")
+    print("==========================================")
+    print("= cmd:", " ".join(cmd))
     out, err = proc.communicate()
-    print out
+    print(out)
     if err:
-        print "=== stderr: ==="
-        print err
+        print("=== stderr: ===")
+        print(err)
 
     cmd = ["python", which("gaudirun.py"),
            "-n", "-v", "--output", outname + ".2.py", outname + '.opts']
     proc = Popen(cmd, stdout=PIPE, stderr=PIPE)
-    print ""
-    print "=========================================="
-    print "========== Second pass (import) =========="
-    print "=========================================="
-    print "= cmd:", " ".join(cmd)
+    print("")
+    print("==========================================")
+    print("========== Second pass (import) ==========")
+    print("==========================================")
+    print("= cmd:", " ".join(cmd))
     out, err = proc.communicate()
-    print out
+    print(out)
     if err:
-        print "=== stderr: ==="
-        print err
+        print("=== stderr: ===")
+        print(err)
     result = eval(open(outname + ".2.py").read())
 
     if result != expected:
-        print "Configuration from old options differs from the python one"
+        print("Configuration from old options differs from the python one")
         retcode = 1
 
-except RuntimeError, x:
-    print x
+except RuntimeError as x:
+    print(x)
     retcode = 1
 
 sys.exit(retcode)
diff --git a/Gaudi/tests/scripts/test_propagate_properties.py b/Gaudi/tests/scripts/test_propagate_properties.py
index 7238fb7c0de42b433a8bbcc9a1f197e9294e3d94..b7b6e96d1ef29fa5e19f7d3ec9887091e5fc7122 100755
--- a/Gaudi/tests/scripts/test_propagate_properties.py
+++ b/Gaudi/tests/scripts/test_propagate_properties.py
@@ -1,3 +1,4 @@
+from __future__ import print_function
 from Gaudi.Configuration import *
 
 # use cases:
@@ -102,12 +103,12 @@ def check(conf, prop, exp):
     v = conf.getProp(prop)
     good = v == exp
     if not good:
-        print "ERROR:",
-    print "%s.%s is %r (expected %r)," % (conf.name(), prop, v, exp),
+        print("ERROR:",)
+    print("%s.%s is %r (expected %r)," % (conf.name(), prop, v, exp),)
     if hasattr(conf, prop):
-        print "set"
+        print("set")
     else:
-        print "unset"
+        print("unset")
     return good
 
 
diff --git a/Gaudi/tests/scripts/test_purge.py b/Gaudi/tests/scripts/test_purge.py
index b30ae4cb36a3ef609fdd811bf81c5f895873a859..80f93a7c82eac00a68baea6a6d72fe3072f6ce22 100755
--- a/Gaudi/tests/scripts/test_purge.py
+++ b/Gaudi/tests/scripts/test_purge.py
@@ -1,3 +1,4 @@
+from __future__ import print_function
 from Gaudi.Configuration import *
 from GaudiKernel.Configurable import purge
 
@@ -15,11 +16,11 @@ second = configurationDict()
 from pprint import PrettyPrinter
 pp = PrettyPrinter()
 
-print "first =",
+print("first =", end=' ')
 pp.pprint(first)
 
-print "purged =",
+print("purged =", end=' ')
 pp.pprint(purged)
 
-print "second =",
+print("second =", end=' ')
 pp.pprint(second)
diff --git a/GaudiCoreSvc/CMakeLists.txt b/GaudiCoreSvc/CMakeLists.txt
index 3b02be3d8eeb093e7f43f3d242f1e1621caad5ea..5d97c031673102d88ff5679c71b4e79eaa815485 100644
--- a/GaudiCoreSvc/CMakeLists.txt
+++ b/GaudiCoreSvc/CMakeLists.txt
@@ -2,7 +2,7 @@ gaudi_subdir(GaudiCoreSvc)
 
 gaudi_depends_on_subdirs(GaudiKernel)
 
-find_package(Boost COMPONENTS system filesystem regex thread python REQUIRED)
+find_package(Boost COMPONENTS system filesystem regex thread ${BOOST_PYTHON_LIB_NAME} REQUIRED)
 find_package(TBB REQUIRED)
 find_package(PythonLibs REQUIRED)
 find_package(ROOT REQUIRED)
diff --git a/GaudiCoreSvc/src/JobOptionsSvc/PythonConfig.cpp b/GaudiCoreSvc/src/JobOptionsSvc/PythonConfig.cpp
index a396ca74c284752970ff7ebd2940ea393402a31c..d46c60956518c4b96b6e43d54f7fc7cc2ba406fd 100644
--- a/GaudiCoreSvc/src/JobOptionsSvc/PythonConfig.cpp
+++ b/GaudiCoreSvc/src/JobOptionsSvc/PythonConfig.cpp
@@ -25,7 +25,10 @@ StatusCode PythonConfig::evaluateConfig( const std::string& filename, const std:
     // some python helper
     std::string command( preAction );
     command += "\nfor name in '";
-    command += filename + "'.split(','): execfile(name)\n";
+    command += filename + "'.split(','):\n";
+    command += "    with open(name) as f:\n";
+    command += "        code = compile(f.read(), name, 'exec')\n";
+    command += "        exec(code)\n";
     command += "from GaudiKernel.Configurable import expandvars\nfrom GaudiKernel.Proxy.Configurable import "
                "applyConfigurableUsers\napplyConfigurableUsers()\n";
     command += postAction;
diff --git a/GaudiExamples/CMakeLists.txt b/GaudiExamples/CMakeLists.txt
index 13e62f55d386ccff5b3f3aef3df0ddb5fb75983d..9f7f0c4319ba099f24d9202458c85bb0b0c4da2c 100644
--- a/GaudiExamples/CMakeLists.txt
+++ b/GaudiExamples/CMakeLists.txt
@@ -5,11 +5,11 @@ gaudi_depends_on_subdirs(GaudiKernel GaudiUtils GaudiGSL GaudiAlg RootCnv)
 find_package(AIDA)
 find_package(HepPDT)
 find_package(ROOT COMPONENTS Tree RIO Hist Net REQUIRED)
-find_package(Boost COMPONENTS python REQUIRED)
+find_package(Boost COMPONENTS ${BOOST_PYTHON_LIB_NAME} REQUIRED)
 find_package(CLHEP)
 find_package(GSL)
 find_package(PythonLibs REQUIRED)
-find_package(RELAX REQUIRED)
+find_package(RELAX)
 find_package(TBB REQUIRED)
 # ROOT runtime
 find_package(PNG REQUIRED)
@@ -143,9 +143,9 @@ if(NOT StatusCodeHeaderData_previous STREQUAL StatusCodeHeaderData)
   file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/HackedStatusCode.h" "${StatusCodeHeaderData}")
 endif()
 
-if(Boost_PYTHON_FOUND)
+if(BOOST_PYTHON_LIB_FOUND)
   gaudi_add_python_module(PyExample src/PythonModule/*.cpp
-                          LINK_LIBRARIES ${Boost_PYTHON_LIBRARY}
+                          LINK_LIBRARIES Boost
                           INCLUDE_DIRS Boost PythonLibs)
 endif()
 
diff --git a/GaudiExamples/options/ConfigurableUser.py b/GaudiExamples/options/ConfigurableUser.py
index b585bfc8f76c984f388c3c7f0c0975f390d31fa4..f72e4e8dd8e030758ed681c074fcc6f51681fb66 100644
--- a/GaudiExamples/options/ConfigurableUser.py
+++ b/GaudiExamples/options/ConfigurableUser.py
@@ -62,8 +62,8 @@ def PostConfAction():
     """
     Action printing the result of the configuration of the ApplicationMgr.
     """
-    print "==== Configuration completed ===="
-    print ApplicationMgr()
+    print("==== Configuration completed ====")
+    print(ApplicationMgr())
 
 
 appendPostConfigAction(PostConfAction)
diff --git a/GaudiExamples/options/ControlFlow/AlgSequencer.py b/GaudiExamples/options/ControlFlow/AlgSequencer.py
index 8a5a94e48ec4c3166eb2a83f9a39efa3bb59e1d9..27da202d1f10c983cca2c64248dfbe6aa99c8dfe 100644
--- a/GaudiExamples/options/ControlFlow/AlgSequencer.py
+++ b/GaudiExamples/options/ControlFlow/AlgSequencer.py
@@ -1,6 +1,7 @@
 ###############################################################
 # Job options file
-# ==============================================================
+#===============================================================
+from __future__ import print_function
 
 from Gaudi.Configuration import *
 from Configurables import ParentAlg, StopperAlg, Prescaler, HelloWorld, TimingAuditor
@@ -42,9 +43,9 @@ sor = HelloWorld('OR') | EventCounter('ORCounter')
 
 all = ParentAlg() >> StopperAlg(StopCount=20) >> top >> sand >> sor
 
-print '# --- Configured Control Flow Expression:'
-print '#', all
-print '# ---'
+print('# --- Configured Control Flow Expression:')
+print('#', all)
+print('# ---')
 EventLoopMgr(PrintControlFlowExpression=True)
 # -----------------------------------------------------------------
 ApplicationMgr(TopAlg=[all],
diff --git a/GaudiExamples/options/ControlFlow/SuperAlgDynamicGraph.py b/GaudiExamples/options/ControlFlow/SuperAlgDynamicGraph.py
index 338dde2a82c0de8eb3c3578fa6fe734acaffd431..99baa9e4f6abe00fb9a26b0cd493a690e1899f4b 100644
--- a/GaudiExamples/options/ControlFlow/SuperAlgDynamicGraph.py
+++ b/GaudiExamples/options/ControlFlow/SuperAlgDynamicGraph.py
@@ -1,6 +1,7 @@
 ###############################################################
 # Job options file
 ###############################################################
+from __future__ import print_function
 
 from Gaudi.Configuration import *
 from Configurables import TimingAuditor, EventLoopMgr
@@ -56,9 +57,9 @@ top = s1 >> s2
 MySuperAlg('s2', PercentPass=75, OutputLevel=DEBUG, UseHelloWorld=False)
 
 
-print '# --- Configured Control Flow Expression:'
-print '#', top
-print '# ---'
+print('# --- Configured Control Flow Expression:')
+print('#', top)
+print('# ---')
 EventLoopMgr(PrintControlFlowExpression=True)
 
 # -----------------------------------------------------------------
diff --git a/GaudiExamples/options/ControlFlow/SuperAlgorithm.py b/GaudiExamples/options/ControlFlow/SuperAlgorithm.py
index 8ce7b55d12be9ff342701152052ece695ae8f10e..77e485b842e56f987f518caffcb815767907c555 100644
--- a/GaudiExamples/options/ControlFlow/SuperAlgorithm.py
+++ b/GaudiExamples/options/ControlFlow/SuperAlgorithm.py
@@ -1,6 +1,7 @@
 ###############################################################
 # Job options file
 ###############################################################
+from __future__ import print_function
 
 from Gaudi.Configuration import *
 from Configurables import ParentAlg, StopperAlg, TimingAuditor, HelloWorld
@@ -36,9 +37,9 @@ except AssertionError:
 
 all = ParentAlg() >> StopperAlg(StopCount=20) >> top >> sand >> sor
 
-print '# --- Configured Control Flow Expression:'
-print '#', all
-print '# ---'
+print('# --- Configured Control Flow Expression:')
+print('#', all)
+print('# ---')
 EventLoopMgr(PrintControlFlowExpression=True)
 
 # -----------------------------------------------------------------
diff --git a/GaudiExamples/options/MyGaudiAlg.py b/GaudiExamples/options/MyGaudiAlg.py
index 42989463dbc5dd72fc27c0efe474542de851d29f..32ae8499daf808db8cff92c6f39e9ae4c3cd7cd1 100644
--- a/GaudiExamples/options/MyGaudiAlg.py
+++ b/GaudiExamples/options/MyGaudiAlg.py
@@ -1,6 +1,7 @@
 ###############################################################
 # Job options file
-# ==============================================================
+#===============================================================
+from __future__ import print_function
 from Gaudi.Configuration import *
 from Configurables import MyGaudiAlgorithm
 
@@ -11,7 +12,7 @@ mygalg = MyGaudiAlgorithm('MyGaudiAlg',
                           OutputLevel=DEBUG)
 mygalg.PrivToolHandle.String = "Is a private tool"
 
-print mygalg
+print(mygalg)
 
 mygalg.tracks.Path = 'BestTracks/Tracks'
 mygalg.hits.Path = 'Rec/Hits'
@@ -19,7 +20,7 @@ mygalg.raw.AlternativePaths = ['/copy/RAW', '/Rec/RAW', '/DAQ/RAW']
 
 mygalg.RootInTES = '/Skim'
 
-print mygalg
+print(mygalg)
 
 ApplicationMgr(EvtMax=10,
                EvtSel='NONE',
diff --git a/GaudiExamples/options/Properties.py b/GaudiExamples/options/Properties.py
index be9d8664bb03ccc772e75daea6c2d3beed234ecb..05d9d700421f1ed98de0d19db0f0968dfc5a5e92 100644
--- a/GaudiExamples/options/Properties.py
+++ b/GaudiExamples/options/Properties.py
@@ -1,3 +1,9 @@
+# Python 2 compatibility
+try:
+    long
+except NameError:
+    long = int
+
 from Gaudi.Configuration import *
 
 # common configuration plus output level threshold
diff --git a/GaudiExamples/scripts/Aida2RootEx.py b/GaudiExamples/scripts/Aida2RootEx.py
index 02a41022a935c5f05e5e4a616ab4eeb2a033b14b..020d036bc35abb0212f1f9341454b40beaabbc35 100755
--- a/GaudiExamples/scripts/Aida2RootEx.py
+++ b/GaudiExamples/scripts/Aida2RootEx.py
@@ -7,6 +7,7 @@
 #  @date 2007-01-24
 # =============================================================================
 " Simple example to illustrate the usage of aida2root converter "
+from __future__ import print_function
 # =============================================================================
 __author__ = "Vanya BELYAEV ibelyaev@phys.syr.edu"
 # =============================================================================
@@ -141,7 +142,7 @@ def useScript(histos):
     for histo in histos:
         root = hsvc.getAsROOT(histo)
         if not root:
-            print "ERROR in access the histogram '%s' " % histo
+            print("ERROR in access the histogram '%s' " % histo)
             continue
         canvas = ROOT.TCanvas('canvas_%d' % i, histo, 250, 250)
         root.Draw()
@@ -156,7 +157,7 @@ def useScript(histos):
             # strictly speaking, it is not needed, but avoids a message on the stderr
             os.remove(name)
         canvas.Print(name)
-        print "The file name is '%s'" % name
+        print("The file name is '%s'" % name)
         i += 1
 
 # =============================================================================
@@ -166,7 +167,7 @@ def useScript(histos):
 # The actual job execution
 # =============================================================================
 if '__main__' == __name__:
-    print __doc__, __author__
+    print(__doc__, __author__)
 
     from GaudiPython.Bindings import AppMgr
 
diff --git a/GaudiExamples/scripts/ArrayProperties.py b/GaudiExamples/scripts/ArrayProperties.py
index 9b23e6d572496f842836405087b60ddbccd59c20..4daba74a804a99953259094168e7315d19f86279 100755
--- a/GaudiExamples/scripts/ArrayProperties.py
+++ b/GaudiExamples/scripts/ArrayProperties.py
@@ -8,6 +8,7 @@
 *                                                                             *
 *******************************************************************************
 """
+from __future__ import print_function
 # =============================================================================
 __author__ = 'Vanya BELYAEV Ivan.Belyaev@nikhef.nl'
 # =============================================================================
@@ -37,7 +38,7 @@ ApplicationMgr(
 # =============================================================================
 if '__main__' == __name__:
 
-    print __doc__, __author__
+    print(__doc__, __author__)
 
     # make sure cling can generate all required methods in Gaudi::Property
     import cppyy
diff --git a/GaudiExamples/scripts/BoostArrayProperties.py b/GaudiExamples/scripts/BoostArrayProperties.py
index ba7eea3d5628caf45c1ec6285354a05e28bc6b7f..491aa9d38e36ccf4eef413d5a8de6ceccf8de3a8 100755
--- a/GaudiExamples/scripts/BoostArrayProperties.py
+++ b/GaudiExamples/scripts/BoostArrayProperties.py
@@ -8,6 +8,7 @@
 *                                                                             *
 *******************************************************************************
 """
+from __future__ import print_function
 # =============================================================================
 __author__ = 'Vanya BELYAEV Ivan.Belyaev@nikhef.nl'
 # =============================================================================
@@ -37,7 +38,7 @@ ApplicationMgr(
 # =============================================================================
 if '__main__' == __name__:
 
-    print __doc__, __author__
+    print(__doc__, __author__)
 
     from GaudiPython.Bindings import AppMgr
 
diff --git a/GaudiExamples/scripts/CounterEx.py b/GaudiExamples/scripts/CounterEx.py
index f316d34bb11870c057c5e171f73817b7b8a98489..d38e18eaee49dadefe88dfdddd18e41a91c9cad0 100755
--- a/GaudiExamples/scripts/CounterEx.py
+++ b/GaudiExamples/scripts/CounterEx.py
@@ -77,11 +77,11 @@ class Counter(GaudiAlgo):
         executed = self.counter('executed')
         prnt = int(executed.flag())
         if 0 == prnt % 1000:
-            print " Event number %s " % prnt
+            print(" Event number %s " % prnt)
             self.printStat()
             bc = self.counter('eff')
             line = "(%s += %s)%s" % (bc.eff() * 100, bc.effErr() * 100, '%')
-            print ' Efficiency (binomial counter "eff"): %s' % line
+            print(' Efficiency (binomial counter "eff"): %s' % line)
 
         return SUCCESS
 
@@ -113,7 +113,7 @@ def configure(gaudi=None):
 # The actual job excution
 # =============================================================================
 if '__main__' == __name__:
-    print __doc__, __author__
+    print(__doc__, __author__)
     gaudi = GaudiPython.AppMgr()
     configure(gaudi)
     gaudi.run(5400)
diff --git a/GaudiExamples/scripts/EvtColRead.py b/GaudiExamples/scripts/EvtColRead.py
index e57202d668809d4bf84a7c9ed999b3b368cd2e66..3b1f4769a474d8d849555f72d3cb6e4268ab7411 100755
--- a/GaudiExamples/scripts/EvtColRead.py
+++ b/GaudiExamples/scripts/EvtColRead.py
@@ -8,6 +8,7 @@
 *                                                                             *
 *******************************************************************************
 """
+from __future__ import print_function
 # =============================================================================
 __author__ = 'Vanya BELYAEV Ivan.Belyaev@lapp.in2p3.fr'
 # =============================================================================
@@ -44,7 +45,7 @@ def configure(gaudi=None):
 # The actual job excution
 # =============================================================================
 if '__main__' == __name__:
-    print __doc__
+    print(__doc__)
     # configuration (options)
     from Configurables import GaudiPersistency, FileCatalog, ApplicationMgr
     GaudiPersistency()
diff --git a/GaudiExamples/scripts/EvtColWrite.py b/GaudiExamples/scripts/EvtColWrite.py
index 225b9672622ef9bd689e2ee434e51548c1752ce5..cebc014149eefa7b3d8507a2591fef26168af922 100755
--- a/GaudiExamples/scripts/EvtColWrite.py
+++ b/GaudiExamples/scripts/EvtColWrite.py
@@ -126,7 +126,7 @@ def configure(gaudi=None):
 # The actual job excution
 # =============================================================================
 if '__main__' == __name__:
-    print __doc__
+    print(__doc__)
     # configuration (options)
     from Configurables import GaudiPersistency, FileCatalog, ApplicationMgr
     GaudiPersistency()
diff --git a/GaudiExamples/scripts/ExtendedProperties.py b/GaudiExamples/scripts/ExtendedProperties.py
index 87148f7233d09f558207ab03fbc0115f7ab31416..e5678f6e0cb4a1e6c3e537e9b0c08b9ff906a891 100755
--- a/GaudiExamples/scripts/ExtendedProperties.py
+++ b/GaudiExamples/scripts/ExtendedProperties.py
@@ -8,6 +8,7 @@
 *                                                                             *
 *******************************************************************************
 """
+from __future__ import print_function
 # =============================================================================
 __author__ = 'Vanya BELYAEV ibelyaev@physics.syr.edu'
 # =============================================================================
@@ -108,7 +109,7 @@ def configure(gaudi=None):
 # =============================================================================
 if '__main__' == __name__:
 
-    print __doc__, __author__
+    print(__doc__, __author__)
 
     gaudi = gaudimodule.AppMgr()
     configure(gaudi)
@@ -125,20 +126,20 @@ if '__main__' == __name__:
     #
     props = alg.properties()
 
-    print 'All Properties of %s ' % alg.name()
+    print('All Properties of %s ' % alg.name())
     for p in props:
         v = props[p].value()
         t = type(v).__name__
-        print "Python: Name/Value:  '%s' / '%s' " % (p, v)
+        print("Python: Name/Value:  '%s' / '%s' " % (p, v))
 
     # get the properties in the form of python dictionary:
-    print 'All Properties of %s ' % alg.name()
+    print('All Properties of %s ' % alg.name())
     properties = {}
     for p in props:
         properties[p] = props[p].value()
 
     for p in properties:
-        print "Python: Name/Value:  '%s' / '%s' " % (p, properties[p])
+        print("Python: Name/Value:  '%s' / '%s' " % (p, properties[p]))
 
 
 # =============================================================================
diff --git a/GaudiExamples/scripts/ExtendedProperties2.py b/GaudiExamples/scripts/ExtendedProperties2.py
index 7b44bf80223ba6c43b8d5a2dc22524ed4a216f54..da3a858de25a37c0798de0cf3b09daad80f89611 100755
--- a/GaudiExamples/scripts/ExtendedProperties2.py
+++ b/GaudiExamples/scripts/ExtendedProperties2.py
@@ -7,6 +7,7 @@
 *                                                                             *
 *******************************************************************************
 """
+from __future__ import print_function
 # =============================================================================
 __author__ = 'Vanya BELYAEV ibelyaev@physics.syr.edu'
 # =============================================================================
@@ -39,7 +40,7 @@ ApplicationMgr(
 # =============================================================================
 if '__main__' == __name__:
 
-    print __doc__, __author__
+    print(__doc__, __author__)
 
     # make sure cling can generate all required methods in Gaudi::Property
     import cppyy
@@ -71,19 +72,19 @@ if '__main__' == __name__:
 
     try:
         xp2.SVector5 = [1, 2, 3, 4, 5, 6]
-    except Exception, e:
-        print ' Exception: ', e
+    except Exception as e:
+        print(' Exception: ', e)
 
     try:
         xp2.Point3D = (1, 2, 3, 4)
-    except Exception, e:
-        print ' Exception: ', e
+    except Exception as e:
+        print(' Exception: ', e)
 
     if not isWin:
         try:
             xp2.Vector4D = (1, 2, 3)
-        except Exception, e:
-            print ' Exception: ', e
+        except Exception as e:
+            print(' Exception: ', e)
 
     xp2.Vectors3D = [(1, 2, 3), (4, 5, 6), [7, 8, 9]]
     if not isWin:
diff --git a/GaudiExamples/scripts/HistoDumpEx.py b/GaudiExamples/scripts/HistoDumpEx.py
index c1f27e46af1418f42aafa732d846efb327bc020a..a8176f873507b90d42bae86ddd0f0d1c71f504d3 100755
--- a/GaudiExamples/scripts/HistoDumpEx.py
+++ b/GaudiExamples/scripts/HistoDumpEx.py
@@ -1,4 +1,5 @@
 #!/usr/bin/env python
+from __future__ import print_function
 
 import ROOT
 
@@ -12,7 +13,7 @@ h1 = ROOT.TH1D("h1", 'title', 200, -4,  4)
 p1 = ROOT.TProfile("p1", 'title', 200, -4,  4, -10, 10, 's')
 p2 = ROOT.TProfile("p2", 'title', 200, -4,  4, -10, 10)
 
-for i in xrange(0, 1000000):
+for i in range(0, 1000000):
     v = r.gauss(0, 2)
     v2 = r.gauss(0, 2)
     if v < 0:
@@ -22,11 +23,11 @@ for i in xrange(0, 1000000):
     p1.Fill(v, -0.1 * v2, 1)
     p2.Fill(v, -0.1 * v2, 1)
 
-print h1 . dump(50, 50, True)
-print h1 . dump(60, 30)
+print(h1 . dump(50, 50, True))
+print(h1 . dump(60, 30))
 
-print histoDump(h1, 10, 10)
-print histoDump(h1, 10, 10, True)
+print(histoDump(h1, 10, 10))
+print(histoDump(h1, 10, 10, True))
 
-print histoDump(p1, 10, 10, True)
-print histoDump(p2, 10, 10, True)
+print(histoDump(p1, 10, 10, True))
+print(histoDump(p2, 10, 10, True))
diff --git a/GaudiExamples/scripts/HistoEx.py b/GaudiExamples/scripts/HistoEx.py
index d75e11edf4fadbd5a0435f1059dcf497e9a1ace9..c914d88d05be64af84c97c1b506b0e40062f2ab5 100755
--- a/GaudiExamples/scripts/HistoEx.py
+++ b/GaudiExamples/scripts/HistoEx.py
@@ -8,6 +8,7 @@
 *                                                                             *
 *******************************************************************************
 """
+from __future__ import print_function
 # =============================================================================
 __author__ = 'Vanya BELYAEV Ivan.Belyaev@lapp.in2p3.fr'
 # =============================================================================
@@ -74,7 +75,7 @@ def configure(gaudi=None):
 # The actual job excution
 # =============================================================================
 if '__main__' == __name__:
-    print __doc__, __author__
+    print(__doc__, __author__)
 
     from GaudiPython.Bindings import AppMgr
     gaudi = AppMgr()
@@ -88,7 +89,7 @@ if '__main__' == __name__:
     for key in histos:
         histo = histos[key]
         if hasattr(histo, 'dump'):
-            print histo.dump(80, 20, True)
+            print(histo.dump(80, 20, True))
 
 # =============================================================================
 # The END
diff --git a/GaudiExamples/scripts/HistoEx1.py b/GaudiExamples/scripts/HistoEx1.py
index 9fdeeb26156ead50b78ba89916398dc65cc3a832..d7b00123ef3193cd7b276b3f757981c4fb007105 100755
--- a/GaudiExamples/scripts/HistoEx1.py
+++ b/GaudiExamples/scripts/HistoEx1.py
@@ -10,6 +10,7 @@
 *                                                                             *
 *******************************************************************************
 """
+from __future__ import print_function
 # =============================================================================
 __author__ = 'Vanya BELYAEV Ivan.Belyaev@lapp.in2p3.fr'
 # =============================================================================
@@ -79,7 +80,7 @@ def configure(gaudi=None):
 # The actual job excution
 # =============================================================================
 if '__main__' == __name__:
-    print __doc__, __author__
+    print(__doc__, __author__)
 
     from GaudiPython.Bindings import AppMgr
     gaudi = AppMgr()
@@ -94,6 +95,6 @@ if '__main__' == __name__:
     for key in histos:
         histo = histos[key]
         if hasattr(histo, 'dump'):
-            print histo.dump(80, 20, True)
+            print(histo.dump(80, 20, True))
 
 # =============================================================================
diff --git a/GaudiExamples/scripts/HistoEx2.py b/GaudiExamples/scripts/HistoEx2.py
index 3b2057fc31c62640835c54a4c8e9b030350755d7..3eb37a5b018ec3559908fab55c5046694f7960b7 100755
--- a/GaudiExamples/scripts/HistoEx2.py
+++ b/GaudiExamples/scripts/HistoEx2.py
@@ -10,6 +10,7 @@
 *                                                                             *
 *******************************************************************************
 """
+from __future__ import print_function
 # =============================================================================
 __author__ = 'Vanya BELYAEV ibelyaev@physics.syr.edu'
 # =============================================================================
@@ -76,7 +77,7 @@ def configure(gaudi=None):
 # The actual job execution
 # =============================================================================
 if '__main__' == __name__:
-    print __doc__, __author__
+    print(__doc__, __author__)
 
     from GaudiPython.Bindings import AppMgr
     import GaudiPython.HistoUtils
@@ -94,6 +95,6 @@ if '__main__' == __name__:
         histos = alg.Histos()
         for key in histos:
             histo = histos[key]
-            print " Alg='%s', ID='%s' , Histo=%s " % (alg.name(), key, histo)
+            print(" Alg='%s', ID='%s' , Histo=%s " % (alg.name(), key, histo))
             if hasattr(histo, 'dump'):
-                print histo.dump(60, 20, True)
+                print(histo.dump(60, 20, True))
diff --git a/GaudiExamples/scripts/HistoUtilsEx.py b/GaudiExamples/scripts/HistoUtilsEx.py
index 68efc4a8f088ef79de97a5c11c7175c36a4ecae4..315edd8f3d7f5aae9a6c54394de009d21422699d 100755
--- a/GaudiExamples/scripts/HistoUtilsEx.py
+++ b/GaudiExamples/scripts/HistoUtilsEx.py
@@ -10,6 +10,7 @@
 Simple example to illustrate the usage functions from HistoUtils module
 (Gaudi histograms outside of algorithm-scope in 'script-like' environment)
 """
+from __future__ import print_function
 # =============================================================================
 __author__ = "Vanya BELYAEV  Ivan.Belyaev@nikhef.nl"
 # =============================================================================
@@ -24,7 +25,7 @@ from GaudiPython.HistoUtils import (
     aida2root
 )
 
-print __doc__
+print(__doc__)
 
 # get the application manager   (create if needed)
 gaudi = AppMgr()
@@ -71,8 +72,8 @@ for i in xrange(0, 10000):
     histo2.fill(gauss())
 
 # print them:
-print ' Histo1:        ', histo1
-print ' Histo2:        ', histo2
+print(' Histo1:        ', histo1)
+print(' Histo2:        ', histo2)
 
 # convert to ROOT:
 rhisto1 = aida2root(histo1)
@@ -103,26 +104,26 @@ rhisto1.Print()
 rhisto2.Print()
 
 # get some "extra infomration"
-print ' Histo1 : mean    /err:  %10f +- %10f  ' % (
-    histo1.mean(), histo1.meanErr())
-print ' Histo1 : rms     /err:  %10f +- %10f  ' % (
-    histo1.rms(), histo1.rmsErr())
-print ' Histo1 : skewness/err:  %10f +- %10f  ' % (
-    histo1.skewness(), histo1.skewnessErr())
-print ' Histo1 : kurtosis/err:  %10f +- %10f  ' % (
-    histo1.kurtosis(), histo1.kurtosisErr())
-print ' Histo1 : path in THS : "%s"' % histo1.path()
-
-
-print ' Histo2 : mean    /err:  %10f +- %10f  ' % (
-    histo2.mean(), histo2.meanErr())
-print ' Histo2 : rms     /err:  %10f +- %10f  ' % (
-    histo2.rms(), histo2.rmsErr())
-print ' Histo2 : skewness/err:  %10f +- %10f  ' % (
-    histo2.skewness(), histo2.skewnessErr())
-print ' Histo2 : kurtosis/err:  %10f +- %10f  ' % (
-    histo2.kurtosis(), histo2.kurtosisErr())
-print ' Histo2 : path in THS : "%s"' % histo2.path()
+print(' Histo1 : mean    /err:  %10f +- %10f  ' % (
+    histo1.mean(), histo1.meanErr()))
+print(' Histo1 : rms     /err:  %10f +- %10f  ' % (
+    histo1.rms(), histo1.rmsErr()))
+print(' Histo1 : skewness/err:  %10f +- %10f  ' % (
+    histo1.skewness(), histo1.skewnessErr()))
+print(' Histo1 : kurtosis/err:  %10f +- %10f  ' % (
+    histo1.kurtosis(), histo1.kurtosisErr()))
+print(' Histo1 : path in THS : "%s"' % histo1.path())
+
+
+print(' Histo2 : mean    /err:  %10f +- %10f  ' % (
+    histo2.mean(), histo2.meanErr()))
+print(' Histo2 : rms     /err:  %10f +- %10f  ' % (
+    histo2.rms(), histo2.rmsErr()))
+print(' Histo2 : skewness/err:  %10f +- %10f  ' % (
+    histo2.skewness(), histo2.skewnessErr()))
+print(' Histo2 : kurtosis/err:  %10f +- %10f  ' % (
+    histo2.kurtosis(), histo2.kurtosisErr()))
+print(' Histo2 : path in THS : "%s"' % histo2.path())
 
 
 # =============================================================================
diff --git a/GaudiExamples/scripts/Mix.py b/GaudiExamples/scripts/Mix.py
index 5d8f16dedb3d1d997927c13295f6aede8ffa282c..844aa519752e6d1fb9d836d009a6d8cf1d7eadf1 100755
--- a/GaudiExamples/scripts/Mix.py
+++ b/GaudiExamples/scripts/Mix.py
@@ -9,6 +9,7 @@
 Simple example to illustrate the problem for task #13911
 https://savannah.cern.ch/task/?13911
 """
+from __future__ import print_function
 # =============================================================================
 __author__ = "Vanya BELYAEV Ivan.Belyaev@itep.ru"
 __date__ = "2010-04-24"
@@ -32,7 +33,7 @@ class SimpleAlgo(GaudiAlgo):
 
     def execute(self):
 
-        print 'I am SimpleAlgo.execute!  ', self.name()
+        print('I am SimpleAlgo.execute!  ', self.name())
 
         return SUCCESS
 
@@ -78,11 +79,11 @@ def configure():
 if '__main__' == __name__:
 
     # make printout of the own documentations
-    print '*' * 120
-    print __doc__
-    print ' Author  : %s ' % __author__
-    print ' Date    : %s ' % __date__
-    print '*' * 120
+    print('*' * 120)
+    print(__doc__)
+    print(' Author  : %s ' % __author__)
+    print(' Date    : %s ' % __date__)
+    print('*' * 120)
 
     configure()
 
diff --git a/GaudiExamples/scripts/Properties.py b/GaudiExamples/scripts/Properties.py
index 1bc10cb59a4b0da5468ece930fb317ed745ccf9c..ad06900c0835c8acddabecf339a3dd10f16a941e 100755
--- a/GaudiExamples/scripts/Properties.py
+++ b/GaudiExamples/scripts/Properties.py
@@ -8,6 +8,7 @@
 *                                                                             *
 *******************************************************************************
 """
+from __future__ import print_function
 # =============================================================================
 __author__ = 'Vanya BELYAEV ibelyaev@physics.syr.edu'
 # =============================================================================
@@ -97,7 +98,7 @@ def configure(gaudi=None):
 # =============================================================================
 if '__main__' == __name__:
 
-    print __doc__, __author__
+    print(__doc__, __author__)
 
     gaudi = gaudimodule.AppMgr()
     configure(gaudi)
@@ -106,10 +107,10 @@ if '__main__' == __name__:
     alg = gaudi.algorithm('PropertyAlg')
 
     props = alg.properties()
-    print 'Properties of %s ' % alg.name()
+    print('Properties of %s ' % alg.name())
     for p in props:
         v = props[p].value()
-        print "Python: Name/Value:  '%s' / '%s' " % (p, v)
+        print("Python: Name/Value:  '%s' / '%s' " % (p, v))
 
 # =============================================================================
 # The END
diff --git a/GaudiExamples/scripts/StringKeyEx.py b/GaudiExamples/scripts/StringKeyEx.py
index ed9168b6a9b23370e3cdf421d7a42a7eb4143cb5..8fd5e71608f57c56a6624b23c40059ff04af98b6 100755
--- a/GaudiExamples/scripts/StringKeyEx.py
+++ b/GaudiExamples/scripts/StringKeyEx.py
@@ -7,6 +7,7 @@
 *                                                                             *
 *******************************************************************************
 """
+from __future__ import print_function
 # =============================================================================
 __author__ = 'Vanya BELYAEV Ivan.Belyaev@nikhef.nl'
 # =============================================================================
@@ -38,8 +39,8 @@ ApplicationMgr(
 # =============================================================================
 if '__main__' == __name__:
 
-    print __doc__
-    print __author__
+    print(__doc__)
+    print(__author__)
 
     from GaudiPython.Bindings import AppMgr
 
@@ -57,13 +58,13 @@ if '__main__' == __name__:
 
     key = SK('new Key')
 
-    print 'set new key: ', key
+    print('set new key: ', key)
 
     ske.Key = key
 
     keys = [key, 'rrr', SK('s')]
 
-    print 'set new keys: ', keys
+    print('set new keys: ', keys)
 
     ske.Keys = keys
 
diff --git a/GaudiExamples/scripts/TupleEx.py b/GaudiExamples/scripts/TupleEx.py
index 4cbaf66d273b79627dc991d3914fb7668bf2d750..af32bb31aa0900ccc15a13d2df5703ec6c5aafc5 100755
--- a/GaudiExamples/scripts/TupleEx.py
+++ b/GaudiExamples/scripts/TupleEx.py
@@ -16,6 +16,7 @@
 *                                                                             *
 *******************************************************************************
 """
+from __future__ import print_function
 # =============================================================================
 __author__ = 'Vanya BELYAEV Ivan.Belyaev@lapp.in2p3.fr'
 # =============================================================================
@@ -103,7 +104,7 @@ def configure(gaudi=None):
 #  @author Vanya BELYAEV ibelyaev@physics.syr.edu
 #  @date 2006-11-26
 if '__main__' == __name__:
-    print __doc__
+    print(__doc__)
     from GaudiPython.Bindings import AppMgr
     gaudi = AppMgr()
     configure(gaudi)
diff --git a/GaudiExamples/scripts/TupleEx1.py b/GaudiExamples/scripts/TupleEx1.py
index 39b038f8ec3814b91145c8fffa75d30566eceb60..cee00d95fe48ec37b565b401332333feb67f1037 100755
--- a/GaudiExamples/scripts/TupleEx1.py
+++ b/GaudiExamples/scripts/TupleEx1.py
@@ -16,6 +16,7 @@
 *                                                                             *
 *******************************************************************************
 """
+from __future__ import print_function
 # =============================================================================
 __author__ = 'Vanya BELYAEV Ivan.Belyaev@lapp.in2p3.fr'
 # =============================================================================
@@ -144,10 +145,10 @@ class TupleEx1(TupleAlgo):
         # =====================================================================
         tuple3 = self.nTuple(3, "Fixed-size arrays/vectors")
 
-        tuple3.array('arflat', vct([flat() for i in xrange(0, 50)]))
-        tuple3.array('arexpo', vct([expo() for i in xrange(0, 62)]))
-        tuple3.array('argau', vct([gauss() for i in xrange(0, 42)]))
-        t = tuple([gauss() for i in xrange(0, 42)])
+        tuple3.array('arflat', vct([flat() for i in range(0, 50)]))
+        tuple3.array('arexpo', vct([expo() for i in range(0, 62)]))
+        tuple3.array('argau', vct([gauss() for i in range(0, 42)]))
+        t = tuple([gauss() for i in range(0, 42)])
         tuple3.array('argau2', vct(t))
 
         tuple3.write()
@@ -192,7 +193,7 @@ def configure(gaudi=None):
 #  @author Vanya BELYAEV ibelyaev@physics.syr.edu
 #  @date 2006-11-26
 if '__main__' == __name__:
-    print __doc__
+    print(__doc__)
     gaudi = GaudiPython.AppMgr()
     configure(gaudi)
     gaudi.run(20)
diff --git a/GaudiExamples/scripts/TupleEx2.py b/GaudiExamples/scripts/TupleEx2.py
index e7196d5dcea1f717ad81884bff543c72224df5f5..6c4926e154bdfd6459d42378c5e5285fe22672e6 100755
--- a/GaudiExamples/scripts/TupleEx2.py
+++ b/GaudiExamples/scripts/TupleEx2.py
@@ -16,6 +16,7 @@
 *                                                                             *
 *******************************************************************************
 """
+from __future__ import print_function
 # =============================================================================
 __author__ = 'Vanya BELYAEV Ivan.Belyaev@lapp.in2p3.fr'
 # =============================================================================
@@ -141,7 +142,7 @@ def configure(gaudi=None):
 #  @author Vanya BELYAEV ibelyaev@physics.syr.edu
 #  @date 2006-11-26
 if '__main__' == __name__:
-    print __doc__
+    print(__doc__)
     gaudi = GaudiPython.AppMgr()
     configure(gaudi)
     gaudi.run(20)
diff --git a/GaudiExamples/scripts/TupleEx3.py b/GaudiExamples/scripts/TupleEx3.py
index e889af80ed4cbe6f183973c87740eedbe049f6fa..365299e4c5dabd418d339becb3dbcdfde20c38aa 100755
--- a/GaudiExamples/scripts/TupleEx3.py
+++ b/GaudiExamples/scripts/TupleEx3.py
@@ -16,6 +16,7 @@
 *                                                                             *
 *******************************************************************************
 """
+from __future__ import print_function
 # =============================================================================
 __author__ = 'Vanya BELYAEV ibelyaev@physics.syr.edu'
 # =============================================================================
@@ -229,7 +230,7 @@ def configure(gaudi=None):
 #  @author Vanya BELYAEV ibelyaev@physics.syr.edu
 #  @date 2007-01-24
 if '__main__' == __name__:
-    print __doc__
+    print(__doc__)
     gaudi = GaudiPython.AppMgr()
     configure(gaudi)
     gaudi.run(10)
diff --git a/GaudiExamples/scripts/TupleEx4.py b/GaudiExamples/scripts/TupleEx4.py
index 0e28ffb3c24f780e85a3a1660297280e57266fae..8eca977250d3fd94484b875a3747449109489375 100755
--- a/GaudiExamples/scripts/TupleEx4.py
+++ b/GaudiExamples/scripts/TupleEx4.py
@@ -72,7 +72,7 @@ tup1 = TupleUtils.nTuple("path",  # the path
                          "It is a title for my n-tuple",  # the title
                          LUN='MYLUN1')  # logical unit
 # fill it:
-for i in xrange(0, 5000):
+for i in range(0, 5000):
     tup1.column('i', i)  # int
     tup1.column('g1', gauss())  # double
     tup1.column('g2', gauss())  # double
@@ -91,7 +91,7 @@ tup2 = TupleUtils.nTuple("another/path",  # the path
                          LUN='MYLUN1')  # logical unit
 
 # fill it:
-for i in xrange(0, 1000):
+for i in range(0, 1000):
 
     # Lorentz Vector
     lv1 = Math.PxPyPzEVector(gauss(), gauss(), gauss(), gauss())
@@ -107,7 +107,7 @@ tup3 = TupleUtils.nTuple("another/path",  # the path
                          LUN='MYLUN2')  # logical unit
 
 # fill it:
-for i in xrange(0, 1000):
+for i in range(0, 1000):
 
     # 3D-Vector
     v1 = Math.XYZVector(gauss(), gauss(), gauss())
@@ -123,7 +123,7 @@ tup4 = TupleUtils.nTuple("another/path",  # the path
                          LUN='MYLUN3')  # logical unit
 
 # fill it:
-for i in xrange(0, 1000):
+for i in range(0, 1000):
 
     # 3D-Point
     p1 = Math.XYZPoint(gauss(), gauss(), gauss())
@@ -147,7 +147,7 @@ CLHEP = cpp.CLHEP
 vct2 = CLHEP.HepVector
 
 # fill it!
-for i in xrange(0, 100):
+for i in range(0, 100):
 
     # variable size vector:
     v1 = vct1()
@@ -174,7 +174,7 @@ tup6 = TupleUtils.nTuple("another/path",  # the path
 
 Gaudi = cpp.Gaudi
 
-for i in xrange(0, 10):
+for i in range(0, 10):
 
     v1 = vct1()
     for j in range(0, 5):
@@ -210,7 +210,7 @@ tup7 = TupleUtils.nTuple("another/path",  # the path
                          "N-tuple: FixMatrices",  # the title
                          LUN='MYLUN3')  # logical unit
 
-for i in xrange(0, 100):
+for i in range(0, 100):
 
     m2 = Gaudi.Matrix2x2()
     tup7.matrix("m2", m2)  # Gaudi::Matrix2x2
@@ -238,7 +238,7 @@ tup8 = TupleUtils.nTuple("another/path",  # the path
                          "N-tuple: FixSymMatrices",  # the title
                          LUN='MYLUN2')  # logical unit
 
-for i in xrange(0, 100):
+for i in range(0, 100):
 
     m2 = Gaudi.SymMatrix2x2()
     tup8.matrix("m2", m2)  # Gaudi::SymMatrix2x2
@@ -268,7 +268,7 @@ tup9 = TupleUtils.nTuple("another/path2",  # the path
                          LUN='MYLUN2')  # logical unit
 
 
-for i in xrange(0, 100):
+for i in range(0, 100):
 
     t = cpp.Gaudi.Time()
 
diff --git a/GaudiExamples/scripts/bug_38882.py b/GaudiExamples/scripts/bug_38882.py
index 27a893488cdb4ff65a24f8220d1fa02afb61cfdf..12e812eea079f8e78db0e4203333b44612353f95 100755
--- a/GaudiExamples/scripts/bug_38882.py
+++ b/GaudiExamples/scripts/bug_38882.py
@@ -1,4 +1,5 @@
 #!/usr/bin/env python
+from __future__ import print_function
 # =============================================================================
 
 # =============================================================================
@@ -25,7 +26,7 @@ class TestAlg(GaudiAlgo):
 
     def execute(self):
         """ The main method 'execute', it is invoked for each event """
-        print "=== %s Execute ===" % self.name()
+        print("=== %s Execute ===" % self.name())
         return SUCCESS
 
 # =============================================================================
diff --git a/GaudiExamples/tests/qmtest/ExtPropValidator.py b/GaudiExamples/tests/qmtest/ExtPropValidator.py
index f2c2170d58babe9484489f73d01b9eaf888b7eac..220782315cb92ba9c17f752dfba2618538a4067c 100644
--- a/GaudiExamples/tests/qmtest/ExtPropValidator.py
+++ b/GaudiExamples/tests/qmtest/ExtPropValidator.py
@@ -50,7 +50,7 @@ def validate(stdout, stderr, result, causes):
     output = map(lambda l: l.rstrip(), stdout.splitlines())
     i = output.__iter__()
     try:
-        while not signature.match(i.next()):
+        while not signature.match(next(i)):
             pass
 
         properties = {}
diff --git a/GaudiExamples/tests/qmtest/gaudiexamples.qms/chronostatsvc.qms/per_event_file.qmt b/GaudiExamples/tests/qmtest/gaudiexamples.qms/chronostatsvc.qms/per_event_file.qmt
index 04fbff25ddb67aadfab0bcb6b74f5d0389975a82..c4f45119e64e9f44fb4f140d7d5292e2cd0ef22b 100644
--- a/GaudiExamples/tests/qmtest/gaudiexamples.qms/chronostatsvc.qms/per_event_file.qmt
+++ b/GaudiExamples/tests/qmtest/gaudiexamples.qms/chronostatsvc.qms/per_event_file.qmt
@@ -36,7 +36,7 @@ if not re.search(expected,stdout):
     causes.append("missing regexp match")
     result["GaudiTest.regexp"] = result.Quote(expected.replace("\\n","\n"))
 
-class Cause:
+class Cause(Exception):
     def __init__(self, msg):
         self.msg = msg
 
@@ -51,7 +51,7 @@ try:
     if entries[0] != 'Timing' or len(entries) != 401:
         raise Cause('timing log content')
 
-except Cause, c:
+except Cause as c:
     causes.append(c.msg)
 
 </text></argument>
diff --git a/GaudiExamples/tests/qmtest/gaudiexamples.qms/metadatasvc.qmt b/GaudiExamples/tests/qmtest/gaudiexamples.qms/metadatasvc.qmt
index 1fd67e676f0e7fb9ec14b90f9bc61eb6ecda29ce..5646692bcfbf32006d2f7082a1e561dfc8c69afa 100644
--- a/GaudiExamples/tests/qmtest/gaudiexamples.qms/metadatasvc.qmt
+++ b/GaudiExamples/tests/qmtest/gaudiexamples.qms/metadatasvc.qmt
@@ -20,7 +20,7 @@ stdout = filter(str.strip,
                 takewhile(str.strip,
                           islice(dropwhile(lambda l: 'Metadata collected:' not in l,
                                  stdout.splitlines()), 1, None)))
-stdout.sort()
+stdout = sorted(stdout)
 from subprocess import check_output, CalledProcessError
 try:
     info = eval(check_output(['dumpMetaData', 'TupleEx.root']))
diff --git a/GaudiExamples/tests/qmtest/gaudiexamples.qms/return_codes.qms/corrupted_input.qmt b/GaudiExamples/tests/qmtest/gaudiexamples.qms/return_codes.qms/corrupted_input.qmt
index cdd4b261b9f9631f387be8ec05a421596f531b29..b9b34042d05835e1e3818c7739e629727a686dc7 100644
--- a/GaudiExamples/tests/qmtest/gaudiexamples.qms/return_codes.qms/corrupted_input.qmt
+++ b/GaudiExamples/tests/qmtest/gaudiexamples.qms/return_codes.qms/corrupted_input.qmt
@@ -14,7 +14,7 @@ shutil.copy('ROOTIO.dst', 'ROOTIO_corrupted.dst')
 
 f = open('ROOTIO_corrupted.dst', 'rb+')
 f.seek(1024)
-f.write('corruption!')
+f.write('corruption!'.encode('utf-8'))
 f.close()
 
 esel = EventSelector()
diff --git a/GaudiExamples/tests/qmtest/gaudiexamples.qms/timing_histos.qmt b/GaudiExamples/tests/qmtest/gaudiexamples.qms/timing_histos.qmt
index bedf98d727d3ee7cccc90c99e428ef44e719f727..bd520ba1eca8f602a3a739eb4448150499ce3c22 100644
--- a/GaudiExamples/tests/qmtest/gaudiexamples.qms/timing_histos.qmt
+++ b/GaudiExamples/tests/qmtest/gaudiexamples.qms/timing_histos.qmt
@@ -16,6 +16,8 @@ testscript = os.path.join(os.environ['GAUDIEXAMPLESROOT'], 'tests', 'scripts', '
 
 test = Popen(['python', testscript], stdout=PIPE, stderr=PIPE)
 out, err = test.communicate()
+out = out.decode('utf-8')
+err = err.decode('utf-8')
 
 result['root_file_check.returncode'] = str(test.returncode)
 if test.returncode:
diff --git a/GaudiExamples/tests/scripts/test_timing_histo_file.py b/GaudiExamples/tests/scripts/test_timing_histo_file.py
index 6387d56a3791ca3c61435eae776f68099703f2ff..5fabc86592f01bbfbf7c14a26eb555745cea0f6e 100755
--- a/GaudiExamples/tests/scripts/test_timing_histo_file.py
+++ b/GaudiExamples/tests/scripts/test_timing_histo_file.py
@@ -5,6 +5,7 @@ Check the content of the ROOT file generated by the test 'gaudiexamples.timing_h
 
 The file must contain a directory called 'TIMER.TIMER' with 3 well defined histograms inside.
 """
+from __future__ import print_function
 import sys
 
 
@@ -57,7 +58,7 @@ def test():
 if __name__ == '__main__':
     try:
         test()
-    except AssertionError, a:
-        print "FAILURE:", a
+    except AssertionError as a:
+        print("FAILURE:", a)
         sys.exit(1)
-    print "SUCCESS"
+    print("SUCCESS")
diff --git a/GaudiHive/options/graphanalysis.py b/GaudiHive/options/graphanalysis.py
index 326027ff95865d198d3b56e157867b7cc9c31f4e..32b6e7901278675f0fd0e1b6c58af39c303cc133 100644
--- a/GaudiHive/options/graphanalysis.py
+++ b/GaudiHive/options/graphanalysis.py
@@ -1,3 +1,4 @@
+from __future__ import print_function
 from pygraph.classes.digraph import digraph
 from pygraph.algorithms.cycles import find_cycle
 from pygraph.algorithms.accessibility import connected_components
@@ -40,10 +41,10 @@ def analyze_and_fix_cycles(gr):
             has_loop = False
             continue
         n_cycles += 1
-        print cycle
-        print "Removed loop by deleting edge (%s,%s)" % (cycle[-1], cycle[0])
+        print(cycle)
+        print("Removed loop by deleting edge (%s,%s)" % (cycle[-1], cycle[0]))
         gr.del_edge((cycle[-1], cycle[0]))
-    print "\nIN TOTAL %i CYCLES\n" % (n_cycles)
+    print("\nIN TOTAL %i CYCLES\n" % (n_cycles))
     return n_cycles > 0  # whether it needed to fix cycles
 
 
@@ -54,10 +55,10 @@ def analyze_connected_componets(gr):
         cc_size[i] = 0
     for k, v in cc.iteritems():
         cc_size[v] = cc_size[v] + 1
-    print "Connected components have the following size:"
+    print("Connected components have the following size:")
 #    for k,v in cc_size.iteritems():
-#      print "%i : %i" %(k,v)
-    print "NUMBER OF CONNECTED COMPONENTS: %i" % (len(cc_size.keys()))
+#      print("%i : %i" %(k,v))
+    print("NUMBER OF CONNECTED COMPONENTS: %i" % (len(cc_size.keys())))
 
 
 def analyze_critical_path(gr):
@@ -73,9 +74,9 @@ def analyze_critical_path(gr):
     for edge in edges:
         critical_time += gr.edge_weight(edge)
 
-    print "Total time   : %s" % total_time
-    print "Critical path: %s" % critical_time
-    print "POSSIBLE SPEEDUP: %s" % (total_time / critical_time)
+    print("Total time   : %s" % total_time)
+    print("Critical path: %s" % critical_time)
+    print("POSSIBLE SPEEDUP: %s" % (total_time / critical_time))
 
 
 def print_graph_to_json(gr, filename):
diff --git a/GaudiHive/options/jsonFromLHCbLog.py b/GaudiHive/options/jsonFromLHCbLog.py
index 5cda50229ef31041ba706d3d1c515a8f9534295e..11b9df409fc25598255e3f0bd5f06c1e64bee6f4 100644
--- a/GaudiHive/options/jsonFromLHCbLog.py
+++ b/GaudiHive/options/jsonFromLHCbLog.py
@@ -1,3 +1,4 @@
+from __future__ import print_function
 from pygraph.classes.graph import graph
 
 
@@ -67,4 +68,4 @@ def load_brunel_scenario(filename):
 if __name__ == "__main__":
     json = {}
     json["algorithms"] = load_brunel_scenario("Brunel.TES.trace.log")
-    print json
+    print(json)
diff --git a/GaudiHive/profiling/brunelWrapper.py b/GaudiHive/profiling/brunelWrapper.py
index aabe4941b8bc176cd5bc1a14b2f38612e16c7a6c..5a77c71a2fe308a9d5a6e38d21e4aae4caf5fa69 100755
--- a/GaudiHive/profiling/brunelWrapper.py
+++ b/GaudiHive/profiling/brunelWrapper.py
@@ -5,7 +5,7 @@ This script acts as a wrapper in order to generate Gaudi configs for running
 with different parameters in order to perform performance measurements.
 It takes command line arguments.
 """
-
+from __future__ import print_function
 import optparse
 """
 NumberOfEvents = 10
@@ -96,7 +96,7 @@ if __name__ == "__main__":
     if options.bg:
         command += " &"
 
-    print command
+    print(command)
     if options.execbrunel:
         import os
         os.system(command)
diff --git a/GaudiHive/profiling/plotBacklogPyRoot.py b/GaudiHive/profiling/plotBacklogPyRoot.py
index 85bee7683b9431f00863cfec8e8e38b08801819e..b1b327f959175a0767d04f2d5dac2940dc215444 100644
--- a/GaudiHive/profiling/plotBacklogPyRoot.py
+++ b/GaudiHive/profiling/plotBacklogPyRoot.py
@@ -1,3 +1,4 @@
+from __future__ import print_function
 from ROOT import *
 import sys
 import re
@@ -79,7 +80,7 @@ def createInFlightGraph(nevts):
     graph.SetLineStyle(2)
     graph.SetTitle(
         "GaudiHive Backlog (Brunel, 100 evts);Events Finished;Event Backlog")
-    print NEventsInFlight
+    print(NEventsInFlight)
     return graph
 
 
@@ -137,6 +138,6 @@ def doPlot(logfilename, logfilename_copy):
 if __name__ == "__main__":
     argc = len(sys.argv)
     if argc != 3:
-        print "Usage: plotBacklogPyRoot.py logfilename logfilename_copy"
+        print("Usage: plotBacklogPyRoot.py logfilename logfilename_copy")
         sys.exit(1)
     doPlot(sys.argv[1], sys.argv[2])
diff --git a/GaudiHive/profiling/plotClonesPyRoot.py b/GaudiHive/profiling/plotClonesPyRoot.py
index b50f1d4f89b669387a35bf9ac7531c14fd3ef9ca..8bc783371b271f3178d086eb2be3cff6e14a3720 100644
--- a/GaudiHive/profiling/plotClonesPyRoot.py
+++ b/GaudiHive/profiling/plotClonesPyRoot.py
@@ -1,3 +1,4 @@
+from __future__ import print_function
 from ROOT import *
 import sys
 import re
@@ -68,7 +69,7 @@ def getCountLatexes(vals, xmax):
     max_nclones = int(max(vals, key=getNclones)[1])
 
     latexes = []
-    for i in xrange(1, max_nclones + 1):
+    for i in range(1, max_nclones + 1):
         n_algos = len(
             filter(lambda runtime_nclones: runtime_nclones[1] == i, vals))
         latexes.append(getText(xmax * 1.01,
@@ -122,6 +123,6 @@ def doPlot(logfilename):
 if __name__ == "__main__":
     argc = len(sys.argv)
     if argc != 2:
-        print "Usage: plotClonesPyRoot.py logfilename"
+        print("Usage: plotClonesPyRoot.py logfilename")
         sys.exit(1)
     doPlot(sys.argv[1])
diff --git a/GaudiHive/profiling/plotSpeedup.py b/GaudiHive/profiling/plotSpeedup.py
index c4d13c820b9087708d893cdf79dc70b08781d81b..b208eccdf91b3c684f36131a25d851b89be4786b 100644
--- a/GaudiHive/profiling/plotSpeedup.py
+++ b/GaudiHive/profiling/plotSpeedup.py
@@ -1,4 +1,5 @@
 #! /usr/bin/env python
+from __future__ import print_function
 
 """
 Script that fetches all the logfiles from disk and reads the timings.
@@ -24,12 +25,12 @@ filename_scheleton = "timing_measurement_BrunelScenario_n150_eif%s_aif%s_nthread
 def getRuntime(n_algos_in_flight,  n_evts_in_flight, cloneFlag):
     filename = filename_scheleton % (
         n_evts_in_flight, n_algos_in_flight, cloneFlag)
-    print filename
+    print(filename)
     rt = 0.
     for line in open(filename, "r").readlines():
         rt = float(line[:-1])
-        # print filename
-        # print rt
+        # print(filename)
+        # print(rt)
     return rt
 
 
@@ -98,7 +99,7 @@ def make_plot(runtimes, cloneFlag):
     graphs = []
     first = True
     for colour, n_evts_in_flight, line_style in zip(colour_l, n_evts_in_flight_l, line_style_l):
-        print n_evts_in_flight
+        print(n_evts_in_flight)
         graph = getSingleGraph(n_evts_in_flight, cloneFlag,
                                runtimes, colour, line_style)
         opts = "LSame"
diff --git a/GaudiHive/profiling/plotSpeedupsPyRoot.py b/GaudiHive/profiling/plotSpeedupsPyRoot.py
index fdda121c033361d55f36c3b84301473534e1be5c..7bcd557925b61ce8a1700c88ca7695df9eb0042f 100644
--- a/GaudiHive/profiling/plotSpeedupsPyRoot.py
+++ b/GaudiHive/profiling/plotSpeedupsPyRoot.py
@@ -1,3 +1,4 @@
+from __future__ import print_function
 from ROOT import *
 
 '''
@@ -117,10 +118,10 @@ if len(sys.argv) > 1:
 
 # main loop: just printouts
 for neif in neif_l:
-    print "Events in flight: %s" % neif
+    print("Events in flight: %s" % neif)
     for tn in nts:
-        print "%s %s %s" % (tn, xtractTiming(
-            neif, tn, False), xtractTiming(neif, tn, True))
+        print("%s %s %s" % (tn, xtractTiming(
+            neif, tn, False), xtractTiming(neif, tn, True)))
 
 
 len_nt = len(nts) + 1
diff --git a/GaudiHive/profiling/prepareBenchmark.py b/GaudiHive/profiling/prepareBenchmark.py
index 664966c8c012f890f5dde305a926e6cb1defbd7a..a46e72582576d5ca57d96f240a5b161118894d26 100644
--- a/GaudiHive/profiling/prepareBenchmark.py
+++ b/GaudiHive/profiling/prepareBenchmark.py
@@ -1,5 +1,5 @@
 # File to prepare the configs and commands to be executed for the benchmark
-
+from __future__ import print_function
 import commands
 
 
@@ -24,10 +24,10 @@ def prepareConfig(template_filename, n_threads=10, n_parallel_events=10, n_paral
 if __name__ == "__main__":
 
     n_threads = 10
-    for n_algos in xrange(1, 11):
-        for n_events in xrange(1, n_algos + 1):
+    for n_algos in range(1, 11):
+        for n_events in range(1, n_algos + 1):
             config = prepareConfig("../options/BrunelScenario.py", n_threads=n_threads,
                                    n_parallel_events=n_events, n_parallel_algorithms=n_algos)
             # config.replace(".py",".log"))
-            print "/usr/bin/time -f %%S -o %s.time `alias gaudirun` %s > %s" % (
-                config.replace(".py", ""), config, "/dev/null")
+            print("/usr/bin/time -f %%S -o %s.time `alias gaudirun` %s > %s" % (
+                config.replace(".py", ""), config, "/dev/null"))
diff --git a/GaudiHive/python/GaudiHive/precedence.py b/GaudiHive/python/GaudiHive/precedence.py
index eb982b8d9bfc099ccdb2766ad8df12bca3aedcb0..0a4d6f37e7c3549a019adca123b5c4f5ebc847af 100644
--- a/GaudiHive/python/GaudiHive/precedence.py
+++ b/GaudiHive/python/GaudiHive/precedence.py
@@ -1,3 +1,4 @@
+from __future__ import print_function
 import os
 import sys
 import random
@@ -15,7 +16,7 @@ def _buildFilePath(filePath):
         __fullFilePath__ = os.path.realpath(os.path.join(
             os.environ.get('GAUDIHIVEROOT', ''), "data", filePath))
         if not os.path.exists(__fullFilePath__):
-            print "\nERROR: invalid file path '%s'. It must be either absolute, or relative to '$GAUDIHIVEROOT/data/'." % filePath
+            print("\nERROR: invalid file path '%s'. It must be either absolute, or relative to '$GAUDIHIVEROOT/data/'." % filePath)
             sys.exit(1)
     else:
         __fullFilePath__ = filePath
@@ -66,8 +67,8 @@ class RealTimeValue(object):
                 time = float(self.timings[capAlgoName])
             else:
                 time = self.defaultTime
-                print "WARNING: TimiNg for %s (or %s) not found in the provided library, using default one: %s" % (
-                    algoName, capAlgoName, time)
+                print("WARNING: TimiNg for %s (or %s) not found in the provided library, using default one: %s" % (
+                    algoName, capAlgoName, time))
 
         time = time * self.factor
 
@@ -161,28 +162,28 @@ class CruncherSequence(object):
         if showStat:
             import pprint
 
-            print "\n===== Statistics on Algorithms ====="
-            print "Total number of algorithm nodes: ", len(
-                self.unique_algos) + sum([self.dupl_algos[i] - 1 for i in self.dupl_algos])
-            print "Number of unique algorithms: ", len(self.unique_algos)
-            print "  -->", len(self.dupl_algos), "of them being re-used with the following distribution: ", [
-                self.dupl_algos[i] for i in self.dupl_algos]
+            print("\n===== Statistics on Algorithms =====")
+            print("Total number of algorithm nodes: ", len(
+                self.unique_algos) + sum([self.dupl_algos[i] - 1 for i in self.dupl_algos]))
+            print("Number of unique algorithms: ", len(self.unique_algos))
+            print("  -->", len(self.dupl_algos), "of them being re-used with the following distribution: ", [
+                self.dupl_algos[i] for i in self.dupl_algos])
             # pprint.pprint(dupl_algos)
 
-            print "\n===== Statistics on Sequencers ====="
-            print "Total number of sequencers: ", len(
-                self.unique_sequencers) + sum([self.dupl_seqs[i] - 1 for i in self.dupl_seqs])
-            print "Number of unique sequencers: ", len(self.unique_sequencers)
-            print "  -->", len(self.dupl_seqs), "of them being re-used with the following distribution: ", [
-                self.dupl_seqs[i] for i in self.dupl_seqs]
+            print("\n===== Statistics on Sequencers =====")
+            print("Total number of sequencers: ", len(
+                self.unique_sequencers) + sum([self.dupl_seqs[i] - 1 for i in self.dupl_seqs]))
+            print("Number of unique sequencers: ", len(self.unique_sequencers))
+            print("  -->", len(self.dupl_seqs), "of them being re-used with the following distribution: ", [
+                self.dupl_seqs[i] for i in self.dupl_seqs])
             # pprint.pprint(dupl_seqs)
-            print "Number of OR-sequencers: ", len(self.OR_sequencers)
+            print("Number of OR-sequencers: ", len(self.OR_sequencers))
 
-            print "\n===== Statistics on DataObjects ====="
-            print "Number of unique DataObjects: ", len(
-                self.unique_data_objects)
+            print("\n===== Statistics on DataObjects =====")
+            print("Number of unique DataObjects: ", len(
+                self.unique_data_objects))
             # pprint.pprint(self.unique_data_objects)
-            print
+            print()
 
     def get(self):
 
@@ -239,7 +240,7 @@ class CruncherSequence(object):
                     self.OR_sequencers.append(n)
                     seq_daughter.ModeOR = True
                 # if self.cfg.node[n].get('Lazy') == 'False':
-                #    print "Non-Lazy - ", n
+                #    print("Non-Lazy - ", n)
                 seq_daughter.ShortCircuit = False
                 if seq_daughter not in seq.Members:
                     seq.Members += [seq_daughter]
diff --git a/GaudiHive/scripts/hivetimeline.py b/GaudiHive/scripts/hivetimeline.py
index bab2d82d8b0b4c82410096e3d3641ef6f4a4a993..12fb8f520db1833816831ec376a121972a3a1d15 100755
--- a/GaudiHive/scripts/hivetimeline.py
+++ b/GaudiHive/scripts/hivetimeline.py
@@ -1,6 +1,6 @@
 #!/usr/bin/env python
 """Plot timeline from TimelineSvc"""
-
+from __future__ import print_function
 __author__ = "Frank Winklmeier"
 
 import sys
@@ -97,7 +97,7 @@ def plot(data, showThreads=True, batch=False, nevtcolors=10, width=1200, height=
         if alg not in colors and len(mycolors) > 0:
             colors[alg] = mycolors.pop(0)
             if len(mycolors) == 0:
-                print "Too many algorithm to show"
+                print("Too many algorithm to show")
 
         if alg in colors:
             t0 = d.start - tmin
diff --git a/GaudiKernel/GaudiKernel/MapBase.h b/GaudiKernel/GaudiKernel/MapBase.h
index f9895d5eef1721024aadc5ca0d0fb35b829d2c2f..35f90de385b9284381f811d5327583e04a9c6f6d 100644
--- a/GaudiKernel/GaudiKernel/MapBase.h
+++ b/GaudiKernel/GaudiKernel/MapBase.h
@@ -33,7 +33,7 @@ namespace Gaudi
      *   - <c> items   </c>
      *   - <c> values  </c>
      *   - <c> get     </c>
-     *   - <c> has_key </c>
+     *   - <c> has_key </c> (for Python 2)
      *
      *  Thus it behaves practically identically
      *  with builtin-type <c>dict</c>
diff --git a/GaudiKernel/python/GaudiConfig/ControlFlow.py b/GaudiKernel/python/GaudiConfig/ControlFlow.py
index e57a70be24d5406dbda87ba5e2d7b96d526f9be6..59a440c519f0dc7e02b6d3fca25f251c9cf68de4 100644
--- a/GaudiKernel/python/GaudiConfig/ControlFlow.py
+++ b/GaudiKernel/python/GaudiConfig/ControlFlow.py
@@ -3,6 +3,7 @@ Classes for the implementation of the Control Flow Structure Syntax.
 
 @see: https://github.com/lhcb/scheduling-event-model/tree/master/controlflow_syntax
 '''
+from __future__ import print_function
 
 
 class ControlFlowNode(object):
@@ -41,6 +42,15 @@ class ControlFlowNode(object):
     def __eq__(self, other):
         return (repr(self) == repr(other))
 
+    def __hash__(self):
+        """Return a unique identifier for this object.
+
+        As we use the `repr` of this object to check for equality, we use it
+        here to define uniqueness.
+        """
+        # The hash of the 1-tuple containing the repr of this object
+        return hash((repr(self),))
+
     def getFullName(self):
         '''
         Allow use of an expression as an algorihtm/sequence in a Gaudi job
@@ -205,12 +215,12 @@ class _TestVisitor(object):
 
     def enter(self, visitee):
         self.depths += 1
-        print "%sEntering %s" % (self.depths * " ", type(visitee))
+        print("%sEntering %s" % (self.depths * " ", type(visitee)))
         if isinstance(visitee, ControlFlowLeaf):
-            print "%s Algorithm name: %s" % (" " * self.depths, visitee)
+            print("%s Algorithm name: %s" % (" " * self.depths, visitee))
 
     def leave(self, visitee):
-        print "%sLeaving %s" % (self.depths * " ", type(visitee))
+        print("%sLeaving %s" % (self.depths * " ", type(visitee)))
         self.depths -= 1
 
 
@@ -325,11 +335,11 @@ def test():
     aLine = line("MyTriggerPath", expression)
     visitor = _TestVisitor()
     visitor2 = DotVisitor()
-    print "\nPrinting trigger line:"
-    print aLine
-    print "\nPrinting expression:"
-    print expression
-    print "\nTraversing through expression:\n"
+    print("\nPrinting trigger line:")
+    print(aLine)
+    print("\nPrinting expression:")
+    print(expression)
+    print("\nTraversing through expression:\n")
     expression.visitNode(visitor)
     expression.visitNode(visitor2)
     visitor2.write("out.dot")
diff --git a/GaudiKernel/python/GaudiKernel/Configurable.py b/GaudiKernel/python/GaudiKernel/Configurable.py
index 1f24e8f25e4506e44fd5563b79b123656cffdb09..cb844fb25900f0ecf31074f84612d517d62673e0 100644
--- a/GaudiKernel/python/GaudiKernel/Configurable.py
+++ b/GaudiKernel/python/GaudiKernel/Configurable.py
@@ -1,6 +1,7 @@
 # File: AthenaCommon/python/Configurable.py
 # Author: Wim Lavrijsen (WLavrijsen@lbl.gov)
 # Author: Martin Woudstra (Martin.Woudstra@cern.ch)
+from __future__ import absolute_import
 
 import copy
 import string
@@ -8,6 +9,7 @@ import types
 import os
 import sys
 from inspect import isclass
+import six
 import GaudiKernel.ConfigurableMeta as ConfigurableMeta
 from GaudiKernel.Constants import error_explanation, \
     VERBOSE, DEBUG, INFO, WARNING, ERROR, FATAL
@@ -104,7 +106,7 @@ class PropertyReference(object):
 # base class for configurable Gaudi algorithms/services/algtools/etc. ======
 
 
-class Configurable(object):
+class Configurable(six.with_metaclass(ConfigurableMeta.ConfigurableMeta, object)):
     """Base class for Gaudi components that implement the IProperty interface.
        Provides most of the boilerplate code, but the actual useful classes
        are its derived ConfigurableAlgorithm, ConfigurableService, and
@@ -119,8 +121,6 @@ class Configurable(object):
     printHeaderWidth = 100
     printHeaderPre = 5
 
-    __metaclass__ = ConfigurableMeta.ConfigurableMeta
-
     __slots__ = (
         '__children',           # controlled components, e.g. private AlgTools
         '__tools',              # private AlgTools  (#PM-->)
@@ -143,19 +143,21 @@ class Configurable(object):
            of a type with the same name will return the same instance."""
 
         global log
+        func_code = six.get_function_code(cls.__init__)
+        func_defaults = six.get_function_defaults(cls.__init__)
         # try to get the name of the Configurable (having a name is compulsory)
         if 'name' in kwargs:
             # simple keyword (by far the easiest)
             name = kwargs['name']
-        elif 'name' in cls.__init__.func_code.co_varnames:
+        elif 'name' in func_code.co_varnames:
             # either positional in args, or default
-            index = list(cls.__init__.func_code.co_varnames).index('name')
+            index = list(func_code.co_varnames).index('name')
             try:
              # var names index is offset by one as __init__ is to be called with self
                 name = args[index - 1]
             except IndexError:
              # retrieve default value, then
-                name = cls.__init__.func_defaults[index - (len(args) + 1)]
+                name = func_defaults[index - (len(args) + 1)]
         else:
             # positional index is assumed (will work most of the time)
             try:
@@ -282,8 +284,8 @@ class Configurable(object):
 
         # this is an abstract class
         if klass == Configurable:
-            raise TypeError, "%s is an ABC and can not be instantiated" % str(
-                Configurable)
+            raise TypeError("%s is an ABC and can not be instantiated" % str(
+                Configurable))
 
         # the following methods require overloading
         # NOT YET  meths = { 'getServices'   : 1,    # retrieve list of services to configure
@@ -294,17 +296,18 @@ class Configurable(object):
 
         for meth, nArgs in meths.items():
             try:
-                f = getattr(klass, meth).im_func
+                f = six.get_unbound_function(getattr(klass, meth))
             except AttributeError:
-                raise NotImplementedError, "%s is missing in class %s" % (
-                    meth, str(klass))
+                raise NotImplementedError("%s is missing in class %s" % (
+                    meth, str(klass)))
 
             # in addition, verify the number of arguments w/o defaults
-            nargcount = f.func_code.co_argcount
-            ndefaults = f.func_defaults and len(f.func_defaults) or 0
+            nargcount = six.get_function_code(f).co_argcount
+            fdefaults = six.get_function_defaults(f)
+            ndefaults = fdefaults and len(fdefaults) or 0
             if not nargcount - ndefaults <= nArgs <= nargcount:
-                raise TypeError, "%s.%s requires exactly %d arguments" % (
-                    klass, meth, nArgs)
+                raise TypeError( "%s.%s requires exactly %d arguments" % (
+                    klass, meth, nArgs))
 
         # for using this Configurable as a (Gaudi) sequence
         self.__children = []
@@ -471,9 +474,12 @@ class Configurable(object):
         except (AttributeError, KeyError):
             pass
 
-    def __nonzero__(self):
+    def __bool__(self):
         return True
 
+    # Python 2 compatibility
+    __nonzero__ = __bool__
+
     def remove(self, items):
         if type(items) != list and type(items) != tuple:
             items = [items]
@@ -502,14 +508,14 @@ class Configurable(object):
         if hasattr(cc, 'setParent') and parent:
             try:
                 cc.setParent(parent)
-            except RuntimeError, e:
+            except RuntimeError as e:
                 # temporary backdoor resolution for compatibility
                 log.error(str(e) + '%s', error_explanation)
                 ccbd = cc.configurables[cc.getJobOptName()]
 
                 # merge properties, new over pre-existing
                 for proxy in self._properties.values():
-                    if proxy.history.has_key(cc):
+                    if cc in proxy.history:
                         proxy.__set__(ccbd, proxy.__get__(cc))
 
                 # consolidate
@@ -578,8 +584,9 @@ class Configurable(object):
         # make sure base class init has been called
         if not hasattr(self, '_initok') or not self._initok:
             # could check more, but this is the only explanation
-            raise TypeError, \
+            raise TypeError(
                 "Configurable.__init__ not called in %s override" % self.__class__.__name__
+            )
 
 #      log.debug("calling setup() on " + self.getFullJobOptName())
 
@@ -822,7 +829,7 @@ class Configurable(object):
                 classname = tool.__name__
             else:
                 classname = type(tool).__name__
-            raise TypeError, "addTool requires AlgTool configurable. Got %s type" % classname
+            raise TypeError("addTool requires AlgTool configurable. Got %s type" % classname)
         self.__tools[name] = priv_tool
         if name in self.__slots__:
             # this is to avoid that the property hides the tool
@@ -1086,6 +1093,15 @@ class ConfigurableAlgorithm(Configurable):
     def __eq__(self, other):
         return (repr(self) == repr(other))
 
+    def __hash__(self):
+        """Return a unique identifier for this object.
+
+        As we use the `repr` of this object to check for equality, we use it
+        here to define uniqueness.
+        """
+        # The hash of the 1-tuple containing the repr of this object
+        return hash((repr(self),))
+
 
 class ConfigurableService(Configurable):
     __slots__ = {'OutputLevel': 0,
@@ -1145,8 +1161,8 @@ class ConfigurableAlgTool(Configurable):
         return pop + Configurable.getPrintTitle(self)
 
     def setParent(self, parentName):
-        #       print "ConfigurableAlgTool.setParent(%s@%x,%r)" % (self.getName(),id(self),parentName)
-        #       print "Calling stack:"
+        #       print("ConfigurableAlgTool.setParent(%s@%x,%r)" % (self.getName(),id(self),parentName))
+        #       print("Calling stack:")
         #       import traceback
         #       traceback.print_stack()
         # propagate parent to AlgTools in children
@@ -1485,8 +1501,8 @@ def applyConfigurableUsers():
         # easiest way to fix bug #103803.
         # <https://savannah.cern.ch/bugs/?103803>
         while True:
-            yield (c for c in Configurable.allConfigurables.values()
-                   if c.isApplicable()).next()
+            yield next(c for c in Configurable.allConfigurables.values()
+                       if c.isApplicable())
 
     debugApplyOrder = 'GAUDI_DUBUG_CONF_USER' in os.environ
     for c in applicableConfUsers():
@@ -1613,7 +1629,7 @@ def purge():
     # FIXME: (MCl) this is needed because instances of ConfigurableGeneric are not
     #        migrated to the correct class when this is known.
     ConfigurableGeneric.configurables.clear()
-    from ProcessJobOptions import _included_files
+    from .ProcessJobOptions import _included_files
     import os.path
     import sys
     for file in _included_files:
@@ -1708,7 +1724,7 @@ class SuperAlgorithm(ControlFlowNode):
                      type(instance).__name__)
             return instance
         else:
-            instance = super(SuperAlgorithm, cls).__new__(cls, name, **kwargs)
+            instance = super(SuperAlgorithm, cls).__new__(cls)
             Configurable.allConfigurables[name] = instance
             return instance
 
diff --git a/GaudiKernel/python/GaudiKernel/ConfigurableDb.py b/GaudiKernel/python/GaudiKernel/ConfigurableDb.py
index 969265f5900f20588ebebee8ca51a27fd2eddcbb..7cf688e34777a006c449e4431a6468723c9aa74d 100644
--- a/GaudiKernel/python/GaudiKernel/ConfigurableDb.py
+++ b/GaudiKernel/python/GaudiKernel/ConfigurableDb.py
@@ -10,7 +10,11 @@ to feed the AthenaEmacs module..."""
 __all__ = ['CfgDb', 'cfgDb', 'loadConfigurableDb', 'getConfigurable']
 
 import string
-_transtable = string.maketrans('<>&*,: ().', '__rp__s___')
+try:
+    _transtable = str.maketrans('<>&*,: ().', '__rp__s___')
+except AttributeError:
+    # Python 2 compatibility
+    _transtable = string.maketrans('<>&*,: ().', '__rp__s___')
 
 import logging
 log = logging.getLogger('ConfigurableDb')
@@ -46,12 +50,12 @@ class _CfgDb(dict):
         cfg = {'package': package,
                'module': module,
                'lib': lib}
-        if self.has_key(configurable):
+        if configurable in self:
             # check if it comes from the same library...
             if cfg['lib'] != self[configurable]['lib']:
                 log.debug("dup!! [%s] p=%s m=%s lib=%s",
                           configurable, package, module, lib)
-                if self._duplicates.has_key(configurable):
+                if configurable in self._duplicates:
                     self._duplicates[configurable] += [cfg]
                 else:
                     self._duplicates[configurable] = [cfg]
@@ -139,7 +143,7 @@ def loadConfigurableDb():
             log.debug("\t-loading [%s]...", confDb)
             try:
                 cfgDb._loadModule(confDb)
-            except Exception, err:
+            except Exception as err:
                 log.warning("Could not load file [%s] !", confDb)
                 log.warning("Reason: %s", err)
             nFiles += 1
@@ -153,7 +157,11 @@ def getConfigurable(className, requester='', assumeCxxClass=True):
     confClass = className
     if assumeCxxClass:
         # assume className is C++: --> translate to python
-        confClass = string.translate(confClass, _transtable)
+        try:
+            confClass = str.translate(confClass, _transtable)
+        except AttributeError:
+            # Python 2 compatibility
+            confClass = string.translate(confClass, _transtable)
     # see if I have it in my dictionary
     confClassInfo = cfgDb.get(confClass)
     if not confClassInfo:
diff --git a/GaudiKernel/python/GaudiKernel/GaudiHandles.py b/GaudiKernel/python/GaudiKernel/GaudiHandles.py
index 51929bb8629151e8f23a13a0ae5e8037b66001c8..e9b70ea5a9ee2f052317fcc645376d42bd916331 100644
--- a/GaudiKernel/python/GaudiKernel/GaudiHandles.py
+++ b/GaudiKernel/python/GaudiKernel/GaudiHandles.py
@@ -1,3 +1,4 @@
+from __future__ import print_function
 # explicit list for wildcard imports
 __all__ = [
     "GaudiHandle",
@@ -187,8 +188,8 @@ class GaudiHandleArray(list):
             # not yet there, so add it
             list.append(self, value)
         else:
-            print "%s    WARNING %r with instance name %r already in list. Not adding %r" % \
-                  (self.__class__.__name__, oldValue, oldValue.getName(), value)
+            print("%s    WARNING %r with instance name %r already in list. Not adding %r" % \
+                  (self.__class__.__name__, oldValue, oldValue.getName(), value))
 
     def isPublic(self):
         return self.__class__.handleType.isPublic
diff --git a/GaudiKernel/python/GaudiKernel/ProcessJobOptions.py b/GaudiKernel/python/GaudiKernel/ProcessJobOptions.py
index d6a2745b0c71e45a4735f7ba05cc1f1a9a87fc27..ae19041c6d7177aea7ef5ae3cbaf586b9dff3d0c 100644
--- a/GaudiKernel/python/GaudiKernel/ProcessJobOptions.py
+++ b/GaudiKernel/python/GaudiKernel/ProcessJobOptions.py
@@ -451,7 +451,9 @@ _parser = JobOptsParser()
 
 
 def _import_python(file):
-    execfile(file, {})
+    with open(file) as f:
+        code = compile(f.read(), file, 'exec')
+        exec(code, {})
 
 
 def _import_pickle(file):
diff --git a/GaudiKernel/python/GaudiKernel/PropertyProxy.py b/GaudiKernel/python/GaudiKernel/PropertyProxy.py
index dcae4ab212e88208459dda90807755824acce140..b034df6548a4690dba35fba9008c4165dfdb7a02 100644
--- a/GaudiKernel/python/GaudiKernel/PropertyProxy.py
+++ b/GaudiKernel/python/GaudiKernel/PropertyProxy.py
@@ -143,7 +143,7 @@ class PropertyProxy(object):
                     except AttributeError:
                      # value not yet set
                         pass
-            except ValueError, e:
+            except ValueError as e:
                 if allowcompat:
                     log.error('inconsistent value types for %s.%s (%s)' %
                               (obj.getName(), self.descr.__name__, str(e)))
@@ -183,7 +183,7 @@ class GaudiHandlePropertyProxyBase(PropertyProxy):
         PropertyProxy.__init__(self, descr, docString, default)
         self._handleType = handleType
         self._confTypeName = 'Configurable' + handleType.componentType
-#      print "%s: %r (%s)" % (self.__class__.__name__,self._handleType,self._confTypeName)
+#      print("%s: %r (%s)" % (self.__class__.__name__,self._handleType,self._confTypeName))
 
     def __get__(self, obj, type=None):
         try:
@@ -195,7 +195,7 @@ class GaudiHandlePropertyProxyBase(PropertyProxy):
                 default = self.convertDefaultToBeSet(obj, default)
                 if default:
                     self.__set__(obj, default)
-            except AttributeError, e:
+            except AttributeError as e:
                 # change type of exception to avoid false error message
                 raise RuntimeError(*e.args)
 
@@ -243,7 +243,7 @@ class GaudiHandlePropertyProxyBase(PropertyProxy):
         # turn string into handle
         isString = type(default) == str
         if not isString and self.isConfig(default):
-            #         print self.fullPropertyName(obj) + ": Setting default configurable: %r" % default
+            #         print(self.fullPropertyName(obj) + ": Setting default configurable: %r" % default)
             return default
         elif isString or self.isHandle(default):
             if isString:
@@ -259,8 +259,8 @@ class GaudiHandlePropertyProxyBase(PropertyProxy):
                 try:
                     conf = self.getDefaultConfigurable(
                         typeAndName, self.fullPropertyName(obj))
-#               print self.fullPropertyName(obj) + ": Setting default private configurable (from default handle): %r" % conf
-                except AttributeError, e:
+#               print(self.fullPropertyName(obj) + ": Setting default private configurable (from default handle): %r" % conf)
+                except AttributeError as e:
                     # change type of exception to avoid false error message
                     raise RuntimeError(*e.args)
                 if conf is None:
@@ -375,7 +375,7 @@ class DataObjectHandleBasePropertyProxy(PropertyProxy):
                 default = self.convertValueToBeSet(obj, default)
                 if default:
                     self.__set__(obj, default)
-            except AttributeError, e:
+            except AttributeError as e:
                 # change type of exception to avoid false error message
                 raise RuntimeError(*e.args)
 
@@ -403,7 +403,7 @@ class DataObjectHandleBasePropertyProxy(PropertyProxy):
 
 
 def PropertyProxyFactory(descr, doc, default):
-    #   print "PropertyProxyFactory( %s, %r )" % (descr.__name__,default)
+    #   print("PropertyProxyFactory( %s, %r )" % (descr.__name__,default))
 
     if isinstance(default, GaudiHandleArray):
         return GaudiHandleArrayPropertyProxy(descr, doc, default)
diff --git a/GaudiKernel/python/GaudiKernel/RootMap.py b/GaudiKernel/python/GaudiKernel/RootMap.py
index 56ec05667dadaee11df297a5bae74a3401833545..06bbe4d429c4e49562f6a3aa8e04c878a1c2093d 100644
--- a/GaudiKernel/python/GaudiKernel/RootMap.py
+++ b/GaudiKernel/python/GaudiKernel/RootMap.py
@@ -1,5 +1,5 @@
 #!/usr/bin/env python
-
+from __future__ import print_function
 import os
 import sys
 
@@ -39,7 +39,7 @@ def _procRootMap(rtmpfile, rtmapdict):
         if line:
             if line[0] != '#':
                 entry, lib = _getEntry(line)
-                if not rtmapdict.has_key(entry):
+                if entry not in rtmapdict:
                     rtmapdict[entry] = []
                 rtmapdict[entry].append(
                     (os.path.join(os.path.dirname(rtmpfile), lib), block))
@@ -48,7 +48,7 @@ def _procRootMap(rtmpfile, rtmapdict):
 
 
 def _procSysRootMap(rtmapdict):
-    if os.environ.has_key("ROOTSYS"):
+    if "ROOTSYS" in os.environ:
         rtmpfile = os.path.join(os.environ["ROOTSYS"], "etc", "system.rootmap")
         block = ""
         for line in open(rtmpfile, 'r'):
@@ -56,14 +56,14 @@ def _procSysRootMap(rtmapdict):
             if line:
                 if line[0] != '#':
                     entry, lib = _getEntry(line)
-                    if not rtmapdict.has_key(entry):
+                    if entry not in rtmapdict:
                         rtmapdict[entry] = []
                     rtmapdict[entry].append(
                         (os.path.join(os.environ["ROOTSYS"], "lib", lib), block))
                 else:
                     block = _getBlock(line)
     else:
-        print "WARNING: No ROOTSYS defined!"
+        print("WARNING: No ROOTSYS defined!")
 
 
 def _isRootMap(filename):
@@ -106,12 +106,12 @@ def printMaps(maps, recomp=None):
         kys = filter(recomp.search, kys)
     for k in kys:
         if len(maps[k]) > 1:
-            print "!!!!!!!!!!!! WARNING - More than one entry !!!!!!!!!!"
+            print("!!!!!!!!!!!! WARNING - More than one entry !!!!!!!!!!")
         for l in maps[k]:
-            print frmat % k,
+            print(frmat % k, end=' ')
             for m in l:
-                print m,
-            print " "
+                print(m, end=' ')
+            print(" ")
     return
 
 
@@ -122,13 +122,13 @@ def shortPrintMaps(maps, recomp=None):
         kys = filter(recomp.search, kys)
     for k in kys:
         if len(maps[k]) > 1:
-            print k, "!!!!!!!!!!!! WARNING - More than one entry !!!!!!!!!!"
+            print(k, "!!!!!!!!!!!! WARNING - More than one entry !!!!!!!!!!")
         else:
-            print k
+            print(k)
         for l in maps[k]:
             for m in l:
-                print "\t%s" % m,
-            print " "
+                print("\t%s" % m, end=' ')
+            print(" ")
     return
 
 
@@ -139,9 +139,9 @@ def printKeys(maps, recomp=None):
         kys = filter(recomp.search, kys)
     for k in kys:
         if len(maps[k]) > 1:
-            print "!!!!!!!!!!!! WARNING - More than one entry !!!!!!!!!!"
+            print("!!!!!!!!!!!! WARNING - More than one entry !!!!!!!!!!")
         for l in maps[k]:
-            print k
+            print(k)
     return
 
 
@@ -152,10 +152,10 @@ def checkDict(maps, recomp=None):
         kys = filter(recomp.search, kys)
     for k in kys:
         if len(maps[k]) > 1:
-            print "!!!!!!!!!!!! WARNING - More than one entry !!!!!!!!!!"
-            print k
+            print("!!!!!!!!!!!! WARNING - More than one entry !!!!!!!!!!")
+            print(k)
             for l in maps[k]:
                 for m in l:
-                    print "\t%s" % m,
-                print " "
+                    print("\t%s" % m, end=' ')
+                print(" ")
     return
diff --git a/GaudiKernel/scripts/gaudiComponentHelp.py b/GaudiKernel/scripts/gaudiComponentHelp.py
index abf7d6cacefa58924de9d5a2b6a84c8a86f065ff..5a92a9ac46af1f54adbbbd198f6f203318cd02cf 100644
--- a/GaudiKernel/scripts/gaudiComponentHelp.py
+++ b/GaudiKernel/scripts/gaudiComponentHelp.py
@@ -2,6 +2,8 @@
 """
 Print help messages for gaudi components
 """
+from __future__ import print_function
+
 from Gaudi import Configuration
 import Configurables
 import os
@@ -24,27 +26,27 @@ if __name__ == "__main__":
 
     cfgDb = Configuration.cfgDb
     if opts.list:
-        print "Available components:\n%s" % (21 * "=")
+        print("Available components:\n%s" % (21 * "="))
         for item in sorted(cfgDb):
-            print "  %s (from %s)" % (item, cfgDb[item]["lib"])
+            print("  %s (from %s)" % (item, cfgDb[item]["lib"]))
         sys.exit()
     elif opts.name:
         name = opts.name
         if name not in cfgDb:
-            print "Component %s not found." % (name)
+            print("Component %s not found." % (name))
             sys.exit()
-        print "\nDumping component information for %s:\n%s" % (
-            name, (35 + len(name)) * "=")
-        print "  Library: %s" % (cfgDb[name]["lib"])
-        print "  Package: %s" % (cfgDb[name]["package"])
-        print "\nProperties:\n%s" % (11 * "-")
+        print("\nDumping component information for %s:\n%s" % (
+            name, (35 + len(name)) * "="))
+        print("  Library: %s" % (cfgDb[name]["lib"]))
+        print("  Package: %s" % (cfgDb[name]["package"]))
+        print("\nProperties:\n%s" % (11 * "-"))
         try:
             properties = getattr(Configurables, name)(
             ).getPropertiesWithDescription()
         except AttributeError:
-            print "  Not a configurable component. No properties to show."
+            print("  Not a configurable component. No properties to show.")
             sys.exit()
         for label, (value, desc) in sorted(properties.iteritems()):
-            print ("  %s\t : %s\t (%s) " % (label, value, str(
-                desc).replace("None", " no description "))).expandtabs(30)
+            print(("  %s\t : %s\t (%s) " % (label, value, str(
+                desc).replace("None", " no description "))).expandtabs(30))
         sys.exit()
diff --git a/GaudiKernel/scripts/genconfuser.py b/GaudiKernel/scripts/genconfuser.py
index b823474e9e50d18cf6fc03a529489426a6091340..8f2f4bf5931bb6a70ee68d6545c3b765caddb85a 100755
--- a/GaudiKernel/scripts/genconfuser.py
+++ b/GaudiKernel/scripts/genconfuser.py
@@ -3,7 +3,7 @@
 """
 Generate _confDb.py files for ConfigurableUser classes.
 """
-
+from __future__ import division
 import os
 import sys
 import time
@@ -14,10 +14,10 @@ from pprint import pformat
 from glob import glob
 from GaudiKernel.ConfigurableDb import cfgDb
 
-logging.VERBOSE = (logging.INFO + logging.DEBUG) / 2
+logging.VERBOSE = (logging.INFO + logging.DEBUG) // 2
 logging.addLevelName(logging.VERBOSE, "VERBOSE")
 logging.verbose = lambda msg, *args, **kwargs: \
-    apply(logging.log, (logging.VERBOSE, msg) + args, kwargs)
+    logging.log(logging.VERBOSE, msg, *args, **kwargs)
 
 
 def _inheritsfrom(derived, basenames):
@@ -28,6 +28,11 @@ def _inheritsfrom(derived, basenames):
 
     'basenames' can be a string or an iterable (of strings).
     """
+    try:
+        basestring
+    except NameError:
+        # Fall back to assuming `str` is the base class in Python 3
+        basestring = str
     if isinstance(basenames, basestring):
         basenames = (basenames,)
     for b in derived.__bases__:
@@ -62,7 +67,7 @@ def loadConfigurableDb():
         log.debug("\t-loading [%s]..." % confDb)
         try:
             cfgDb._loadModule(confDb)
-        except Exception, err:
+        except Exception as err:
             # It may happen that the file is found but not completely
             # written, usually during parallel builds, but we do not care.
             log.warning("Could not load file [%s] !", confDb)
@@ -105,7 +110,7 @@ def getConfigurableUsers(modulename, root, mayNotExist=False):
     g, l = {}, {}
     try:
         logging.verbose("importing %s", shortmodname)
-        exec "import %s as mod" % shortmodname in g, l
+        exec("import %s as mod" % shortmodname, g, l)
     finally:
         # restore old sys.path
         logging.verbose("restoring old sys.path")
@@ -253,8 +258,8 @@ def main():
     output_dir = os.path.dirname(outputfile)
     try:
         logging.info("Creating directory %r", output_dir)
-        os.makedirs(output_dir, 0755)
-    except OSError, err:
+        os.makedirs(output_dir, 0o755)
+    except OSError as err:
         import errno
         if err.errno == errno.EEXIST:
             # somebody already - perhaps concurrently - created that dir.
diff --git a/GaudiKernel/scripts/make_patch.py b/GaudiKernel/scripts/make_patch.py
index 441f64b99a67d9f030582bf7f76a44fd25d5cb2b..3a29dda57fff2b074da695b7b1d7a061969883bd 100755
--- a/GaudiKernel/scripts/make_patch.py
+++ b/GaudiKernel/scripts/make_patch.py
@@ -16,13 +16,13 @@ def command(cmd, *args, **kwargs):
     d.update(kwargs)
     cmd = [cmd] + list(args)
     logging.debug("Execute command: %r %r", " ".join(cmd), kwargs)
-    proc = apply(Popen, (cmd,), d)
+    proc = Popen(cmd, **d)
     return proc.communicate()
 
 
-cmt = lambda *args, **kwargs: apply(command, ("cmt",) + args, kwargs)
-cvs = lambda *args, **kwargs: apply(command, ("cvs",) + args, kwargs)
-svn = lambda *args, **kwargs: apply(command, ("svn",) + args, kwargs)
+cmt = lambda *args, **kwargs: command("cmt", *args, **kwargs)
+cvs = lambda *args, **kwargs: command("cvs", *args, **kwargs)
+svn = lambda *args, **kwargs: command("svn", *args, **kwargs)
 
 
 def broadcast_packages():
diff --git a/GaudiKernel/scripts/root_map_dump.py b/GaudiKernel/scripts/root_map_dump.py
index c441f98c5c061be0416a570d0cb8731bb4d6eca5..6d1d24e42bd2f8c67471f4e82594ae8a46f1ca71 100755
--- a/GaudiKernel/scripts/root_map_dump.py
+++ b/GaudiKernel/scripts/root_map_dump.py
@@ -1,5 +1,5 @@
 #!/usr/bin/env python
-
+from __future__ import print_function
 import os.path
 from GaudiKernel.RootMap import getMaps, checkDict
 from GaudiKernel.RootMap import printMaps, shortPrintMaps
@@ -7,7 +7,7 @@ from GaudiKernel.RootMap import printKeys
 
 
 def _help(argv):
-    print """
+    print("""
   %s [-h] [-s|-e|-c] [-r] [-p pattern] [listofpath]
   
      -h: dump this Help
@@ -23,7 +23,7 @@ def _help(argv):
   rootmap file(s) on separate lines. The output is sorted according to
   the name of the entry. A Warning is issued if an entry appears
   several times.
-  """ % os.path.basename(argv[0])
+  """ % os.path.basename(argv[0]))
 
 
 if __name__ == "__main__":
diff --git a/GaudiKernel/src/Util/genconf.cpp b/GaudiKernel/src/Util/genconf.cpp
index a33d8f271d627ceac4d9453dd4fa8e7ba52e4976..c71d2cc875e8903409594b2aa68746155341d63b 100644
--- a/GaudiKernel/src/Util/genconf.cpp
+++ b/GaudiKernel/src/Util/genconf.cpp
@@ -657,7 +657,12 @@ void configGenerator::genHeader( std::ostream& py, std::ostream& db )
   // python file part
   std::string now = Gaudi::Time::current().format( true );
   py << "#" << now //<< "\n"
-     << "\"\"\"Automatically generated. DO NOT EDIT please\"\"\"\n";
+     << "\"\"\"Automatically generated. DO NOT EDIT please\"\"\"\n"
+     << "import sys\n"
+     << "if sys.version_info >= (3,):\n"
+     << "    # Python 2 compatibility\n"
+     << "    long = int\n";
+
   if ( m_importGaudiHandles ) {
     py << "from GaudiKernel.GaudiHandles import *\n";
   }
@@ -761,7 +766,7 @@ void configGenerator::pythonizeValue( const PropertyBase* p, string& pvalue, str
     pvalue = cvalue;
     ptype  = "int";
   } else if ( ti == typeIndex<long long>() || ti == typeIndex<unsigned long long>() ) {
-    pvalue = cvalue + "L";
+    pvalue = "long(" + cvalue + ")";
     ptype  = "long";
   } else if ( ti == typeIndex<float>() || ti == typeIndex<double>() ) {
     // forces python to handle this as a float: put a dot in there...
diff --git a/GaudiKernel/tests/nose/test_Configurables.py b/GaudiKernel/tests/nose/test_Configurables.py
index 8b132d7291a58d8775f2364eb35fe9f83a96ae47..5b4e2cff7818ceb0708f0739275d8c1f9bd24634 100644
--- a/GaudiKernel/tests/nose/test_Configurables.py
+++ b/GaudiKernel/tests/nose/test_Configurables.py
@@ -49,7 +49,7 @@ def test_invalid_value():
         raise
     except ValueError:
         pass
-    except Exception, x:
+    except Exception as x:
         assert False, 'ValueError exception expected, got %s' % type(
             x).__name__
 
@@ -60,7 +60,7 @@ def test_invalid_value():
         raise
     except ValueError:
         pass
-    except Exception, x:
+    except Exception as x:
         assert False, 'ValueError exception expected, got %s' % type(
             x).__name__
 
@@ -85,7 +85,7 @@ def test_invalid_key():
         raise
     except AttributeError:
         pass
-    except Exception, x:
+    except Exception as x:
         assert False, 'AttributeError exception expected, got %s' % type(
             x).__name__
 
diff --git a/GaudiKernel/tests/nose/test_instructionsetLevel.py b/GaudiKernel/tests/nose/test_instructionsetLevel.py
index df910a646b3c55e74f542f372bb19992b464ef03..4857b1b5276990ae022443b2410d7ac9bb72bc38 100644
--- a/GaudiKernel/tests/nose/test_instructionsetLevel.py
+++ b/GaudiKernel/tests/nose/test_instructionsetLevel.py
@@ -5,6 +5,7 @@ from subprocess import Popen, PIPE
 def test():
     out = Popen(['instructionsetLevel.exe', 'all'],
                 stdout=PIPE).communicate()[0]
+    out = out.decode('utf-8')
     known_flags = set(l.strip() for l in out.splitlines())
 
     expected = set()
@@ -14,6 +15,7 @@ def test():
             break
 
     out = Popen(['instructionsetLevel.exe'], stdout=PIPE).communicate()[0]
+    out = out.decode('utf-8')
     found = set(l.strip() for l in out.splitlines())
     # FIXME: these seem not to be reported by Linux
     found -= set(['sse3', 'avx512f'])
diff --git a/GaudiKernel/tests/qmtest/scripts/test_bug_87340.py b/GaudiKernel/tests/qmtest/scripts/test_bug_87340.py
index 03f04b47837183c32ae8e727974fd25a1c0e3408..e1625159d515690411c8b37743307e62b7a48ed0 100755
--- a/GaudiKernel/tests/qmtest/scripts/test_bug_87340.py
+++ b/GaudiKernel/tests/qmtest/scripts/test_bug_87340.py
@@ -4,6 +4,7 @@ Test that the there is no signature for MsgStream operator<< <char []> in
 GaudiKernel.
 See https://savannah.cern.ch/bugs?87340
 """
+from __future__ import print_function
 import os
 import sys
 import re
@@ -21,29 +22,30 @@ varname = 'LD_LIBRARY_PATH'
 searchpath.extend(os.environ.get(varname, "").split(os.pathsep))
 
 try:
-    lib = (p
+    lib = next(p
            for p in (os.path.join(n, libname)
                      for n in searchpath)
-           if os.path.exists(p)).next()
+           if os.path.exists(p))
 except StopIteration:
-    print >> sys.stderr, 'FAILURE: Cannot find', repr(
-        libname), 'in', searchpath
+    print('FAILURE: Cannot find', repr(libname), 'in', searchpath,
+          file=sys.stderr)
     sys.exit(2)
 
 nm = Popen(["nm", '-C', lib], stdout=PIPE)
 output, _ = nm.communicate()
+output = output.decode('utf-8')
 
 if nm.returncode:
-    print output
-    print >> sys.stderr, 'FAILURE: nm call failed'
+    print(output)
+    print('FAILURE: nm call failed', file=sys.stderr)
     sys.exit(nm.returncode)
 
 signature = re.compile(r"MsgStream&amp; operator&lt;&lt; &lt;char \[\d+\]&gt;")
 
-lines = filter(signature.search, output.splitlines())
+lines = list(filter(signature.search, output.splitlines()))
 if lines:
-    print "\n".join(lines)
-    print >> sys.stderr, "FAILURE: found MsgStream operator<< specialization"
+    print("\n".join(lines))
+    print("FAILURE: found MsgStream operator<< specialization", file=sys.stderr)
     sys.exit(1)
 else:
-    print "SUCCESS: no MsgStream operator<< specialization found"
+    print("SUCCESS: no MsgStream operator<< specialization found")
diff --git a/GaudiMP/python/GaudiMP/GMPBase.py b/GaudiMP/python/GaudiMP/GMPBase.py
index 58295473bce454bf080f87e3c4e0cf7a5ae1dadd..9a692bbb67bce31aa02162f6203831d7f5f0b193 100644
--- a/GaudiMP/python/GaudiMP/GMPBase.py
+++ b/GaudiMP/python/GaudiMP/GMPBase.py
@@ -1,3 +1,4 @@
+from __future__ import print_function
 from Gaudi.Configuration import *
 from GaudiPython import AppMgr, gbl, setOwnership, PyAlgorithm, SUCCESS, FAILURE, InterfaceCast
 from ROOT import TBufferFile, TBuffer
@@ -215,7 +216,7 @@ class CollectHistograms(PyAlgorithm):
         # send 100 at a time
         chunk = 100
         reps = len(ks) / chunk + 1
-        for i in xrange(reps):
+        for i in range(reps):
             someKeys = ks[i * chunk: (i + 1) * chunk]
             smalld = dict([(key, self._gmpc.hDict[key]) for key in someKeys])
             self._gmpc.hq.put((self._gmpc.nodeID, smalld))
@@ -307,7 +308,7 @@ class EventCommunicator(object):
         elif self._gmpc.nodeType == 'Worker':
             downstream = 1
 
-        for i in xrange(downstream):
+        for i in range(downstream):
             self.qout.put('FINISHED')
         if self._gmpc.nodeType != 'Writer':
             self.qout.join()
@@ -623,7 +624,7 @@ class GMPComponent(object):
                     objects += 1
                 histDict[n] = o
         else:
-            print 'WARNING : no histograms to recover?'
+            print('WARNING : no histograms to recover?')
         return histDict
 
     def Initialize(self):
@@ -687,9 +688,9 @@ class Reader(GMPComponent):
 
     def DumpEvent(self):
         tb = TBufferFile(TBuffer.kWrite)
-        # print '----Reader dumping Buffer!!!'
+        # print('----Reader dumping Buffer!!!')
         self.ts.dumpBuffer(tb)
-        # print '\tBuffer Dumped, size : %i'%( tb.Length() )
+        # print('\tBuffer Dumped, size : %i'%( tb.Length() ))
         return tb
 
     def DoFirstEvent(self):
@@ -1468,11 +1469,11 @@ class Coord(object):
         # one queue from Reader-Workers
         rwk = JoinableQueue()
         # one queue from each worker to writer
-        workersWriter = [JoinableQueue() for i in xrange(self.nWorkers)]
+        workersWriter = [JoinableQueue() for i in range(self.nWorkers)]
         d = {}
         d[-1] = (None, rwk)              # Reader
         d[-2] = (workersWriter, None)    # Writer
-        for i in xrange(self.nWorkers):
+        for i in range(self.nWorkers):
             d[i] = (rwk, workersWriter[i])
         return d
 
diff --git a/GaudiMP/python/GaudiMP/Parallel.py b/GaudiMP/python/GaudiMP/Parallel.py
index 865501a37a01abfce4b99ea33ed49c586b026e60..08682236c230e6816ad5489f6ea0047f18d04a47 100644
--- a/GaudiMP/python/GaudiMP/Parallel.py
+++ b/GaudiMP/python/GaudiMP/Parallel.py
@@ -1,5 +1,6 @@
 # File: GaudiMP/Parallel.py
 # Author: Pere Mato (pere.mato@cern.ch)
+from __future__ import print_function
 
 """ GaudiMP.Parallel module.
     This module provides 'parallel' processing support for GaudiPyhton.
@@ -181,7 +182,7 @@ class WorkManager(object):
                 self._mergeStatistics(stat)
             end = time.time()
             self._printStatistics()
-            print 'Time elapsed since server creation %f' % (end - start)
+            print('Time elapsed since server creation %f' % (end - start))
         # --- Call the Local Finalize
         task.finalize()
 
@@ -189,11 +190,11 @@ class WorkManager(object):
         njobs = 0
         for stat in self.stats.values():
             njobs += stat.njob
-        print 'Job execution statistics:'
-        print 'job count | % of all jobs | job time sum | time per job | job server'
+        print('Job execution statistics:')
+        print('job count | % of all jobs | job time sum | time per job | job server')
         for name, stat in self.stats.items():
-            print '       %d |        %6.2f |     %8.3f |    %8.3f | %s' % (
-                stat.njob, 100. * stat.njob / njobs, stat.time, stat.time / stat.njob, name)
+            print('       %d |        %6.2f |     %8.3f |    %8.3f | %s' % (
+                stat.njob, 100. * stat.njob / njobs, stat.time, stat.time / stat.njob, name))
 
     def _mergeStatistics(self, stat):
         if stat.name not in self.stats:
@@ -225,8 +226,8 @@ class SshSession(object):
                            (sys.executable, ppprefix, sys.version.split()[0]))
         self.session.read_lazy()
         self.session.read_lazy()
-        print 'started ppserver in ', hostname
+        print('started ppserver in ', hostname)
 
     def __del__(self):
         self.session.close()
-        print 'killed ppserver in ', self.host
+        print('killed ppserver in ', self.host)
diff --git a/GaudiMP/python/GaudiMP/pTools.py b/GaudiMP/python/GaudiMP/pTools.py
index 497dcd479aa9376da182e07fc154c7ec40648ee9..78ee9a450a3b94af29766ba472591d3244463c05 100644
--- a/GaudiMP/python/GaudiMP/pTools.py
+++ b/GaudiMP/python/GaudiMP/pTools.py
@@ -379,7 +379,7 @@ class FileRecordsAgent():
             cob = ob2.containedObjects()[0]
             min = cob.earliest()
             max = cob.latest()
-            for j in xrange(sz):
+            for j in range(sz):
                 cob = ob.containedObjects()[j]
                 self.log.debug('Adding TimeSpanFSR')
                 if cob.earliest() < min:
@@ -408,7 +408,7 @@ class FileRecordsAgent():
         baseLumi = LumiFSR(l)
         # Now deal with the argument Non-empty Keyed Container of LumiFSRs
         nCont = keyedC.numberOfObjects()
-        for i in xrange(nCont):
+        for i in range(nCont):
             obj = keyedC.containedObject(i)
             nextLumi = LumiFSR(obj)
             baseLumi.merge(nextLumi)
@@ -594,7 +594,7 @@ class Syncer(object):
         self.d = {}
         self.manyEvents = manyEvents
 
-        for i in xrange(-2, nWorkers):
+        for i in range(-2, nWorkers):
             self.d[i] = SyncMini(Event(), lastEvent=Event())
             if self.manyEvents:
                 self.limitFirst = firstEvent
@@ -612,7 +612,7 @@ class Syncer(object):
             return sc
 
         # Regular version ----------------------------
-        for i in xrange(0, self.limit, self.step):
+        for i in range(0, self.limit, self.step):
             if self.checkAll():
                 self.log.info('%s : All procs done @ %i s' % (step, i))
                 break
diff --git a/GaudiMP/scripts/compareOutputFiles.py b/GaudiMP/scripts/compareOutputFiles.py
index 59a4c62ceceed155a23a7f730f68599f8976fe41..d5321c07aecf436dbed1730a7676ec399accef65 100755
--- a/GaudiMP/scripts/compareOutputFiles.py
+++ b/GaudiMP/scripts/compareOutputFiles.py
@@ -1,4 +1,4 @@
-
+from __future__ import print_function
 from Gaudi.Configuration import *
 from GaudiPython import AppMgr, gbl
 from ROOT import TFile, TBufferFile, TBuffer
@@ -71,12 +71,12 @@ def CompareTrees(pname, sname):
     if pMeta == sMeta:
         pass
     else:
-        print 'Meta Data differs'
+        print('Meta Data differs')
 
     if pEvent == sEvent:
         pass
     else:
-        print 'Event data differs'
+        print('Event data differs')
 
     if pOther != sOther:
         pset = set(pOther)
@@ -84,11 +84,11 @@ def CompareTrees(pname, sname):
         pExtra = pset - sset
         sExtra = sset - pset
         if pExtra:
-            print 'Extra Data in parallel file : ', pExtra
+            print('Extra Data in parallel file : ', pExtra)
         if sExtra:
-            print 'Extra Data in serial   file : ', sExtra
+            print('Extra Data in serial   file : ', sExtra)
         if sExtra or pExtra:
-            print 'Files will have different sizes'
+            print('Files will have different sizes')
     pf.Close()
     sf.Close()
 
@@ -101,9 +101,9 @@ def switchDict(d):
     nvals = len(vals)
     for v in vals:
         if vals.count(v) > 1:
-            print 'Dictionary cannot be switched, values not unique'
+            print('Dictionary cannot be switched, values not unique')
             return None
-    print 'Dict has keys/values : %i/%i' % (nkeys, nvals)
+    print('Dict has keys/values : %i/%i' % (nkeys, nvals))
     pairs = d.items()  # returns (key, val) tuples in a list
     newd = {}
     for k, entry in pairs:
@@ -119,11 +119,11 @@ def printDict(d, name='unspecified'):
     #   key     value
     #   ...
     #
-    print '-' * 80
-    print 'Dictionary %s : ' % (name)
+    print('-' * 80)
+    print('Dictionary %s : ' % (name))
     for k in iter(d.keys()):
-        print '\t', k, '\t', d[k]
-    print '-' * 80
+        print('\t', k, '\t', d[k])
+    print('-' * 80)
 
 
 def Reader(readerType, filename, qacross, qToEngine):
@@ -172,7 +172,7 @@ def Reader(readerType, filename, qacross, qToEngine):
             pass
         lsks = len(serOrder.keys())
         lpks = len(order.keys())
-        print 'Events in Files (serial/parallel) : %i / %i' % (lsks, lpks)
+        print('Events in Files (serial/parallel) : %i / %i' % (lsks, lpks))
 
     # now run files in the order specified by the serial ordering
     # and send them one by one to the comparison engine
@@ -188,7 +188,7 @@ def Reader(readerType, filename, qacross, qToEngine):
             [(l, (evt[l].__class__.__name__, evt[l].__repr__())) for l in lst])
         qToEngine.put(ascii)
     qToEngine.put(None)
-    print '%s Reader Finished' % (readerType)
+    print('%s Reader Finished' % (readerType))
 
 
 def ComparisonEngine(pQueue, sQueue):
@@ -205,22 +205,22 @@ def ComparisonEngine(pQueue, sQueue):
         pitem = pQueue.get()
         sitem = sQueue.get()
         if pitem == sitem == None:
-            print 'Termination Signals received ok'
+            print('Termination Signals received ok')
             break
         elif pitem == None:
-            print 'pitem != sitem : ', pitem, sitem
+            print('pitem != sitem : ', pitem, sitem)
             break
         elif sitem == None:
-            print 'pitem != sitem : ', pitem, sitem
+            print('pitem != sitem : ', pitem, sitem)
             break
         results.append(compareEvents(pitem, sitem))
-    print '=' * 80
-    print 'Comparison Engine Finished'
-    print '-' * 80
-    print 'Total Events Checked : %i' % (len(results))
-    print 'Perfect Matches      : %i' % (sum(results))
-    print 'Errors               : %i' % (len(results) - sum(results))
-    print '=' * 80
+    print('=' * 80)
+    print('Comparison Engine Finished')
+    print('-' * 80)
+    print('Total Events Checked : %i' % (len(results)))
+    print('Perfect Matches      : %i' % (sum(results)))
+    print('Errors               : %i' % (len(results) - sum(results)))
+    print('=' * 80)
 
 
 def checkForAddressDifference(a, b):
@@ -264,7 +264,7 @@ def compareEvents(s, p):
             if p[e][0] == 'DataObject':
                 pks.remove(e)
             else:
-                print 'Extra Other thing found!', e, p[e][0]
+                print('Extra Other thing found!', e, p[e][0])
                 return False
 
     # check 2 : same paths?
@@ -276,7 +276,7 @@ def compareEvents(s, p):
     # check 3 : check the content
     l = len(sks)
     diffs = []
-    for i in xrange(l):
+    for i in range(l):
         key = sks[i]
         # compare class name
         if s[key][0] == p[key][0]:
@@ -300,7 +300,7 @@ def compareEvents(s, p):
 
 def CheckFileRecords(par, ser):
 
-    print "Checking File Records"
+    print("Checking File Records")
 
     parFSR = GetFSRdicts(par)
     serFSR = GetFSRdicts(ser)
@@ -310,15 +310,15 @@ def CheckFileRecords(par, ser):
     diff2 = set(parFSR["EventCountFSR"].iteritems()) - \
         set(serFSR["EventCountFSR"].iteritems())
 
-    print "\nDifferent entries in TimeSpanFSR:  \t" + \
-        str(len(diff1)) + "\nDifferent entries in EventCountFSR:\t" + str(len(diff2))
+    print("\nDifferent entries in TimeSpanFSR:  \t" + \
+        str(len(diff1)) + "\nDifferent entries in EventCountFSR:\t" + str(len(diff2)))
 
     for k in ["LumiFSRBeamCrossing", "LumiFSRBeam2", "LumiFSRNoBeam"]:
         diff3 = set(parFSR[k]["key"]) - set(serFSR[k]["key"])
         diff4 = set(parFSR[k]["incr"]) - set(serFSR[k]["incr"])
         diff5 = set(parFSR[k]["integral"]) - set(serFSR[k]["integral"])
-        print "Different entries in " + str(k) + ": \tkey: " + str(
-            len(diff3)) + " increment: " + str(len(diff4)) + " integral: " + str(len(diff5))
+        print("Different entries in " + str(k) + ": \tkey: " + str(
+            len(diff3)) + " increment: " + str(len(diff4)) + " integral: " + str(len(diff5)))
 
 
 def LumiFSR(lumi):
@@ -362,7 +362,7 @@ def GetFSRdict(filename, queue):
     options += "LumiFsrReader().Persistency='ROOT'; LumiFsrReader().EvtMax = 1; from Configurables import LHCbApp; LHCbApp().Persistency='ROOT';  from Configurables import CondDB, DDDBConf;"
     options += " CondDB().UseLatestTags=['%s']; DDDBConf(DataType='%s');" % (
         2011, 2011)
-    exec options
+    exec(options)
     app = AppMgr()
     app.run(1)
     fsr = app.filerecordsvc()
@@ -409,25 +409,25 @@ def CompareFSR(pout, sout):
     parFSR = pout.get()
     serFSR = sout.get()
 
-    print "Comparing File Records"
+    print("Comparing File Records")
 
     diff1 = set(parFSR["TimeSpanFSR"].iteritems()) - \
         set(serFSR["TimeSpanFSR"].iteritems())
     diff2 = set(parFSR["EventCountFSR"].iteritems()) - \
         set(serFSR["EventCountFSR"].iteritems())
 
-    print "\nDifferent entries in TimeSpanFSR:  \t" + \
-        str(len(diff1)) + "\nDifferent entries in EventCountFSR:\t" + str(len(diff2))
+    print("\nDifferent entries in TimeSpanFSR:  \t" + \
+        str(len(diff1)) + "\nDifferent entries in EventCountFSR:\t" + str(len(diff2)))
 
     for k in ["LumiFSRBeamCrossing", "LumiFSRBeam2", "LumiFSRNoBeam"]:
         diff3 = set(parFSR[k]['key']) - set(serFSR[k]['key'])
         diff4 = set(parFSR[k]['incr']) - set(serFSR[k]['incr'])
         diff5 = set(parFSR[k]['integral']) - set(serFSR[k]["integral"])
-        print "Different entries in " + str(k) + ": \tkey: " + str(
-            len(diff3)) + " increment: " + str(len(diff4)) + " integral: " + str(len(diff5))
+        print("Different entries in " + str(k) + ": \tkey: " + str(
+            len(diff3)) + " increment: " + str(len(diff4)) + " integral: " + str(len(diff5)))
 
-    print "\nParallel: \n" + str(parFSR)
-    print "\nSerial: \n" + str(serFSR)
+    print("\nParallel: \n" + str(parFSR))
+    print("\nSerial: \n" + str(serFSR))
 
 
 if __name__ == '__main__':
@@ -435,13 +435,13 @@ if __name__ == '__main__':
     args = sys.argv
     args.pop(0)  # get rid of script name
     if len(args) != 2:
-        print 'Please supply two arguments : > python loadFile <parallelFile> <serialFile>'
+        print('Please supply two arguments : > python loadFile <parallelFile> <serialFile>')
         sys.exit(0)
     else:
         par = 'PFN:' + args[0]
         ser = 'PFN:' + args[1]
-        print 'Parallel File to be analysed : %s' % (par)
-        print 'Serial   File to be analysed : %s' % (ser)
+        print('Parallel File to be analysed : %s' % (par))
+        print('Serial   File to be analysed : %s' % (ser))
 
     pname = par[4:]  # TFile doesn't need the "PFN:" prefix
     sname = ser[4:]
@@ -459,7 +459,7 @@ if __name__ == '__main__':
 
     #CompareTrees( pname, sname )
 
-    print "Check File Records"
+    print("Check File Records")
 
     ser = sys.argv[0]
     par = sys.argv[1]
diff --git a/GaudiMP/scripts/compareRootHistos.py b/GaudiMP/scripts/compareRootHistos.py
index 2af9b4ed617d895c18edca7d8ee3fdd82e014097..aa5afaf3918c2466a33a6373323ca0f95b5ff1e6 100755
--- a/GaudiMP/scripts/compareRootHistos.py
+++ b/GaudiMP/scripts/compareRootHistos.py
@@ -1,4 +1,5 @@
 #! /usr/bin/env python
+from __future__ import print_function
 from optparse import OptionParser
 import re
 import sys
@@ -88,7 +89,7 @@ def comparePaths(t1, t2):
         ds = t2[1]
         dp = t1[1]
     else:
-        print 'Neither tuple is Reference Root file reference?'
+        print('Neither tuple is Reference Root file reference?')
         return
 
     dsks = ds.keys()
@@ -100,42 +101,42 @@ def comparePaths(t1, t2):
     pset = set(dpks)
     os, hs = composition((ref, ds))
     op, hp = composition((test, dp))
-    print '\n' + '=' * 80
-    print 'Comparison of Paths : Reference vs Test ROOT files'
-    print '-' * 80
-    print 'Number of paths in Reference file : %i (objects, histos) = ( %i, %i )' % (
-        len(dsks), os, hs)
-    print 'Number of paths in Test file : %i (objects, histos) = ( %i, %i )' % (
-        len(dpks), op, hp)
+    print('\n' + '=' * 80)
+    print('Comparison of Paths : Reference vs Test ROOT files')
+    print('-' * 80)
+    print('Number of paths in Reference file : %i (objects, histos) = ( %i, %i )' % (
+        len(dsks), os, hs))
+    print('Number of paths in Test file : %i (objects, histos) = ( %i, %i )' % (
+        len(dpks), op, hp))
     matching = sset.intersection(pset)
     matchingHistos = 0
     for n in matching:
         if ds[n].__class__.__name__ in histos:
             matchingHistos += 1
-    print '\nMatching paths                 : %i' % (len(matching))
+    print('\nMatching paths                 : %i' % (len(matching)))
     uSer = sset - pset
     # work out histos unique to test file
     uniqueReferenceHistos = 0
     for n in uSer:
         if ds[n].__class__.__name__ in histos:
             uniqueReferenceHistos += 1
-    print 'Paths unique to Reference file : %i ( %i Histos )' % (
-        len(uSer), uniqueReferenceHistos)
+    print('Paths unique to Reference file : %i ( %i Histos )' % (
+        len(uSer), uniqueReferenceHistos))
     if uSer:
         for n in uSer:
-            print '\t%s : \t%s' % (ds[n], n)
+            print('\t%s : \t%s' % (ds[n], n))
     uPar = pset - sset
     uniqueTestHistos = 0
     for n in uPar:
         if dp[n].__class__.__name__ in histos:
             uniqueTestHistos += 1
-    print 'Paths unique to Test file : %i ( %i Histos )' % (
-        len(uPar), uniqueTestHistos)
+    print('Paths unique to Test file : %i ( %i Histos )' % (
+        len(uPar), uniqueTestHistos))
     if uPar:
         for n in uPar:
-            print '\t%s : \t%s' % (dp[n], n)
-    print 'Matching Histos to test : %i' % (matchingHistos)
-    print '=' * 80 + '\n'
+            print('\t%s : \t%s' % (dp[n], n))
+    print('Matching Histos to test : %i' % (matchingHistos)
+    print('=' * 80 + '\n')
     return (((os, hs), (op, hp)), (uSer, uniqueReferenceHistos), (uPar, uniqueTestHistos), matchingHistos)
 # =============================================================================
 
@@ -158,7 +159,7 @@ def bin2binIdentity(h1, h2):
 
     nbins = getNbins(h1)
     diffbins = 0
-    for ibin in xrange(0, nbins):
+    for ibin in range(0, nbins):
         h1bin = h1.GetBinContent(ibin)
         h2bin = h2.GetBinContent(ibin)
         diffbins += (h1bin != h2bin)
@@ -194,7 +195,7 @@ def compareHistos(t1, t2, state, checkBin2BinIdentity):
         ds = t2[1]
         dp = t1[1]
     else:
-        print 'Neither tuple is Reference Root file reference?'
+        print('Neither tuple is Reference Root file reference?')
         return
 
     # histocount, objectcount for test/reference
@@ -252,9 +253,9 @@ def compareHistos(t1, t2, state, checkBin2BinIdentity):
             # check for (non-zero sum of bin error) && (non-zero integrals) for K-Test
             sBinError = 0.0
             pBinError = 0.0
-            for i in xrange(sh.GetNbinsX()):
+            for i in range(sh.GetNbinsX()):
                 sBinError += sh.GetBinError(i)
-            for i in xrange(ph.GetNbinsX()):
+            for i in range(ph.GetNbinsX()):
                 pBinError += ph.GetBinError(i)
             sint = sh.Integral()
             pint = ph.Integral()
@@ -275,7 +276,7 @@ def compareHistos(t1, t2, state, checkBin2BinIdentity):
                 if int(kTest):
                     passedKol += 1
                 else:
-                    # ; print 'KTest result : ', kTest
+                    # ; print('KTest result : ', kTest)
                     failedKol += 1
                     diffKols.append(h)
             else:
@@ -290,95 +291,95 @@ def compareHistos(t1, t2, state, checkBin2BinIdentity):
                     xIntegrals += 1
         else:
             notfound += 1
-            print 'not found? ', h
+            print('not found? ', h)
 
     # report on Failed Entry-Checks
-    print '\n\n' + '-' * 80
-    print 'Summary of histos with different Entries'
-    print '-' * 80
+    print('\n\n' + '-' * 80)
+    print('Summary of histos with different Entries')
+    print('-' * 80)
     if diffEntries:
         diffEntries.sort()
         for e in diffEntries:
-            print '\t\t\t%s:\t%i != %i' % (
-                e, int(ds[e].GetEntries()), int(dp[e].GetEntries()))
-    print '-' * 80
+            print('\t\t\t%s:\t%i != %i' % (
+                e, int(ds[e].GetEntries()), int(dp[e].GetEntries())))
+    print('-' * 80)
 
     # report on Failed Kolmogorov Tests
-    print '\n\n' + '-' * 60
-    print 'Summary of histos which failed Kolmogorov Test'
-    print '-' * 60
+    print('\n\n' + '-' * 60)
+    print('Summary of histos which failed Kolmogorov Test')
+    print('-' * 60)
     if diffKols:
         diffKols.sort()
         for e in diffKols:
             result = kTestResults[e]  # DP Calculated twice ARGH!!
-            print '%s\t\t%s :\tK-Test Result :\t %5.16f' % (
-                ds[e].ClassName(), e, result)
-    print '-' * 60
+            print('%s\t\t%s :\tK-Test Result :\t %5.16f' % (
+                ds[e].ClassName(), e, result))
+    print('-' * 60)
 
     # report on Failed Integral Checks
-    print '\n\n' + '-' * 60
-    print 'Summary of histos which failed Integral Check'
-    print '-' * 60
+    print('\n\n' + '-' * 60)
+    print('Summary of histos which failed Integral Check')
+    print('-' * 60)
     if diffIntegrals:
         diffIntegrals.sort()
         for e in diffIntegrals:
             diff = dp[e].Integral() - ds[e].Integral()
             pc = (diff * 100) / ds[e].Integral()
-            print '%s\t\t%s:\t Diff = %5.6f\tPercent Diff to Reference : %5.6f ' % (
-                ds[e].ClassName(), e, diff, pc)
-    print '-' * 60 + '\n'
-    print '=' * 80 + '\n'
+            print('%s\t\t%s:\t Diff = %5.6f\tPercent Diff to Reference : %5.6f ' % (
+                ds[e].ClassName(), e, diff, pc))
+    print('-' * 60 + '\n')
+    print('=' * 80 + '\n')
 
     # Report on failed bin2bin identity
     if checkBin2BinIdentity:
         # report on b2b checks
-        print '\n\n' + '-' * 80
-        print 'Summary of histos with at least one bin with different Entries'
-        print '-' * 80
+        print('\n\n' + '-' * 80)
+        print('Summary of histos with at least one bin with different Entries')
+        print('-' * 80)
         if diffIdentity:
             diffIdentity.sort()
             for e in diffIdentity:
-                print '%s\t\t%s: %i different bins' % (
-                    ds[e].ClassName(), e, identityDiffBins[e])
-            print '-' * 80
-
-    print '\n' + '=' * 80
-    print 'Comparison : Reference/Test ROOT Histo files'
-    print '\n\t\tReference\tTest'
-    print '\tObjects : %i\t%i\t\t( p-s = %i )' % (referenceObjects,
-                                                  parallObjects, parallObjects - referenceObjects)
-    print '\tHistos  : %i\t%i\t\t( p-s = %i )' % (referenceHistos,
-                                                  parallHistos,  parallHistos - referenceHistos)
-    print '\t          __________'
-    print '\tTotal   : %i\t%i\n' % (
-        referenceHistos + referenceObjects,  parallHistos + parallObjects)
-    print 'Objects/Histos unique to Reference File : %i / %i' % (
-        len(uniqueSerPaths) - uniqueSerHistos, uniqueSerHistos)
-    print 'Objects/Histos unique to Test File : %i / %i' % (
-        len(uniqueParPaths) - uniqueParHistos, uniqueParHistos)
-    print '\nMatching Histograms valid for Comparison : %i' % (mh)
-    print '\nOmissions\' patterns : '
+                print('%s\t\t%s: %i different bins' % (
+                    ds[e].ClassName(), e, identityDiffBins[e]))
+            print('-' * 80)
+
+    print('\n' + '=' * 80)
+    print('Comparison : Reference/Test ROOT Histo files')
+    print('\n\t\tReference\tTest')
+    print('\tObjects : %i\t%i\t\t( p-s = %i )' % (referenceObjects,
+                                                  parallObjects, parallObjects - referenceObjects))
+    print('\tHistos  : %i\t%i\t\t( p-s = %i )' % (referenceHistos,
+                                                  parallHistos,  parallHistos - referenceHistos))
+    print('\t          __________')
+    print('\tTotal   : %i\t%i\n' % (
+        referenceHistos + referenceObjects,  parallHistos + parallObjects))
+    print('Objects/Histos unique to Reference File : %i / %i' % (
+        len(uniqueSerPaths) - uniqueSerHistos, uniqueSerHistos))
+    print('Objects/Histos unique to Test File : %i / %i' % (
+        len(uniqueParPaths) - uniqueParHistos, uniqueParHistos))
+    print('\nMatching Histograms valid for Comparison : %i' % (mh))
+    print('\nOmissions\' patterns : ')
     for entry in gRegexBlackList:
-        print '\t%s' % (entry)
-    print '\nHistograms for Comparison (after Omissions) : %i' % (
-        mh - len(gRegexBlackList))
-    print '\n\tHISTOGRAM TESTS : '
-    print '\t\tKOLMOGOROV TEST      : %i' % (kTested)
-    print '\t\tINTEGRAL TEST        : %i' % (otherTest)
-    print '\t\tENTRIES TEST         : %i' % (xEntries)
+        print('\t%s' % (entry))
+    print('\nHistograms for Comparison (after Omissions) : %i' % (
+        mh - len(gRegexBlackList)))
+    print('\n\tHISTOGRAM TESTS : ')
+    print('\t\tKOLMOGOROV TEST      : %i' % (kTested))
+    print('\t\tINTEGRAL TEST        : %i' % (otherTest))
+    print('\t\tENTRIES TEST         : %i' % (xEntries))
     if checkBin2BinIdentity:
-        print '\t\tBIN2BIN TEST         : %i' % (passedIdentity)
-    print '\t\t                       ____'
-    print '\t\tTested               : %i' % (cEntries)
-
-    print '\n\tDISCREPANCIES : '
-    print '\t\tK-Test      : %i' % (failedKol)
-    print '\t\tIntegrals   : %i' % (xIntegrals)
-    print '\t\tEntries     : %i' % (xEntries)
+        print('\t\tBIN2BIN TEST         : %i' % (passedIdentity))
+    print('\t\t                       ____')
+    print('\t\tTested               : %i' % (cEntries))
+
+    print('\n\tDISCREPANCIES : ')
+    print('\t\tK-Test      : %i' % (failedKol))
+    print('\t\tIntegrals   : %i' % (xIntegrals))
+    print('\t\tEntries     : %i' % (xEntries))
     retval = failedKol + xIntegrals + xEntries + failedIdentity
     if retval != 0:
-        print '\nThe two sets of histograms were not identical'
-    print '\n' + '=' * 80
+        print('\nThe two sets of histograms were not identical')
+    print('\n' + '=' * 80)
     return retval
 
 # =============================================================================
@@ -407,8 +408,8 @@ if __name__ == '__main__':
     (options, args) = parser.parse_args()
 
     if len(args) != 2:
-        print "Wrong number of rootfiles. Usage:"
-        print usage
+        print("Wrong number of rootfiles. Usage:")
+        print(usage)
         sys.exit(1)
 
     extractBlacklist(options.blacklist)
@@ -416,9 +417,9 @@ if __name__ == '__main__':
     testFile, referenceFile = args
 
     tfs = TFile(testFile, 'REC')
-    print 'opening Test File : %s' % (testFile)
+    print('opening Test File : %s' % (testFile))
     tfp = TFile(referenceFile, 'REC')
-    print 'opening Reference File : %s' % (referenceFile)
+    print('opening Reference File : %s' % (referenceFile))
 
     # get structure of TFiles in a list of (path, object) tuples
     lref = rec(tfs)
diff --git a/GaudiPluginService/python/GaudiPluginService/cpluginsvc.py b/GaudiPluginService/python/GaudiPluginService/cpluginsvc.py
index e8fd4d8c2969ab6156a64ebfb9d196b2af58a588..3aad2debc60bd073ee1f73590dea4971a3f9981f 100644
--- a/GaudiPluginService/python/GaudiPluginService/cpluginsvc.py
+++ b/GaudiPluginService/python/GaudiPluginService/cpluginsvc.py
@@ -1,4 +1,5 @@
 # cpluginsvc is a ctypes-based wrapper for the C-exposed API of GaudiPluginService
+from __future__ import print_function
 __doc__ = '''
 cpluginsvc is a ctypes-based wrapper for the C-API of the GaudiPluginService.
 
@@ -9,11 +10,11 @@ e.g.:
 ...     try:
 ...         f.load()
 ...     except Exception:
-...         print ("** could not load [%s] for factory [%s]" % (f.library, f.name))
+...         print(("** could not load [%s] for factory [%s]" % (f.library, f.name)))
 ...         continue
-...     print f
+...     print(f)
 ...     for k,v in f.properties.iteritems():
-...         print ("\t%s: %s" % (k,v))
+...         print(("\t%s: %s" % (k,v)))
 '''
 
 import ctypes
@@ -101,19 +102,19 @@ class Factory(ctypes.Structure):
 
     @property
     def name(self):
-        return self._id
+        return self._id.decode('utf-8')
 
     @property
     def library(self):
-        return _lib.cgaudi_factory_get_library(self)
+        return _lib.cgaudi_factory_get_library(self).decode('utf-8')
 
     @property
     def type(self):
-        return _lib.cgaudi_factory_get_type(self)
+        return _lib.cgaudi_factory_get_type(self).decode('utf-8')
 
     @property
     def classname(self):
-        return _lib.cgaudi_factory_get_classname(self)
+        return _lib.cgaudi_factory_get_classname(self).decode('utf-8')
 
     @property
     def properties(self):
@@ -225,17 +226,17 @@ for f in _functions_list:
 
 
 if __name__ == "__main__":
-    print ("instance: %s" % registry())
-    print ("factories: %d" % len(factories()))
+    print("instance: %s" % registry())
+    print("factories: %d" % len(factories()))
     for _, f in factories().items():
         try:
             f.load()
         except Exception:
-            print (
+            print(
                 "** could not load [%s] for factory [%s]" % (f.library, f.name))
             continue
-        print f
+        print(f)
         for k, v in f.properties.items():
-            print ("\t%s: %s" % (k, v))
+            print("\t%s: %s" % (k, v))
 
 # EOF
diff --git a/GaudiPolicy/python/GaudiTesting/BaseTest.py b/GaudiPolicy/python/GaudiTesting/BaseTest.py
index 48cc70c191d02a47490ac235a91b07b95b6f299b..8408b16749d8970529aa17327a5d70ce49aac1b1 100644
--- a/GaudiPolicy/python/GaudiTesting/BaseTest.py
+++ b/GaudiPolicy/python/GaudiTesting/BaseTest.py
@@ -13,6 +13,8 @@ import logging
 
 from subprocess import Popen, PIPE, STDOUT
 
+import six
+
 
 def sanitize_for_xml(data):
     '''
@@ -37,7 +39,7 @@ def dumpProcs(name):
     if 'WORKSPACE' in os.environ:
         p = Popen(['ps', '-fH', '-U', getuser()], stdout=PIPE)
         with open(os.path.join(os.environ['WORKSPACE'], name), 'w') as f:
-            f.write(p.communicate()[0])
+            f.write(p.communicate()[0].decode('utf-8'))
 
 
 def kill_tree(ppid, sig):
@@ -48,13 +50,13 @@ def kill_tree(ppid, sig):
     log = logging.getLogger('kill_tree')
     ps_cmd = ['ps', '--no-headers', '-o', 'pid', '--ppid', str(ppid)]
     get_children = Popen(ps_cmd, stdout=PIPE, stderr=PIPE)
-    children = map(int, get_children.communicate()[0].split())
+    children = map(int, get_children.communicate()[0].decode('utf-8').split())
     for child in children:
         kill_tree(child, sig)
     try:
         log.debug('killing process %d', ppid)
         os.kill(ppid, sig)
-    except OSError, err:
+    except OSError as err:
         if err.errno != 3:  # No such process
             raise
         log.debug('no such process %d', ppid)
@@ -101,7 +103,7 @@ class BaseTest(object):
                 optionFile = tempfile.NamedTemporaryFile(suffix='.py')
             else:
                 optionFile = tempfile.NamedTemporaryFile(suffix='.opts')
-            optionFile.file.write(self.options)
+            optionFile.file.write(self.options.encode('utf-8'))
             optionFile.seek(0)
             self.args.append(RationalizePath(optionFile.name))
 
@@ -110,7 +112,7 @@ class BaseTest(object):
             self.environment = os.environ
         else:
             self.environment = dict(
-                self.environment.items() + os.environ.items())
+                list(self.environment.items()) + list(os.environ.items()))
 
         platform_id = (os.environ.get('BINARY_TAG') or
                        os.environ.get('CMTCONFIG') or
@@ -145,7 +147,7 @@ class BaseTest(object):
 
             prog = which(prog) or prog
 
-            args = map(RationalizePath, self.args)
+            args = list(map(RationalizePath, self.args))
 
             if prog_ext == ".py":
                 params = ['python', RationalizePath(prog)] + args
@@ -169,7 +171,10 @@ class BaseTest(object):
                 self.proc = Popen(params, stdout=PIPE, stderr=PIPE,
                                   env=self.environment)
                 logging.debug('(pid: %d)', self.proc.pid)
-                self.out, self.err = self.proc.communicate()
+                out, err = self.proc.communicate()
+                # Use `or ''` as the result is None if there was no output
+                self.out = out.decode('utf-8') or ''
+                self.err = err.decode('utf-8') or ''
 
             thread = threading.Thread(target=target)
             thread.start()
@@ -183,7 +188,7 @@ class BaseTest(object):
                 cmd = ['gdb', '--pid', str(self.proc.pid), '--batch',
                        '--eval-command=thread apply all backtrace']
                 gdb = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=STDOUT)
-                self.stack_trace = gdb.communicate()[0]
+                self.stack_trace = gdb.communicate()[0].decode('utf-8')
 
                 kill_tree(self.proc.pid, signal.SIGTERM)
                 thread.join(60)
@@ -245,14 +250,14 @@ class BaseTest(object):
                          'Unsupported Platforms': 'unsupported_platforms',
                          'Stack Trace': 'stack_trace'}
         resultDict = [(key, getattr(self, attr))
-                      for key, attr in field_mapping.iteritems()
+                      for key, attr in field_mapping.items()
                       if getattr(self, attr)]
         resultDict.append(('Working Directory',
                            RationalizePath(os.path.join(os.getcwd(),
                                                         self.workdir))))
-        # print dict(resultDict).keys()
-        resultDict.extend(self.result.annotations.iteritems())
-        # print self.result.annotations.keys()
+        # print(dict(resultDict).keys())
+        resultDict.extend(self.result.annotations.items())
+        # print(self.result.annotations.keys())
         return dict(resultDict)
 
     # -------------------------------------------------#
@@ -280,12 +285,12 @@ class BaseTest(object):
         if causes is None:
             causes = self.causes
 
-        reflines = filter(
-            None, map(lambda s: s.rstrip(), reference.splitlines()))
+        reflines = list(filter(
+            None, map(lambda s: s.rstrip(), reference.splitlines())))
         if not reflines:
             raise RuntimeError("Empty (or null) reference")
         # the same on standard output
-        outlines = filter(None, map(lambda s: s.rstrip(), stdout.splitlines()))
+        outlines = list(filter(None, map(lambda s: s.rstrip(), stdout.splitlines())))
 
         res_field = "GaudiTest.RefBlock"
         if id:
@@ -622,12 +627,14 @@ class Result:
         self.annotations = annotations.copy()
 
     def __getitem__(self, key):
-        assert type(key) in types.StringTypes
+        assert isinstance(key, six.string_types)
         return self.annotations[key]
 
     def __setitem__(self, key, value):
-        assert type(key) in types.StringTypes
-        assert type(value) in types.StringTypes
+        assert isinstance(key, six.string_types), \
+               'expected {0!r} to be of string type'.format(key)
+        assert isinstance(value, six.string_types), \
+               'expected {0!r} to be of string type'.format(value)
         self.annotations[key] = value
 
     def Quote(self, string):
@@ -681,7 +688,7 @@ class BasicOutputValidator:
                 r'Warning in <TInterpreter::ReadRootmapFile>: .* is already in .*')
 
             def keep_line(l): return not to_ignore.match(l)
-            return filter(keep_line, s1.splitlines()) == filter(keep_line, s2.splitlines())
+            return list(filter(keep_line, s1.splitlines())) == list(filter(keep_line, s2.splitlines()))
         else:
             return s1.splitlines() == s2.splitlines()
 
@@ -739,7 +746,7 @@ class LineSkipper(FilePreprocessor):
     def __init__(self, strings=[], regexps=[]):
         import re
         self.strings = strings
-        self.regexps = map(re.compile, regexps)
+        self.regexps = list(map(re.compile, regexps))
 
     def __processLine__(self, line):
         for s in self.strings:
@@ -976,7 +983,7 @@ class ReferenceFileValidator:
     def __call__(self, stdout, result):
         causes = []
         if os.path.isfile(self.reffile):
-            orig = open(self.reffile).xreadlines()
+            orig = open(self.reffile).readlines()
             if self.preproc:
                 orig = self.preproc(orig)
                 result[self.result_key + '.preproc.orig'] = \
@@ -988,8 +995,8 @@ class ReferenceFileValidator:
             new = self.preproc(new)
 
         diffs = difflib.ndiff(orig, new, charjunk=difflib.IS_CHARACTER_JUNK)
-        filterdiffs = map(lambda x: x.strip(), filter(
-            lambda x: x[0] != " ", diffs))
+        filterdiffs = list(map(lambda x: x.strip(), filter(
+            lambda x: x[0] != " ", diffs)))
         if filterdiffs:
             result[self.result_key] = result.Quote("\n".join(filterdiffs))
             result[self.result_key] += result.Quote("""
diff --git a/GaudiPolicy/python/GaudiTesting/QMTTest.py b/GaudiPolicy/python/GaudiTesting/QMTTest.py
index 6152dbe6105be251f957c7b056bedf741519ae08..6f2aba32a7300c07cd86e63eec1a404d42c7c3c7 100644
--- a/GaudiPolicy/python/GaudiTesting/QMTTest.py
+++ b/GaudiPolicy/python/GaudiTesting/QMTTest.py
@@ -1,5 +1,5 @@
 # -*- coding: utf-8 -*-
-from BaseTest import *
+from GaudiTesting.BaseTest import *
 import logging
 import os
 import sys
@@ -72,7 +72,7 @@ class QMTTest(BaseTest):
                         # positional or keyword
                         if a not in positional and a not in kwargs:
                             kwargs[a] = self.extra_args[a]
-                    return apply(self.callable, args, kwargs)
+                    return self.callable(*args, **kwargs)
 
             # local names to be exposed in the script
             stdout_ref = self._expandReferenceFileName(self.reference)
@@ -111,8 +111,8 @@ class QMTTest(BaseTest):
                                                  "result": result,
                                                  "causes": self.causes})
                                 }
-            # print self.validator
-            exec self.validator in globals(), exported_symbols
+            # print(self.validator)
+            exec(self.validator, globals(), exported_symbols)
             return result, self.causes
         else:
             return super(QMTTest, self).ValidateOutput(stdout, stderr, result)
diff --git a/GaudiPolicy/python/GaudiTesting/Run.py b/GaudiPolicy/python/GaudiTesting/Run.py
index 7424e902c6ad3f6fad8db2fc88dc2099a2a76333..6e8f7abe00d833d2342bd94fcc1f17caa5d5ffa0 100644
--- a/GaudiPolicy/python/GaudiTesting/Run.py
+++ b/GaudiPolicy/python/GaudiTesting/Run.py
@@ -1,8 +1,10 @@
 # -*- coding: utf-8 -*-
+from __future__ import print_function
+import importlib
 import os
 import sys
 import xml.sax.saxutils as XSS
-import BaseTest as GT
+from GaudiTesting import BaseTest as GT
 import logging
 
 # FIXME: module alias for backward compatibility
@@ -14,14 +16,14 @@ def basic_report(results):
     Report function taking the dictionary from BasicTest.run() and display
     stdout and stderr from it.
     '''
-    print '=== stdout ==='
-    print results.get('stdout', '')
-    print '=== stderr ==='
-    print results.get('stderr', '')
-    print '=== result ==='
-    print results.get('Status')
+    print('=== stdout ===')
+    print(results.get('stdout', ''))
+    print('=== stderr ===')
+    print(results.get('stderr', ''))
+    print('=== result ===')
+    print(results.get('Status'))
     if results.get('Status') != 'passed' and 'Causes' in results:
-        print '   ', 'unexpected ' + ', '.join(results['Causes'])
+        print('   ', 'unexpected ' + ', '.join(results['Causes']))
 
 
 def quiet_report(results):
@@ -37,10 +39,10 @@ def ctest_report(results):
     from it in a CTest-friendly way.
     '''
     # It's weird, I know, but it tells CTest not to cut the output.
-    print 'CTEST_FULL_OUTPUT'
-    print results.get('stdout', '')
+    print('CTEST_FULL_OUTPUT')
+    print(results.get('stdout', ''))
     handler = {'Environment': lambda v: '\n'.join('{0}={1}'.format(*item)
-                                                  for item in sorted(v.iteritems())),
+                                                  for item in sorted(v.items())),
                'Causes': lambda v: 'unexpected ' + ', '.join(v)}
 
     def id_handler(v): return str(v)
@@ -158,14 +160,12 @@ def main():
             test_module = os.environ.get(
                 'GAUDI_QMTEST_MODULE', 'GaudiTesting.QMTTest')
             test_class = os.environ.get('GAUDI_QMTEST_CLASS', 'QMTTest')
-            exec 'from {} import {} as test_class'.format(
-                test_module, test_class)
+
+            test_class = getattr(importlib.import_module(test_module), test_class)
             fileToTest = test_class(filename)
             results = fileToTest.run()
-        except Exception, e:
-            logging.error(
-                'Exception caught when trying to instantiate qmt test python object')
-            logging.error(e)
+        except Exception as e:
+            logging.exception('Exception caught when trying to instantiate qmt test python object')
             return 1
 
     report = globals()[opts.report + '_report']
diff --git a/GaudiPolicy/qmtest_classes/GaudiTest.py b/GaudiPolicy/qmtest_classes/GaudiTest.py
index 12027ad635bbeb2f337c5dd97ba1b2b4907db3ae..ccdb4359159a5b78a57ffa62af334e8031792bb0 100644
--- a/GaudiPolicy/qmtest_classes/GaudiTest.py
+++ b/GaudiPolicy/qmtest_classes/GaudiTest.py
@@ -2,6 +2,7 @@
 # File:   GaudiTest.py
 # Author: Marco Clemencic CERN/PH-LBC
 ########################################################################
+from __future__ import print_function
 __author__ = 'Marco Clemencic CERN/PH-LBC'
 ########################################################################
 # Imports
@@ -62,7 +63,10 @@ if sys.platform == "win32":
     import win32pipe
     import win32process
 else:
-    import cPickle
+    try:
+        import cPickle
+    except ImportError:
+        import pickle as cPickle
     import fcntl
     import select
     import qm.sigmask
@@ -82,7 +86,7 @@ class TemporaryEnvironment:
         Create a temporary environment on top of the one specified
         (it can be another TemporaryEnvironment instance).
         """
-        # print "New environment"
+        # print("New environment")
         self.old_values = {}
         self.env = orig
         self._keep_same = keep_same
@@ -152,7 +156,7 @@ class TemporaryEnvironment:
         """
         Revert the changes on destruction.
         """
-        # print "Restoring the environment"
+        # print("Restoring the environment")
         self.restore()
 
     def gen_script(self, shell_type):
@@ -657,7 +661,7 @@ class ReferenceFileValidator:
     def __call__(self, stdout, result):
         causes = []
         if os.path.isfile(self.reffile):
-            orig = open(self.reffile).xreadlines()
+            orig = open(self.reffile).readlines()
             if self.preproc:
                 orig = self.preproc(orig)
         else:
@@ -1085,7 +1089,7 @@ class GaudiFilterExecutable(qm.executable.Filter):
                     max_fds = os.sysconf("SC_OPEN_MAX")
                 except:
                     max_fds = 256
-                for fd in xrange(max_fds):
+                for fd in range(max_fds):
                     try:
                         os.close(fd)
                     except:
@@ -1503,7 +1507,7 @@ class GaudiExeTest(ExecTestBase):
                         # positional or keyword
                         if a not in positional and a not in kwargs:
                             kwargs[a] = self.extra_args[a]
-                    return apply(self.callable, args, kwargs)
+                    return self.callable(*args, **kwargs)
             # local names to be exposed in the script
             exported_symbols = {"self": self,
                                 "stdout": stdout,
@@ -1533,7 +1537,7 @@ class GaudiExeTest(ExecTestBase):
                                                                             "causes": causes}),
 
                                 }
-            exec self.validator in globals(), exported_symbols
+            exec(self.validator, globals(), exported_symbols)
         else:
             self.ValidateWithReference(stdout, stderr, result, causes)
 
@@ -1889,7 +1893,7 @@ class GaudiExeTest(ExecTestBase):
             # Write the output file
             codecs.open(destfile, "w", encoding='utf-8').write(xml)
         except:
-            print 'WARNING: problem generating Eclipse launcher'
+            print('WARNING: problem generating Eclipse launcher')
 
 
 try:
@@ -2303,7 +2307,7 @@ class XMLResultStream(ResultStream):
             else:
                 value.text = convert_xml_illegal_chars(result[field])
 
-        if result.has_key("ExecTest.stdout"):  # "ExecTest.stdout" in result :
+        if "ExecTest.stdout" in result:  # "ExecTest.stdout" in result :
             Measurement = ET.SubElement(Results, "Measurement")
             value = ET.SubElement(Measurement, "Value")
             if "<pre>" in result["ExecTest.stdout"][0:6]:
diff --git a/GaudiPolicy/scripts/CTestXML2HTML b/GaudiPolicy/scripts/CTestXML2HTML
index 69200a240c1469d2317cbc4fc0a97b77697d2554..a98a3018cc20659ad5f57de0264c5c3f5ab845bc 100755
--- a/GaudiPolicy/scripts/CTestXML2HTML
+++ b/GaudiPolicy/scripts/CTestXML2HTML
@@ -1,5 +1,6 @@
 #!/usr/bin/env python
 # -*- coding:utf-8 -*-
+from __future__ import print_function
 import os
 import sys
 import json
@@ -34,8 +35,8 @@ def cleanXml(xmlFileName):
     def hexConvert(char):
         return hex(ord(char))
 
-    print " Trying to repair the xml file"
-    print " Replacing invalid char"
+    print(" Trying to repair the xml file")
+    print(" Replacing invalid char")
     xmlFile = open(xmlFileName, 'r')
     data = xmlFile.read()
     xmlFile.close()
@@ -50,7 +51,7 @@ def cleanXml(xmlFileName):
         xmlFile.close()
         del tree
     except:
-        print " Removing invalid char"
+        print(" Removing invalid char")
         xmlFile = open(xmlFileName, 'w')
         xmlFile.write(_illegal_xml_chars_Re.sub("", data))
         xmlFile.close()
@@ -65,7 +66,7 @@ def formatMeasurementText(txt, escape=False, preformat=True):
     from codecs import encode
     if hasattr(txt, 'decode'):
         txt = txt.decode(errors='ignore')
-    txt = encode(txt, 'utf-8', 'xmlcharrefreplace')
+    txt = encode(txt, 'utf-8', 'xmlcharrefreplace').decode('utf-8')
     if escape:
         txt = escape_xml(txt)
     if preformat:
@@ -325,7 +326,7 @@ class TestOrganizer:
 
     def _addStatistics(self):  # ,groupContainer,fatherAdresses):
         """ add statistics to the html structure."""
-        for group in self._groups.iteritems():
+        for group in self._groups.items():
             if group[0] not in self.fieldToAvoidList:
                 self._addStatistic(group[1])
 
@@ -348,12 +349,12 @@ class TestOrganizer:
                 stats.clear()
                 stats.set("class", "statistics")
 
-        for stat in group["Statistics"].iteritems():
+        for stat in group["Statistics"].items():
             ET.SubElement(stats, "span", {
                           "class": stat[0]}).text = '  ' + stat[0] + ' = ' + str(stat[1])
 
         # process all the subgroups
-        for grp in group.iteritems():
+        for grp in group.items():
             if grp[0] not in self.fieldToAvoidList:
                 self._addStatistic(grp[1])
 
@@ -388,7 +389,7 @@ class TestOrganizer:
         else:
             sites = newdataset.getchildren()
         for site in sites:
-            print "    -> Process the site: " + site.get("BuildStamp")
+            print("    -> Process the site: " + site.get("BuildStamp"))
             Testing = site.find("Testing")
             # process each test case
             for Test in Testing.findall("Test"):
@@ -568,7 +569,7 @@ def updateAnnotations(site, Testing, outputTemp, annotationsList):
 def main():
 
     #progStartTime = datetime.now()
-    # print "program launch at "+progStartTime.strftime("%b %d %H:%M %Z")
+    # print("program launch at "+progStartTime.strftime("%b %d %H:%M %Z"))
 
     # parse the command line option
     usage = "usage: %prog [options] Convert CTest like XML into a webpage"
@@ -593,7 +594,7 @@ def main():
     inputs = []
     if options.inputFile is not None:
         if not os.path.isfile(options.inputFile):
-            print " The input file is not a valid file"
+            print(" The input file is not a valid file")
             exit(1)
         else:
             inputs.append(options.inputFile)
@@ -607,11 +608,11 @@ def main():
 
     # close the program if there is no file found
     if inputs == []:
-        print " No file found \n"
+        print(" No file found \n")
         exit()
 
-    print "Converting *Test.xml files from %s to HTML format in %s" % (
-        options.inputDirectory, options.outputDirectory)
+    print("Converting *Test.xml files from %s to HTML format in %s" % (
+        options.inputDirectory, options.outputDirectory))
     # verify or create the output directory
     output = options.outputDirectory
     if not os.path.isdir(output):
@@ -639,7 +640,7 @@ def main():
 
     # process each file
     for fileName in inputs:
-        print "Process the file : " + fileName
+        print("Process the file : " + fileName)
         # read and parse the xml file
         try:
             tree = ET.parse(fileName)
@@ -656,7 +657,7 @@ def main():
         else:
             sites = newdataset.getchildren()
         for site in sites:
-            # print "    -> Process the site : "+site.get("BuildStamp")
+            # print("    -> Process the site : "+site.get("BuildStamp"))
             Testing = site.find("Testing")
             tests = Testing.findall('Test')
             if not tests:
@@ -682,7 +683,7 @@ def main():
                         for d in os.environ.get('DATA_PATH', '').split(os.pathsep)
                         if os.path.isdir(os.path.join(d, 'HTMLTestReportSkel'))][0]
             except IndexError:
-                print "Cannot find skeleton directory in ${DATA_PATH}"
+                print("Cannot find skeleton directory in ${DATA_PATH}")
                 exit(1)
 
             # Copy the template directory (do not overwrite)
@@ -778,12 +779,10 @@ def main():
                     text = formatMeasurementText(text, escape=True)
                     # no "Measurement" or no "Value" or no text
                 except AttributeError as x:
-                    print 'WARNING: {0[id]}: AttributeError: {1}'.format(
-                        summary, x)
+                    print('WARNING: {0[id]}: AttributeError: {1}'.format(summary, x))
                     text = '<i>no stdout</i>'
                 except KeyError as x:
-                    print 'WARNING: {0[id]}: KeyError: {1}'.format(
-                        summary, x)
+                    print('WARNING: {0[id]}: KeyError: {1}'.format(summary, x))
                     # encoding or compressions unknown, keep original text
                     text = formatMeasurementText(value=text, escape=True)
                 with open(os.path.join(testCaseDir, "stdout"), "w") as stdout:
@@ -843,15 +842,15 @@ def main():
             organizer_failed.write()
 
     if sum(failures_count.values()):
-        print 'Some tests failed:'
-        print '\n'.join('  %s: %s' % i for i in failures_count.items())
+        print('Some tests failed:')
+        print('\n'.join('  %s: %s' % i for i in failures_count.items()))
         sys.exit(1)
 
-    # print " End of the program"
+    # print(" End of the program")
     #progEndTime = datetime.now()
     #delta = progEndTime - progStartTime
-    # print "program ended at "+progEndTime.strftime("%b %d %H:%M %Z")
-    # print "Duration : "+str(total_seconds_replacement(delta))+" seconds"
+    # print("program ended at "+progEndTime.strftime("%b %d %H:%M %Z"))
+    # print("Duration : "+str(total_seconds_replacement(delta))+" seconds")
 
 
 if __name__ == '__main__':
diff --git a/GaudiPolicy/scripts/ZipPythonDir.py b/GaudiPolicy/scripts/ZipPythonDir.py
index b4a4daac3374bb215384aff4be3d701a3fdab86f..fe87e57eac104e0439971c2be8639911a419fcf3 100755
--- a/GaudiPolicy/scripts/ZipPythonDir.py
+++ b/GaudiPolicy/scripts/ZipPythonDir.py
@@ -11,7 +11,7 @@ import stat
 import time
 import re
 import codecs
-from StringIO import StringIO
+from io import BytesIO
 
 # Class for generic exception coming from the zipdir() function
 
@@ -106,7 +106,7 @@ def checkEncoding(fileObj):
     # find the encoding of the file, if specified (in the first two lines)
     enc_exp = re.compile(r"coding[:=]\s*([-\w.]+)")
     for l in islice(fileObj, 2):
-        m = enc_exp.search(l)
+        m = enc_exp.search(l.decode('ascii'))
         if m:
             enc = m.group(1)
             break
@@ -151,7 +151,7 @@ def zipdir(directory, no_pyc=False):
             infolist = []
         (added, modified, untouched, removed) = _zipChanges(directory, infolist)
         if added or modified or removed:
-            tempBuf = StringIO()
+            tempBuf = BytesIO()
             z = zipfile.PyZipFile(tempBuf, "w", zipfile.ZIP_DEFLATED)
             for f in added + modified + untouched:
                 src = os.path.join(directory, f)
@@ -173,7 +173,7 @@ def zipdir(directory, no_pyc=False):
             log.info("File '%s' closed", filename)
         else:
             log.info("Nothing to do on '%s'", filename)
-    except UnicodeDecodeError, x:
+    except UnicodeDecodeError as x:
         log.error("Wrong encoding in file '%s':", src)
         log.error("    %s", x)
         log.error("Probably you forgot the line '# -*- coding: utf-8 -*-'")
diff --git a/GaudiPolicy/scripts/createProjVersHeader.py b/GaudiPolicy/scripts/createProjVersHeader.py
index 5c82c4ce697c57bee16def912d4fe8ce0186cf84..c78f1043dd0979b1b05cb8b2a6b9160adcc20225 100755
--- a/GaudiPolicy/scripts/createProjVersHeader.py
+++ b/GaudiPolicy/scripts/createProjVersHeader.py
@@ -1,5 +1,5 @@
 #!/usr/bin/env python
-
+from __future__ import print_function
 import os
 import sys
 import re
@@ -21,7 +21,7 @@ def main():
 
     project, version, outputfile = args
     if not opts.quiet:
-        print "Creating %s for %s %s" % (outputfile, project, version)
+        print("Creating %s for %s %s" % (outputfile, project, version))
 
     for style in [lhcb_ver_style, atlas_ver_style, plain_ver_style]:
         m = re.match(style, version)
@@ -38,7 +38,7 @@ def main():
     outdir = os.path.dirname(outputfile)
     if not os.path.exists(outdir):
         if not opts.quiet:
-            print "Creating directory", outdir
+            print("Creating directory", outdir)
         os.makedirs(outdir)
 
     # Prepare data to be written
diff --git a/GaudiProfiling/CMakeLists.txt b/GaudiProfiling/CMakeLists.txt
index 3def1797cb1b84d76c500d2c4f784c4d9816e15e..c19ac48f09489b488136b398f4ff6cfa179c4612 100644
--- a/GaudiProfiling/CMakeLists.txt
+++ b/GaudiProfiling/CMakeLists.txt
@@ -4,7 +4,7 @@ gaudi_depends_on_subdirs(GaudiKernel GaudiAlg)
 
 if (CMAKE_SYSTEM_NAME MATCHES Linux)
 
-find_package(Boost COMPONENTS python REQUIRED)
+find_package(Boost COMPONENTS ${BOOST_PYTHON_LIB_NAME} REQUIRED)
 find_package(PythonLibs REQUIRED)
 find_package(unwind)
 find_package(gperftools)
@@ -29,7 +29,7 @@ if(UNWIND_FOUND AND CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64")
                    INCLUDE_DIRS unwind ZLIB)
 
   gaudi_add_python_module(PyCPUFamily src/python/CPUFamily.cpp
-                          LINK_LIBRARIES ${Boost_PYTHON_LIBRARY}
+                          LINK_LIBRARIES Boost
                           INCLUDE_DIRS PythonLibs)
 
   gaudi_add_executable(GaudiGenProfilingHtml src/app/pfm_gen_analysis.cpp
diff --git a/GaudiProfiling/python/GaudiProfiling/GenerateGaudiOpts.py b/GaudiProfiling/python/GaudiProfiling/GenerateGaudiOpts.py
index 5f1857ed4e8da393a470da092c8c7743769075f5..1216b51603975744ec7adefd123ef4abcce881b5 100644
--- a/GaudiProfiling/python/GaudiProfiling/GenerateGaudiOpts.py
+++ b/GaudiProfiling/python/GaudiProfiling/GenerateGaudiOpts.py
@@ -1,3 +1,4 @@
+from __future__ import print_function
 import os
 import sys
 import time
@@ -41,7 +42,7 @@ def generateOptions(counter, cmask, invmask, sampling_period, startatevent, stor
     pfaud.START_AT_EVENT = startatevent
     #pfaud.LEVEL = 5
     AuditorSvc().Auditors.append(pfaud)
-    print pfaud
+    print(pfaud)
 
 
 class XmlDictObject(dict):
@@ -61,7 +62,7 @@ class XmlDictObject(dict):
         self.__setitem__(item, value)
 
     def __str__(self):
-        if self.has_key('_text'):
+        if '_text' in self:
             return self.__getitem__('_text')
         else:
             return ''
@@ -138,7 +139,7 @@ def _ConvertXmlToDictRecurse(node, dictclass):
     for child in node:
         # recursively add the element's children
         newitem = _ConvertXmlToDictRecurse(child, dictclass)
-        if nodedict.has_key(child.tag):
+        if child.tag in nodedict:
             # found duplicate tag, force a list
             if type(nodedict[child.tag]) is type([]):
                 # append to existing list
@@ -175,6 +176,6 @@ def ConvertXmlToDict(root, dictclass=XmlDictObject):
     if type(root) == type(''):
         root = ElementTree.parse(root).getroot()
     elif not isinstance(root, ElementTree.Element):
-        raise TypeError, 'Expected ElementTree.Element or file path string'
+        raise TypeError('Expected ElementTree.Element or file path string')
 
     return dictclass({root.tag: _ConvertXmlToDictRecurse(root, dictclass)})
diff --git a/GaudiProfiling/scripts/GaudiProfiler b/GaudiProfiling/scripts/GaudiProfiler
index 60a02ed37855698b6309f0a9458a2ac39bb25126..8b68fbbb46e2cb4da7c94b29dc918c2b603b2dd4 100755
--- a/GaudiProfiling/scripts/GaudiProfiler
+++ b/GaudiProfiling/scripts/GaudiProfiler
@@ -1,4 +1,5 @@
 #!/usr/bin/env python
+from __future__ import print_function
 
 import os
 import sys
@@ -11,11 +12,11 @@ from subprocess import Popen
 if __name__ == '__main__':
     from PyCPUFamily import CPUFamily
     if 'GAUDIPROFILINGROOT' not in os.environ:
-        print """
+        print("""
             **************************************** ERROR *****************************************
             * GaudiProfiler: Environment variable $GAUDIPROFILINGROOT is missing. Process stopped. *
             ****************************************************************************************
-        """
+        """)
         sys.exit()
     if 'GAUDIPROFILERCONFIG' in os.environ:
         configFile = os.environ['GAUDIPROFILERCONFIG']
@@ -25,7 +26,7 @@ if __name__ == '__main__':
     try:
         conf = ConvertXmlToDict(configFile)
     except xml.parsers.expat.ExpatError:
-        print "Invalid xml file: %s" % configFile
+        print("Invalid xml file: %s" % configFile)
         sys.exit()
     #gaudiRunParams = ' '.join(sys.argv[1:])
     storeResultsAt = conf.PFM_CONFIG.PROPERTIES.OUTPUT_DIR
@@ -43,7 +44,7 @@ if __name__ == '__main__':
         sampling_period = []
         if type([]) != type(es.EVENT):
             es.EVENT = [es.EVENT]
-        for x in xrange(len(es.EVENT)):
+        for x in range(len(es.EVENT)):
             counter.append(es.EVENT[x].NAME)
             cmask.append(es.EVENT[x].CMASK)
             invmask.append(es.EVENT[x].INVMASK)
@@ -51,14 +52,14 @@ if __name__ == '__main__':
         res = Popen(['gaudirun.py'] + sys.argv[1:] + ['--option', 'from %s import generateOptions; generateOptions(%s, %s, %s, %s, %s, %r, %r)' %
                                                       (moduleName, counter, cmask, invmask, sampling_period, startAtEvent, storeResultsAt, family)]).wait()
         if res:
-            print """
+            print("""
             ************************************** ERROR ***************************************
             * GaudiProfiler: PerfMonAuditor failed. Is pfm library and kernel patch installed? *
             ************************************************************************************
-            """
+            """)
             sys.exit(res)
     # post-processing
     os.system("GaudiGenProfilingHtml.exe %s --caa" % storeResultsAt)
     shutil.copy2(os.path.sep.join([os.environ['GAUDIPROFILINGROOT'], 'data',
                                    'sorttable.js']), os.path.sep.join([storeResultsAt, 'HTML']))
-    print "Results:", os.path.sep.join([storeResultsAt, 'HTML', 'index.html'])
+    print("Results:", os.path.sep.join([storeResultsAt, 'HTML', 'index.html']))
diff --git a/GaudiPython/GaudiPython/Helpers.h b/GaudiPython/GaudiPython/Helpers.h
index 7541737878f216820741adfb1a31a388128c08b9..d4901fe6cd43b9cec8461258fa7168c3608f410a 100644
--- a/GaudiPython/GaudiPython/Helpers.h
+++ b/GaudiPython/GaudiPython/Helpers.h
@@ -3,6 +3,33 @@
 
 #include "Python.h"
 
+// Python 3 compatibility
+#if PY_MAJOR_VERSION >= 3
+
+#define PyInt_FromLong PyLong_FromLong
+
+#define PyBuffer_Type PyMemoryView_Type
+
+// Taken from ROOT's TPyBufferFactory
+static PyObject* PyBuffer_FromReadWriteMemory(void* ptr, int size)
+{
+#if PY_VERSION_HEX > 0x03000000
+  // Python 3 will set an exception if nullptr, just rely on size == 0
+  if (!ptr) {
+    static long dummy[1];
+    ptr = dummy;
+    size = 0;
+  }
+#endif
+  Py_buffer bufinfo = {ptr, NULL, size, 1, 0, 1, NULL, NULL, NULL, NULL,
+#if PY_VERSION_HEX < 0x03030000
+  {0, 0},
+#endif
+  NULL};
+  return PyMemoryView_FromBuffer(&bufinfo);
+}
+#endif
+
 #include "GaudiKernel/DataObject.h"
 #include "GaudiKernel/IAlgManager.h"
 #include "GaudiKernel/IAlgTool.h"
@@ -149,11 +176,17 @@ namespace GaudiPython
     template <class T>
     static Py_ssize_t Array_length( PyObject* self )
     {
+#if PY_MAJOR_VERSION >= 3
+      Py_buffer bufinfo;
+      ( *( self->ob_type->tp_as_buffer->bf_getbuffer ) )( self, &bufinfo, PyBUF_SIMPLE );
+      Py_ssize_t size = bufinfo.len;
+#else
 #if PY_VERSION_HEX < 0x02050000
       const
 #endif
-          char*  buf  = 0;
+          char* buf   = 0;
       Py_ssize_t size = ( *( self->ob_type->tp_as_buffer->bf_getcharbuffer ) )( self, 0, &buf );
+#endif
       return size / sizeof( T );
     }
 
@@ -176,7 +209,13 @@ namespace GaudiPython
       const
 #endif
           char*  buf  = nullptr;
+#if PY_MAJOR_VERSION >= 3
+      Py_buffer bufinfo;
+      ( *( self->ob_type->tp_as_buffer->bf_getbuffer ) )( self, &bufinfo, PyBUF_SIMPLE );
+      Py_ssize_t size = bufinfo.len;
+#else
       Py_ssize_t size = ( *( self->ob_type->tp_as_buffer->bf_getcharbuffer ) )( self, 0, &buf );
+#endif
       if ( idx < 0 || idx >= size / int( sizeof( T ) ) ) {
         PyErr_SetString( PyExc_IndexError, "buffer index out of range" );
         return nullptr;
diff --git a/GaudiPython/python/GaudiPython/Bindings.py b/GaudiPython/python/GaudiPython/Bindings.py
index 83c2be7d04c61950d94e886b734f13ab81f6875d..82d7bdd455fa2b326fd717dcf5eef4e9250ef20f 100644
--- a/GaudiPython/python/GaudiPython/Bindings.py
+++ b/GaudiPython/python/GaudiPython/Bindings.py
@@ -1,6 +1,6 @@
 # File: GaudiPython/Bindings.py
 # Author: Pere Mato (pere.mato@cern.ch)
-
+from __future__ import absolute_import, print_function
 """ GaudiPython.Bindings module.
     This module provides the basic bindings of the main Gaudi
     components to Python. It is itself based on the ROOT cppyy
@@ -25,16 +25,20 @@ try:
     import cppyy
 except ImportError:
     # FIXME: backward compatibility
-    print "# WARNING: using PyCintex as cppyy implementation"
+    print("# WARNING: using PyCintex as cppyy implementation")
     import PyCintex as cppyy
 
+if sys.version_info >= (3,):
+    # Python 2 compatibility
+    long = int
+
 if ROOT6WorkAroundEnabled('ROOT-5478'):
     # Trigger the loading of GaudiKernelDict
     cppyy.gbl.DataObject
     # Trigger the loading of GaudiPythonDict
     cppyy.gbl.Chrono
 
-import Pythonizations
+from . import Pythonizations
 # Import Configurable from AthenaCommon or GaudiKernel if the first is not
 # available.
 from GaudiKernel.Proxy.Configurable import Configurable, getNeededConfigurables
@@ -66,7 +70,7 @@ namespace GaudiPython { namespace Helpers {
 
 # toIntArray, toShortArray, etc.
 for l in [l for l in dir(Helper) if re.match("^to.*Array$", l)]:
-    exec "%s = Helper.%s" % (l, l)
+    exec("%s = Helper.%s" % (l, l))
     __all__.append(l)
 
 # FIXME: (MCl) Hack to handle ROOT 5.18 and ROOT >= 5.20
@@ -106,9 +110,9 @@ class InterfaceCast(object):
                 if obj.queryInterface(self.type.interfaceID(), ip).isSuccess():
                     return ip
                 else:
-                    print "ERROR: queryInterface failed for", obj, "interface:", self.type
-            except Exception, e:
-                print "ERROR: exception", e, "caught when retrieving interface", self.type, "for object", obj
+                    print("ERROR: queryInterface failed for", obj, "interface:", self.type)
+            except Exception as e:
+                print("ERROR: exception", e, "caught when retrieving interface", self.type, "for object", obj)
                 import traceback
                 traceback.print_stack()
         return None
@@ -135,7 +139,7 @@ def loaddict(dict):
         try:
             cppyy.loadDict(dict)
         except:
-            raise ImportError, 'Error loading dictionary library'
+            raise ImportError('Error loading dictionary library')
 
 # ---get a class (by loading modules if needed)--------------------------------
 
@@ -258,7 +262,7 @@ class iProperty(object):
         ip = self.getInterface()
         if ip:
             if not gbl.Gaudi.Utils.hasProperty(ip, name):
-                raise AttributeError, 'property %s does not exist' % name
+                raise AttributeError('property %s does not exist' % name)
             prop = ip.getProperty(name)
 
             if ROOT6WorkAroundEnabled('ROOT-7201'):
@@ -271,8 +275,8 @@ class iProperty(object):
 
             if canSetValue:
                 if not prop.setValue(value):
-                    raise AttributeError, 'property %s could not be set from %s' % (
-                        name, value)
+                    raise AttributeError('property %s could not be set from %s' % (
+                        name, value))
             else:
                 if tuple == type(value):
                     value = str(value)
@@ -288,8 +292,8 @@ class iProperty(object):
                 else:
                     sc = prop.fromString(value)
                 if sc.isFailure():
-                    raise AttributeError, 'property %s could not be set from %s' % (
-                        name, value)
+                    raise AttributeError('property %s could not be set from %s' % (
+                        name, value))
         else:
             if type(value) == str:
                 value = '"%s"' % value  # need double quotes
@@ -311,7 +315,7 @@ class iProperty(object):
         ip = self.getInterface()
         if ip:
             if not gbl.Gaudi.Utils.hasProperty(ip, name):
-                raise AttributeError, 'property %s does not exist' % name
+                raise AttributeError('property %s does not exist' % name)
             prop = ip.getProperty(name)
             if StringProperty == type(prop):
                 return prop.value()
@@ -331,7 +335,7 @@ class iProperty(object):
                     return eval(p.value(), {}, {})
                 except:
                     return p.value()
-            raise AttributeError, 'property %s does not exist' % name
+            raise AttributeError('property %s does not exist' % name)
 
     def properties(self):
         dct = {}
@@ -347,10 +351,10 @@ class iProperty(object):
             for p in props:
                 try:
                     dct[p.name()] = PropertyEntry(p)
-                except (ValueError, TypeError), e:
-                    raise ValueError, "gaudimodule.iProperty.properties(): %s%s processing property %s.%s = %s" % \
+                except (ValueError, TypeError) as e:
+                    raise ValueError("gaudimodule.iProperty.properties(): %s%s processing property %s.%s = %s" % \
                         (e.__class__.__name__, e.args,
-                         propsFrom, p.name(), p.value())
+                         propsFrom, p.name(), p.value()))
         return dct
 
     def name(self):
@@ -597,7 +601,7 @@ class iDataSvc(iService):
                 node = root.registry()
             else:
                 return
-        print node.identifier()
+        print(node.identifier())
         if node.object():
             for l in self.leaves(node):
                 self.dump(l)
@@ -697,7 +701,7 @@ class iHistogramSvc(iDataSvc):
         >>> svc = ...
         >>> histo = svc.book( .... )
         """
-        return apply(self._ihs.book, args)
+        return self._ihs.book(*args)
 
     def bookProf(self, *args):
         """
@@ -705,7 +709,7 @@ class iHistogramSvc(iDataSvc):
         >>> svc = ...
         >>> histo = svc.bookProf( .... )
         """
-        return apply(self._ihs.bookProf, args)
+        return self._ihs.bookProf(*args)
 
     def __getitem__(self, path):
         """
@@ -750,14 +754,14 @@ class iNTupleSvc(iDataSvc):
         iDataSvc.__init__(self, name, ints)
 
     def book(self, *args):
-        return apply(self._ints.book, args)
+        return self._ints.book(*args)
 
     def defineOutput(self, files, typ="Gaudi::RootCnvSvc"):
         """ Defines the mapping between logical names and the output file
             Usage:
               defineOutput({'LUN1':'MyFile1.root', 'LUN2':'Myfile2.root'}, svc='Gaudi::RootCnvSvc')
         """
-        import Persistency as prs
+        from . import Persistency as prs
         helper = prs.get(typ)
         helper.configure(AppMgr())
         self.Output = [helper.formatOutput(
@@ -867,7 +871,7 @@ class iEventSelector(iService):
         self.__dict__['g'] = AppMgr()
 
     def open(self, stream, typ='Gaudi::RootCnvSvc', **kwargs):
-        import Persistency as prs
+        from . import Persistency as prs
         helper = prs.get(typ)
         helper.configure(self.g)
         self.Input = helper.formatInput(stream, **kwargs)
@@ -943,9 +947,11 @@ class AppMgr(iService):
                 GaudiKernel.Proxy.Configurable.applyConfigurableUsers()
             # This is the default and could be overridden with "selfopts"
             self.OutputLevel = 3
-            selfprops = Configurable.allConfigurables.get('ApplicationMgr', {})
-            if selfprops:
-                selfprops = expandvars(selfprops.getValuedProperties())
+            try:
+                appMgr = Configurable.allConfigurables['ApplicationMgr']
+                selfprops = expandvars(appMgr.getValuedProperties())
+            except KeyError:
+                selfprops = {}
             for p, v in selfprops.items():
                 setattr(self, p, v)
             for p, v in selfoptions.items():
@@ -990,14 +996,25 @@ class AppMgr(iService):
         if hasattr(Configurable, "_configurationLocked"):
             Configurable._configurationLocked = True
 
-        # Ensure that the exit method is called when exiting from Python
+        self._install_exit_handlers()
+
+    def _install_exit_handlers(self):
+        """Ensure that the exit method is called when exiting from Python, and
+        try to ensure that ROOT doesn't intefere too much."""
         import atexit
         atexit.register(self.exit)
 
+        try:
+            exit_handlers = atexit._exithandlers
+        except AttributeError:
+            # Python 3's atext does not expose _exithandlers, so we can't do
+            # anything more
+            return
+
         # ---Hack to avoid bad interactions with the ROOT exit handler
         # Look for an exit handler installed by ROOT
         root_handler_installed = False
-        for h in atexit._exithandlers:
+        for h in exit_handlers:
             func = h[0]
             if hasattr(func, "__module__") and func.__module__ == "ROOT":
                 root_handler_installed = True
@@ -1164,19 +1181,19 @@ class AppMgr(iService):
         Print the sequence of Algorithms.
         """
         def printAlgo(algName, appMgr, prefix=' '):
-            print prefix + algName
+            print(prefix + algName)
             alg = appMgr.algorithm(algName.split("/")[-1])
             prop = alg.properties()
-            if prop.has_key("Members"):
+            if "Members" in prop:
                 subs = prop["Members"].value()
                 for i in subs:
                     printAlgo(i.strip('"'), appMgr, prefix + "     ")
         mp = self.properties()
         prefix = 'ApplicationMgr    SUCCESS '
-        print prefix + "****************************** Algorithm Sequence ****************************"
+        print(prefix + "****************************** Algorithm Sequence ****************************")
         for i in mp["TopAlg"].value():
             printAlgo(i, self, prefix)
-        print prefix + "******************************************************************************"
+        print(prefix + "******************************************************************************")
 
     def config(self, **args):
         """
@@ -1192,7 +1209,7 @@ class AppMgr(iService):
         for file in files:
             sc = self.readOptions(file)
             if sc.isFailure():
-                raise RuntimeError, ' Unable to read file "' + file + '" '
+                raise RuntimeError(' Unable to read file "' + file + '" ')
         options = args.get('options', None)
         if options:
             import tempfile
@@ -1213,7 +1230,7 @@ class AppMgr(iService):
             tmpfile.close()
             sc = self.readOptions(tmpfilename)
             if sc.isFailure():
-                raise RuntimeError, ' Unable to read file "' + tmpfilename + '" '
+                raise RuntimeError(' Unable to read file "' + tmpfilename + '" ')
             os.remove(tmpfilename)
         # We need to make sure that the options are taken by the ApplicationMgr
         # The state is already configured, so we need to do something....
@@ -1343,7 +1360,7 @@ class AppMgr(iService):
 def _getFIDandEvents(pfn):
     tfile = gbl.TFile.Open(pfn)
     if not tfile:
-        raise 'Cannot open ROOT file ', pfn
+        raise IOError('Cannot open ROOT file {0}'.format(pfn))
     tree = tfile.Get('##Params')
     tree.GetEvent(0)
     text = tree.db_string
@@ -1363,14 +1380,14 @@ def getComponentProperties(name):
     properties = {}
     if name == 'GaudiCoreSvc':
         if Helper.loadDynamicLib(name) != 1:
-            raise ImportError,  'Error loading component library ' + name
+            raise ImportError('Error loading component library ' + name)
         factorylist = gbl.FactoryTable.instance().getEntries()
         factories = _copyFactoriesFromList(factorylist)
         g = AppMgr(outputlevel=7)
     else:
         g = AppMgr(outputlevel=7)
         if Helper.loadDynamicLib(name) != 1:
-            raise ImportError,  'Error loading component library ' + name
+            raise ImportError('Error loading component library ' + name)
         factorylist = gbl.FactoryTable.instance().getEntries()
         factories = _copyFactoriesFromList(factorylist)
     svcloc = gbl.Gaudi.svcLocator()
@@ -1393,9 +1410,9 @@ def getComponentProperties(name):
                     obj = factory.instantiate(dummysvc)
                 else:
                     obj = factory.instantiate(svcloc)
-            except RuntimeError, text:
-                print 'Error instantiating', cname, ' from ', name
-                print text
+            except RuntimeError as text:
+                print('Error instantiating', cname, ' from ', name)
+                print(text)
                 continue
             prop = iProperty('dummy', obj)
             properties[cname] = [ctype, prop.properties()]
@@ -1448,7 +1465,7 @@ class PyAlgorithm (_PyAlgorithm):
         self._algmgr = InterfaceCast(gbl.IAlgManager)(self._svcloc)
         sc = self._algmgr.addAlgorithm(self)
         if sc.isFailure():
-            raise RuntimeError, 'Unable to add Algorithm'
+            raise RuntimeError('Unable to add Algorithm')
 
     def __del__(self):
         sc = self._algmgr.removeAlgorithm(self)
diff --git a/GaudiPython/python/GaudiPython/GaudiAlgs.py b/GaudiPython/python/GaudiPython/GaudiAlgs.py
index 6558cd677b0e729971a207beb6a2b19fab3a7db8..441ff723b23d854e2c7605c323b73092b9ebff53 100644
--- a/GaudiPython/python/GaudiPython/GaudiAlgs.py
+++ b/GaudiPython/python/GaudiPython/GaudiAlgs.py
@@ -1,4 +1,5 @@
 #!/usr/bin/env python
+from __future__ import print_function
 # =============================================================================
 # @file
 #
@@ -209,12 +210,12 @@ def _init_(self, name, **args):
     algMgr = appMgr._algmgr
     status = algMgr.addAlgorithm(self)
     if status.isFailure():
-        raise RuntimeError, 'Unable to add Algorithm "' + name + '"'
+        raise RuntimeError('Unable to add Algorithm "' + name + '"')
     iAlgorithm.__init__(self, name, self)
     for key in args:
         setattr(self, key, args[key])
     # take some care about the ownership of the algorithms
-    if not appMgr.__dict__.has_key('GaudiPythonAlgos'):
+    if 'GaudiPythonAlgos' not in appMgr.__dict__:
         appMgr.__dict__['GaudiPythonAlgos'] = []
     appMgr.__dict__['GaudiPythonAlgos'].append(self)
 
@@ -492,7 +493,7 @@ def _getProperty_(self, pname):
     Get the property by name
     """
     if not self.hasProperty(pname):
-        raise AttributeError, 'property %s does not exist' % pname
+        raise AttributeError('property %s does not exist' % pname)
     return iAlgorithm.__getattr__(self, pname)
 
 # =============================================================================
@@ -504,7 +505,7 @@ def _setProperty_(self, pname, pvalue):
     Set the property from the value
     """
     if not self.hasProperty(pname):
-        raise AttributeError, 'property %s does not exist' % pname
+        raise AttributeError('property %s does not exist' % pname)
     return iAlgorithm.__setattr__(self, pname, pvalue)
 
 # =============================================================================
@@ -518,7 +519,7 @@ def _get_attr_(self, pname):
     """
     if self.hasProperty(pname):
         return iAlgorithm.__getattr__(self, pname)
-    raise AttributeError, 'attribute/property %s does not exist' % pname
+    raise AttributeError('attribute/property %s does not exist' % pname)
 
 # =============================================================================
 # set the attribute or property
@@ -895,7 +896,7 @@ class objectmethod(object):
         self.method = m
 
     def __call__(self, *args):
-        print args
+        print(args)
         return self.method(*args)
 
 
@@ -926,7 +927,7 @@ def _execute_(self):
     """
     The fictive 'execute' method, which MUST be overwitten by user
     """
-    raise RuntimeError, 'Execute method is not implemented for %s' % self.name()
+    raise RuntimeError('Execute method is not implemented for %s' % self.name())
 
 
 GaudiAlgo.execute = _execute_
@@ -1339,7 +1340,7 @@ def _get_all_tools_(self, method):
     _func = getattr(AlgDecorator, method)
     _num = _func(self, _tools)
     if _tools.size() != _num:
-        raise RuntimeError, 'Unable to extract Tools'
+        raise RuntimeError('Unable to extract Tools')
     _res = []
     for _tool in _tools:
         _res += [iAlgTool(_tool.name(), _tool)]
@@ -1355,7 +1356,7 @@ def _Tools_a_(self):
     >>> alg   = ...             ## get the algorithm
     >>> tools = alg.Tools()     ## get the tools
     >>> for tool in tools :
-    ...        print tool
+    ...        print(tool)
 
     """
     _cmp = getattr(self, '_ialg')
@@ -1374,7 +1375,7 @@ def _Tools_t_(self):
     >>> tool  = ...              ## get the tool
     >>> tools = tool.Tools()     ## get the tools
     >>> for t in tools :
-    ...        print t
+    ...        print(t)
 
     """
     _cmp = getattr(self, '_itool')
@@ -1397,7 +1398,7 @@ def _get_all_counters_(self, method, name=None):
     _func = getattr(AlgDecorator, method)
     _num = _func(self, _nams, _cnts)
     if _nams.size() != _num or _cnts.size() != _num:
-        raise RuntimeError, 'Unable to extract Counters'
+        raise RuntimeError('Unable to extract Counters')
     _res = {}
     for _i in range(0, _num):
         _nam = _nams[_i]
@@ -1417,14 +1418,14 @@ def _Counters_a_(self, name=None):
     >>> alg  = ...             ## get the algorithm
     >>> cnts = alg.Counters()  ## get the counters
     >>> for key in cnts :
-    ...        print key, cnts[key]
+    ...        print(key, cnts[key])
 
 
     Retrieve the counter, managed GaudiCommon<TYPE> base by name:
 
     >>> alg = ...                        ## get the algorithm
     >>> cnt = alg.Counters('MyCounter')  ## get the counter
-    >>> print cnt
+    >>> print(cnt)
 
     """
     _cmp = getattr(self, '_ialg')
@@ -1442,14 +1443,14 @@ def _Counters_t_(self, name=None):
     >>> tool = ...              ## get the tool
     >>> cnts = tool.Counters()  ## get the counters
     >>> for key in cnts :
-    ...        print key, cnts[key]
+    ...        print(key, cnts[key])
 
 
     Retrieve the counter, managed GaudiCommon<TYPE> base by name:
 
     >>> tool = ...                         ## get the tool
     >>> cnt  = tool.Counters('MyCounter')  ## get the counter
-    >>> print cnt
+    >>> print(cnt)
 
     """
     _cmp = getattr(self, '_itool')
@@ -1477,7 +1478,7 @@ def _Counter_a_(self, name):
 
     >>> alg  = ...                     ## get the algorithm
     >>> cnt  = alg.Counter('#accept')  ## get the counter
-    >>> print cnt
+    >>> print(cnt)
 
     """
     _cmp = getattr(self, '_ialg')
@@ -1494,7 +1495,7 @@ def _Counter_t_(self, name):
 
     >>> tool = ...                      ## get the tool
     >>> cnt  = tool.Counter('#accept')  ## get the counter
-    >>> print cnt
+    >>> print(cnt)
 
     """
     _cmp = getattr(self, '_itool')
@@ -1529,7 +1530,7 @@ def _get_all_histos_(component, method, name):
         _fun = getattr(HistoDecorator, method)
         _num = _fun(component, _ids, _his)
         if _ids.size() != _num or _his.size() != _num:
-            raise RuntimeError, 'Unable to extract Histos!'
+            raise RuntimeError('Unable to extract Histos!')
         for _i in range(0, _num):
             _id = _ids[_i]
             if _id.numeric():
@@ -1563,13 +1564,13 @@ def _Histos_a_(self, name=None):
     >>> alg = ...              ## get the algorithm
     >>> histos = alg.Histos()  ## get all histograms & profiles
     >>> for key in histos :
-    ...        print key, histos[key]
+    ...        print(key, histos[key])
 
     Retrive the histogram with the certain ID :
 
     >>> alg = ...                           ## get the algorithm
     >>> histo = alg.Histos('some histo ID') ## get the histo by ID
-    >>> print histo
+    >>> print(histo)
 
     """
     _cmp = getattr(self, '_ialg')
@@ -1587,13 +1588,13 @@ def _Histos_t_(self, name=None):
     >>> tool = ...              ## get the tool
     >>> histos = tool.Histos()  ## get all histograms & profiles
     >>> for key in histos :
-    ...        print key, histos[key]
+    ...        print(key, histos[key])
 
     Retrive the historgam with certain ID :
 
     >>> tool = ...                           ## get the tool
     >>> histo = tool.Histos('some histo ID') ## get the histo by ID
-    >>> print histo
+    >>> print(histo)
 
     """
     _cmp = getattr(self, '_itool')
@@ -1631,10 +1632,10 @@ import GaudiPython.HistoUtils
 
 
 def _help_():
-    print __doc__, __author__
-    print '\t\t\tDoc-string for class GaudiAlgo \n', GaudiAlgo.__doc__
-    print '\t\t\tDoc-string for class HistoAlgo \n', HistoAlgo.__doc__
-    print '\t\t\tDoc-string for class TupleAlgo \n', TupleAlgo.__doc__
+    print(__doc__, __author__)
+    print('\t\t\tDoc-string for class GaudiAlgo \n', GaudiAlgo.__doc__)
+    print('\t\t\tDoc-string for class HistoAlgo \n', HistoAlgo.__doc__)
+    print('\t\t\tDoc-string for class TupleAlgo \n', TupleAlgo.__doc__)
 
 
 # =============================================================================
diff --git a/GaudiPython/python/GaudiPython/HistoUtils.py b/GaudiPython/python/GaudiPython/HistoUtils.py
index 0e154df1728522335cd19060a69254072432c19b..ffb90e65f890daf54141a7dad1ca9c0b14cbf4d5 100644
--- a/GaudiPython/python/GaudiPython/HistoUtils.py
+++ b/GaudiPython/python/GaudiPython/HistoUtils.py
@@ -1,4 +1,5 @@
 #!/usr/bin/env python
+from __future__ import print_function
 # =============================================================================
 # This module contains set of simple and useful utilities for booking and
 #   manipulations with Gaudi-AIDA histograms, inspired by Thomas' request
@@ -61,7 +62,7 @@ def _getAppMgr(**kwargs):
     if not gaudi:
         gaudi = AppMgr()
     if not gaudi:
-        raise RuntimeError, 'Unable to get valid ApplicationMgr'
+        raise RuntimeError('Unable to get valid ApplicationMgr')
 
     state = gaudi._isvc.FSMState()
     if state < cpp.Gaudi.StateMachine.CONFIGURED:
@@ -235,7 +236,7 @@ def book(*args, **kwargs):
 
     svc = _getHistoSvc(**kwargs)
     if not svc:
-        raise RuntimeError, 'Unable to get valid HistogramService '
+        raise RuntimeError('Unable to get valid HistogramService ')
     # book the histogram using the service
     return svc.book(*args)  # RETURN
 
@@ -315,7 +316,7 @@ def bookProf(*args, **kwargs):
     """
     svc = _getHistoSvc(**kwargs)
     if not svc:
-        raise RuntimeError, 'Unable to get valid HistogramService '
+        raise RuntimeError('Unable to get valid HistogramService ')
     # book the histogram using the service
     return svc.bookProf(*args)  # RETURN
 
@@ -340,7 +341,7 @@ def getAsAIDA(path, **kwargs):
     """
     svc = _getHistoSvc(**kwargs)
     if not svc:
-        raise RuntimeError, 'Unable to get valid HistogramService '
+        raise RuntimeError('Unable to get valid HistogramService ')
     # return the histogram
     return svc.getAsAIDA(path)  # RETURN
 
@@ -365,7 +366,7 @@ def getAsROOT(path, **kwargs):
     """
     svc = _getHistoSvc(**kwargs)
     if not svc:
-        raise RuntimeError, 'Unable to get valid HistogramService '
+        raise RuntimeError('Unable to get valid HistogramService ')
     # return the histogram
     return svc.getAsROOT(path)  # RETURN
 
@@ -521,7 +522,7 @@ def _moment_(self, order, value=0):
     for 1D histogram
 
     >>> h1 = ...
-    >>> print h1.moment ( 5 )
+    >>> print(h1.moment ( 5 ))
 
     """
     return HistoStats.moment(self, order, value)
@@ -536,7 +537,7 @@ def _momentErr_(self, order):
     for 1D histogram
 
     >>> h1 = ...
-    >>> print h1.momentErr ( 5 )
+    >>> print(h1.momentErr ( 5 ))
 
     """
     return HistoStats.momentErr(self, order)
@@ -550,7 +551,7 @@ def _centralMoment_(self, order):
     for 1D histogram
 
     >>> h1 = ...
-    >>> print h1.centralMoment ( 5 )
+    >>> print(h1.centralMoment ( 5 ))
 
     """
     return HistoStats.centralMoment(self, order)
@@ -565,7 +566,7 @@ def _centralMomentErr_(self, order):
     for 1D histogram
 
     >>> h1 = ...
-    >>> print h1.centralMomentErr ( 5 )
+    >>> print(h1.centralMomentErr ( 5 ))
 
     """
     return HistoStats.centralMomentErr(self, order)
@@ -579,7 +580,7 @@ def _skewness_(self):
     Evaluate 'bin-by-bin' skewness for 1D AIDA histogram
 
     >>> h1 = ...
-    >>> print h1.skewness()
+    >>> print(h1.skewness())
 
     """
     return HistoStats.skewness(self)
@@ -593,7 +594,7 @@ def _skewnessErr_(self):
     Evaluate error for 'bin-by-bin' skewness
 
     >>> h1 = ...
-    >>> print h1.skewnessErr()
+    >>> print(h1.skewnessErr())
 
     """
     return HistoStats.skewnessErr(self)
@@ -607,7 +608,7 @@ def _kurtosis_(self):
     Evaluate 'bin-by-bin' kurtosis
 
     >>> h1 = ...
-    >>> print h1.kurtosis ()
+    >>> print(h1.kurtosis ())
 
     """
     return HistoStats.kurtosis(self)
@@ -621,7 +622,7 @@ def _kurtosisErr_(self):
     Evaluate error for 'bin-by-bin' kurtotis for 1D AIDA histogram
 
     >>> h1 = ...
-    >>> print h1.kurtotisErr()
+    >>> print(h1.kurtotisErr())
 
     """
     return HistoStats.kurtosisErr(self)
@@ -763,13 +764,13 @@ def _nEntries_(self, i1, i2=-10000000):
     attention: underflow bin is included!
 
     >>> h1
-    >>> print h1.nEntries ( 10 )
+    >>> print(h1.nEntries ( 10 ))
 
     Get number of entries in histogram form the certain
     minimal bin up to the certain maximal bin (not-included)
 
     >>> h1
-    >>> print h1.nEntries ( 10 , 15 )
+    >>> print(h1.nEntries ( 10 , 15 ))
 
     """
     if i2 < i1 or i2 < 0:
@@ -785,13 +786,13 @@ def _nEntriesFrac_(self, i1, i2=-10000000):
     attention: underflow bin is included!
 
     >>> h1
-    >>> print h1.nEntriesFrac ( 10 )
+    >>> print(h1.nEntriesFrac ( 10 ))
 
     Get the fraction of entries in histogram form the certain
     minimal bin up to the certain maximal bin (not-included)
 
     >>> h1
-    >>> print h1.nEntriesFrac ( 10 , 15 )
+    >>> print(h1.nEntriesFrac ( 10 , 15 ))
 
     """
     if i2 < i1 or i2 < 0:
@@ -807,13 +808,13 @@ def _nEntriesFracErr_(self, i1, i2=-10000000):
     attention: underflow bin is included!
 
     >>> h1
-    >>> print h1.nEntriesFracErr( 10 )
+    >>> print(h1.nEntriesFracErr( 10 ))
 
     Get error  fraction of entries in histogram form the certain
     minimal bin up to the certain maximal bin (not-included)
 
     >>> h1
-    >>> print h1.nEntriesFracErr ( 10 , 15 )
+    >>> print(h1.nEntriesFracErr ( 10 , 15 ))
 
     """
     if i2 < i1 or i2 < 0:
@@ -884,7 +885,7 @@ def _path_(self):
     Get the path in THS for the given AIDA object:
 
     >>> aida =
-    >>> print aida.path()
+    >>> print(aida.path())
 
     """
     return cpp.Gaudi.Utils.Histos.path(self)
@@ -906,11 +907,11 @@ def __dumpHisto__(histo, *args):
     Dump the histogram/profile in text format (a'la HBOOK)
 
     >>> histo
-    >>> print dumpHisto ( histo )
+    >>> print(dumpHisto ( histo ))
 
-    >>> print histo.dump()
-    >>> print histo.dump( 20 , 20 )
-    >>> print histo.dump( 20 , 20 , True )
+    >>> print(histo.dump())
+    >>> print(histo.dump( 20 , 20 ))
+    >>> print(histo.dump( 20 , 20 , True ))
 
     Uses:
 
@@ -999,10 +1000,10 @@ class HistoFile:
 # =============================================================================
 if '__main__' == __name__:
     import sys
-    print __doc__
+    print(__doc__)
     for o in __all__:
-        print o
-        print sys.modules[__name__].__dict__[o].__doc__
+        print(o)
+        print(sys.modules[__name__].__dict__[o].__doc__)
 
 
 # =============================================================================
diff --git a/GaudiPython/python/GaudiPython/Pythonizations.py b/GaudiPython/python/GaudiPython/Pythonizations.py
index 648e7f1bc4bab3a791eec93c6d57cc97c09914ba..53916d43b8c587ca13d3ea0fe58fa0fcb5f99dec 100644
--- a/GaudiPython/python/GaudiPython/Pythonizations.py
+++ b/GaudiPython/python/GaudiPython/Pythonizations.py
@@ -1,5 +1,8 @@
 # File: GaudiPython/Pythonizations.py
 # Author: Pere Mato (pere.mato@cern.ch)
+from __future__ import print_function
+
+import six
 
 """ This Pythonizations module provides a number of useful pythonizations
     of adaptation of some classes.
@@ -11,7 +14,7 @@ try:
     from cppyy import gbl
 except ImportError:
     # FIXME: backward compatibility
-    print "# WARNING: using PyCintex as cppyy implementation"
+    print("# WARNING: using PyCintex as cppyy implementation")
     from PyCintex import gbl
 
 if not hasattr(gbl, 'ostream'):
@@ -155,7 +158,7 @@ if gbl.gROOT.GetVersionInt() <= 51800:
 #  @code
 #
 #    >>> m = ...  ## the map
-#    >>> for key in m : print key , m[key]
+#    >>> for key in m : print(key , m[key])
 #
 #  @endcode
 #  @see Gaudi::Utils::MapBase
@@ -174,7 +177,7 @@ def __mapbase_iter__(self):
     The iterator for MapBase-based containers
 
     >>> m = ...  ## the map
-    >>> for key in m : print key , m[key]
+    >>> for key in m : print(key , m[key])
 
     """
     _size = len(self)
@@ -189,7 +192,7 @@ def __mapbase_iter__(self):
 #  @code
 #
 #    >>> m = ...  ## the map
-#    >>> for key,value in m.iteritems() : print key , value
+#    >>> for key,value in m.iteritems() : print(key , value)
 #
 #  @endcode
 #  @see Gaudi::Utils::MapBase
@@ -211,7 +214,7 @@ def __mapbase_iteritems__(self):
     The iterator for MapBase-based containers
 
     >>> m = ...  ## the map
-    >>> for key,value in m.iteritems() : print key, value
+    >>> for key,value in m.iteritems() : print(key, value)
 
     """
     _size = len(self)
@@ -332,7 +335,7 @@ def __mapbase_values__(self):
 #  @code
 #
 #    >>> m      = ...        ## the map
-#    >>> if 'a' in m : print 'key is in the map!'
+#    >>> if 'a' in m : print('key is in the map!')
 #
 #  @endcode
 #  @see Gaudi::Utils::MapBase
@@ -396,7 +399,7 @@ def __mapbase_get__(self, key, value=None):
 #  @code
 #
 #    >>> m      = ...        ## the map
-#    >>> print m
+#    >>> print(m)
 #
 #  @endcode
 #  @see Gaudi::Utils::MapBase
@@ -412,7 +415,7 @@ def __mapbase_str__(self):
     Representation of MapBase-based maps:
 
     >>> m     = ...        ## the map
-    >>> print map
+    >>> print(map)
 
     """
     _result = ' { '
@@ -496,14 +499,19 @@ gbl.Gaudi.Utils.MapBase . __len__ = lambda s: s.size()
 gbl.Gaudi.Utils.MapBase . __iter__ = __mapbase_iter__
 gbl.Gaudi.Utils.MapBase .   keys = __mapbase_keys__
 gbl.Gaudi.Utils.MapBase . __iteritems__ = __mapbase_iteritems__
-gbl.Gaudi.Utils.MapBase .   iteritems = __mapbase_iteritems__
-gbl.Gaudi.Utils.MapBase .   items = __mapbase_items__
 gbl.Gaudi.Utils.MapBase .   values = __mapbase_values__
 gbl.Gaudi.Utils.MapBase . __contains__ = __mapbase_contains__
-gbl.Gaudi.Utils.MapBase .   has_key = __mapbase_contains__
 gbl.Gaudi.Utils.MapBase .   get = __mapbase_get__
 gbl.Gaudi.Utils.MapBase . __str__ = __mapbase_str__
 gbl.Gaudi.Utils.MapBase . __repr__ = __mapbase_str__
 gbl.Gaudi.Utils.MapBase . __setitem__ = __mapbase_setitem__
 gbl.Gaudi.Utils.MapBase . __delitem__ = __mapbase_delitem__
 gbl.Gaudi.Utils.MapBase . __getitem__ = lambda s, key: s.at(key)
+if six.PY2:
+    # Behaviour is like Python 2 dict
+    gbl.Gaudi.Utils.MapBase .   iteritems = __mapbase_iteritems__
+    gbl.Gaudi.Utils.MapBase .   items = __mapbase_items__
+    gbl.Gaudi.Utils.MapBase .   has_key = __mapbase_contains__
+else:
+    # Behaviour is like Python 3+ dict
+    gbl.Gaudi.Utils.MapBase .   items = __mapbase_iteritems__
diff --git a/GaudiPython/python/GaudiPython/TupleUtils.py b/GaudiPython/python/GaudiPython/TupleUtils.py
index ad8a5662312d43307fbfd259fb965c454472fd99..3272075eb2c6767ca6a9c23d8a5eb468b89ca7e4 100644
--- a/GaudiPython/python/GaudiPython/TupleUtils.py
+++ b/GaudiPython/python/GaudiPython/TupleUtils.py
@@ -1,4 +1,5 @@
 #!/usr/bin/env python
+from __future__ import print_function
 # =============================================================================
 # This module contains set of simple and useful utilities to booking and
 #  manipulation with N-Tuples (in the spirit of GaudiTuples<TYPE>)
@@ -198,8 +199,8 @@ def _TupleUtils_AtExit_():
     AtExit function for GaudiPython.TupleUtils module
     """
     if activeTuples():
-        print 'WARNING: the list of local TupleTools is not empty!'
-        print 'WARNING: please use GaudiPython.TupleUtils.releaseTuples() at the end'
+        print('WARNING: the list of local TupleTools is not empty!')
+        print('WARNING: please use GaudiPython.TupleUtils.releaseTuples() at the end')
 
 
 import atexit
@@ -208,10 +209,10 @@ atexit.register(_TupleUtils_AtExit_)
 # =============================================================================
 if "__main__" == __name__:
     import sys
-    print __doc__, __all__
+    print(__doc__, __all__)
     for o in __all__:
-        print "\n\n\t", o, "\n"
-        print sys.modules[__name__].__dict__[o].__doc__
+        print("\n\n\t", o, "\n")
+        print(sys.modules[__name__].__dict__[o].__doc__)
 
 # =============================================================================
 # The end
diff --git a/GaudiPython/python/GaudiPython/__init__.py b/GaudiPython/python/GaudiPython/__init__.py
index 457d8e0850d80addc2402b7a7697beff99162f61..e52ffb8302518c15e260b9c638ced8c3f6537cf8 100644
--- a/GaudiPython/python/GaudiPython/__init__.py
+++ b/GaudiPython/python/GaudiPython/__init__.py
@@ -1,5 +1,6 @@
 # File: GaudiPython/__init__.py
 # Author: Pere Mato (pere.mato@cern.ch)
+from __future__ import absolute_import, print_function
 
 """
    GaudiPython main module.
@@ -12,8 +13,8 @@
 # ensure that we (and the subprocesses) use the C standard localization
 import os
 if os.environ.get('LC_ALL') != 'C':
-    print '# setting LC_ALL to "C"'
+    print('# setting LC_ALL to "C"', flush=True)
     os.environ['LC_ALL'] = 'C'
 
-from Bindings import *
-from Pythonizations import *
+from .Bindings import *
+from .Pythonizations import *
diff --git a/GaudiPython/src/Lib/Algorithm.cpp b/GaudiPython/src/Lib/Algorithm.cpp
index 2c5e6b500c098bb4fd5e966afea3907eab8af3bf..20bc44aee12c72785278faa11c2ea8a9478acb9f 100644
--- a/GaudiPython/src/Lib/Algorithm.cpp
+++ b/GaudiPython/src/Lib/Algorithm.cpp
@@ -2,6 +2,16 @@
 // Include files
 // ============================================================================
 #include "Python.h"
+
+// Python 3 compatibility
+#if PY_MAJOR_VERSION >= 3
+
+#define PyInt_Check PyLong_Check
+
+#define PyInt_AS_LONG PyLong_AS_LONG
+
+#endif
+
 // ============================================================================
 // GaudiKernel
 // ============================================================================
diff --git a/GaudiPython/src/Services/PythonScriptingSvc.cpp b/GaudiPython/src/Services/PythonScriptingSvc.cpp
index 261dcba51c4704f905adfde962e8b8e64342f15e..ced7bff0816386671ca1b2fd9d7dfc18ede5b8e7 100644
--- a/GaudiPython/src/Services/PythonScriptingSvc.cpp
+++ b/GaudiPython/src/Services/PythonScriptingSvc.cpp
@@ -1,5 +1,16 @@
 #include "Python.h"
 
+// Python 3 compatibility
+#if PY_MAJOR_VERSION >= 3
+
+#define PySys_SetArgv_Char_t wchar_t
+
+#else
+
+#define PySys_SetArgv_Char_t char
+
+#endif
+
 // Include Files
 #include "GaudiKernel/ISvcLocator.h"
 #include "GaudiKernel/MsgStream.h"
@@ -55,7 +66,7 @@ StatusCode PythonScriptingSvc::initialize()
     }
   }
 
-  char* progName[] = {const_cast<char*>( "GaudiPython" )};
+  PySys_SetArgv_Char_t* progName[] = {const_cast<PySys_SetArgv_Char_t*>( L"GaudiPython" )};
 
   // Initialize the Python interpreter.  Required.
   Py_Initialize();
diff --git a/GaudiRelease/prepare_gaudi_release.py b/GaudiRelease/prepare_gaudi_release.py
index 5ea0147fc2f11fa75891ce55017bdfcc9135fcfc..e15aa0c07a49eda834a6a89eed6d381ded58b58d 100755
--- a/GaudiRelease/prepare_gaudi_release.py
+++ b/GaudiRelease/prepare_gaudi_release.py
@@ -4,7 +4,7 @@ Script to prepare the release of Gaudi.
 
 @author Marco Clemencic
 '''
-
+from __future__ import print_function
 import os
 import sys
 import logging
@@ -130,7 +130,7 @@ def main():
         m = re.match(r'^\s*set\(\s*heptools_version\s+(\S*)\s*\)', l)
         if m:
             HEPToolsVers = m.group(1)
-            print "Using HEPTools", HEPToolsVers
+            print("Using HEPTools", HEPToolsVers)
             break
     else:
         logging.error('Cannot find HEPTools version')
diff --git a/GaudiRelease/svn_tag_release.py b/GaudiRelease/svn_tag_release.py
index d9d9b37979f134ceb64e4c9ebedd5a94f4f4f662..44ec04023a0734411428d7800cdf4bb0e7713523 100755
--- a/GaudiRelease/svn_tag_release.py
+++ b/GaudiRelease/svn_tag_release.py
@@ -5,6 +5,7 @@ release of Gaudi.
 See https://twiki.cern.ch/twiki/bin/view/Gaudi/GaudiSVNRepository for a
 description of the repository structure.
 """
+from __future__ import print_function
 __author__ = "Marco Clemencic <Marco.Clemencic@cern.ch>"
 
 import os
@@ -17,8 +18,8 @@ from ConfigParser import ConfigParser
 
 
 def svn(*args, **kwargs):
-    print "> svn", " ".join(args)
-    return apply(Popen, (["svn"] + list(args),), kwargs)
+    print("> svn", " ".join(args))
+    return Popen(["svn"] + list(args), **kwargs)
 
 
 def svn_ls(url):
@@ -42,7 +43,7 @@ def svn_exists(url):
 def checkout_structure(url, proj, branch):
     def checkout_level(base):
         dirs = ["%s/%s" % (base, d) for d in svn_ls(base) if d.endswith("/")]
-        apply(svn, ["up", "-N"] + dirs).wait()
+        svn("up", "-N", *args).wait()
         return dirs
 
     root = basename(url)
diff --git a/GaudiSvc/scripts/dumpMetaData b/GaudiSvc/scripts/dumpMetaData
index bd2b14c764b73443a9dfc7428ea508e1b7025164..3d8d2c9560ed3f8b25495b6cb3c529c12ccff2e9 100755
--- a/GaudiSvc/scripts/dumpMetaData
+++ b/GaudiSvc/scripts/dumpMetaData
@@ -23,5 +23,5 @@ if __name__ == '__main__':
 
     try:
         pprint(getMetaData(filename))
-    except RuntimeError, x:
+    except RuntimeError as x:
         parser.error(str(x))
diff --git a/RootCnv/scripts/IOTest.py b/RootCnv/scripts/IOTest.py
index 9e6231ec029dd3c495b43e1ec637aa0989d23bba..6e9ab417d41f2ebfa0218a56fd9d149c10947946 100755
--- a/RootCnv/scripts/IOTest.py
+++ b/RootCnv/scripts/IOTest.py
@@ -10,6 +10,7 @@
    M.Frank  CERN/LHCb
 
 """
+from __future__ import print_function
 import os
 import sys
 import time
@@ -85,16 +86,16 @@ def update():
 def printDelta(s0, s1):
     for s in s1:
         if s == 'time':
-            print '%15s : %10.2F sec' % (s, (s1[s] - s0[s]))
+            print('%15s : %10.2F sec' % (s, (s1[s] - s0[s])))
         else:
-            print '%15s : %10.2F MB' % (s, (s1[s] - s0[s]) / 1.E6)
+            print('%15s : %10.2F MB' % (s, (s1[s] - s0[s]) / 1.E6))
 
 
 import GaudiPython
 appMgr = GaudiPython.AppMgr()
 sel = appMgr.evtsel()
 evt = appMgr.evtsvc()
-print sel.Input
+print(sel.Input)
 
 start = update()
 # test 1
@@ -104,13 +105,13 @@ while 1 > 0:
     if N == 10:
         start = update()
     # if not evt['/Event/DAQ/RawEvent']:
-    #    print 'Failed to access /Event/DAQ/RawEvent'
+    #    print('Failed to access /Event/DAQ/RawEvent')
     if not evt['/Event/Rec/Header']:
-        print 'Failed to access /Event/Rec/Header'
+        print('Failed to access /Event/Rec/Header')
         break
     N += 1
     if N > 100000:
         break
 end = update()
-print 'Read %d events' % N
+print('Read %d events' % N)
 printDelta(start, end)
diff --git a/cmake/GaudiProjectConfig.cmake b/cmake/GaudiProjectConfig.cmake
index 313eab30fa8ffb9623ee4e854c90ca7f7d9794e3..3c85bd8b4d629113e65b207bac99b8ce03c65633 100644
--- a/cmake/GaudiProjectConfig.cmake
+++ b/cmake/GaudiProjectConfig.cmake
@@ -198,7 +198,16 @@ include(CMakeParseArguments)
 include(CMakeFunctionalUtils)
 include(BinaryTagUtils)
 
-find_package(PythonInterp 2.7)
+find_package(PythonInterp)
+
+# Need to use a version-dependent Boost Python library
+if(${PYTHON_VERSION_MAJOR} EQUAL "3")
+  set(BOOST_PYTHON_LIB_NAME "python3")
+  set(BOOST_PYTHON_LIB_FOUND "YES")
+elseif(${PYTHON_VERSION_MAJOR} EQUAL "2")
+  set(BOOST_PYTHON_LIB_NAME "python2")
+  set(BOOST_PYTHON_LIB_FOUND "YES")
+endif()
 
 #-------------------------------------------------------------------------------
 # gaudi_project(project version
@@ -735,8 +744,11 @@ __path__ = [d for d in [os.path.join(d, '${pypack}') for d in sys.path if d]
             if (d.startswith('${CMAKE_BINARY_DIR}') or
                 d.startswith('${CMAKE_SOURCE_DIR}')) and
                (os.path.exists(d) or 'python.zip' in d)]
-if os.path.exists('${CMAKE_SOURCE_DIR}/${package}/python/${pypack}/__init__.py'):
-    execfile('${CMAKE_SOURCE_DIR}/${package}/python/${pypack}/__init__.py')
+fname = '${CMAKE_SOURCE_DIR}/${package}/python/${pypack}/__init__.py'
+if os.path.exists(fname):
+    with open(fname) as f:
+        code = compile(f.read(), fname, 'exec')
+        exec(code)
 ")
       endforeach()
     endif()
@@ -2707,6 +2719,7 @@ function(gaudi_install_python_modules)
   file(GLOB sub-dir RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} python/*)
   foreach(dir ${sub-dir})
     if(NOT dir STREQUAL python/.svn
+       AND NOT dir MATCHES "__pycache__"
        AND IS_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/${dir}
        AND NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${dir}/__init__.py)
       set(pyfile ${CMAKE_CURRENT_SOURCE_DIR}/${dir}/__init__.py)
@@ -2738,6 +2751,7 @@ function(gaudi_install_scripts)
           PATTERN "CVS" EXCLUDE
           PATTERN ".svn" EXCLUDE
           PATTERN "*~" EXCLUDE
+          PATTERN "__pycache__" EXCLUDE
           PATTERN "*.pyc" EXCLUDE)
 endfunction()
 
diff --git a/cmake/cmakeLogToCacheGrind.py b/cmake/cmakeLogToCacheGrind.py
index b3ae5cdb6af02503cf6ae92ed0e8f89b04a8dad2..8617fafa10e954a6cdc039ee2865f025282bce13 100644
--- a/cmake/cmakeLogToCacheGrind.py
+++ b/cmake/cmakeLogToCacheGrind.py
@@ -5,12 +5,12 @@ Gaudi cmake process (See Instrument.cmake) and create an other file
 understandable by kcachegrind for easy visualization of where we spend time in
 cmake.
 '''
+from __future__ import print_function
 import sys
 
 
 def usage():
-    print "Invalid arguments\nProper syntax is :\n  %s <log file> <callgrind file>" % sys.argv[
-        0]
+    print("Invalid arguments\nProper syntax is :\n  %s <log file> <callgrind file>" % sys.argv[0])
 
 
 if len(sys.argv) != 3:
@@ -45,7 +45,7 @@ for line in open(sys.argv[1]).readlines():
     elif key == 'ENDTIME':
         sfunc, stime = callStack.pop()
         if sfunc != func:
-            print 'Mismatch START/END for %s/%s' % (sfunc, func)
+            print('Mismatch START/END for %s/%s' % (sfunc, func))
             sys.exit()
         deltatime = int(time) - stime
         # add time spent to this function
diff --git a/cmake/extract_qmtest_metadata.py b/cmake/extract_qmtest_metadata.py
index 3a7c5bff94e5c89ecc8d126fdcb4d627ee8f7951..1bcd78d7f3389a575cc5c6c12f10823fd45b7c42 100755
--- a/cmake/extract_qmtest_metadata.py
+++ b/cmake/extract_qmtest_metadata.py
@@ -81,8 +81,8 @@ def analyze_deps(pkg, rootdir):
         prereqs = [fix_test_name(el.text, pkg)
                    for el in tree.findall(prereq_xpath)]
         if prereqs:
-            print ('set_property(TEST {0} APPEND PROPERTY DEPENDS {1})'
-                   .format(name, ' '.join(prereqs)))
+            print('set_property(TEST {0} APPEND PROPERTY DEPENDS {1})'
+                  .format(name, ' '.join(prereqs)))
 
 
 def analyze_suites(pkg, rootdir):
@@ -110,13 +110,13 @@ def analyze_suites(pkg, rootdir):
 
     # transpose the dictionary of lists
     test_labels = collections.defaultdict(set)
-    for label, tests in labels.iteritems():
+    for label, tests in labels.items():
         for test in tests:
             test_labels[test].add(label)
 
-    for test, labels in test_labels.iteritems():
-        print ('set_property(TEST {0} APPEND PROPERTY LABELS {1})'
-               .format(test, ' '.join(labels)))
+    for test, labels in test_labels.items():
+        print('set_property(TEST {0} APPEND PROPERTY LABELS {1})'
+              .format(test, ' '.join(labels)))
 
 
 def analyze_disabling(pkg, rootdir):
@@ -138,8 +138,8 @@ def analyze_disabling(pkg, rootdir):
                      for el in tree.findall(unsupp_xpath)
                      if re.search(el.text, platform_id)]
         if skip_test:
-            print ('set_property(TEST {0} APPEND PROPERTY LABELS disabled)'
-                   .format(name))
+            print('set_property(TEST {0} APPEND PROPERTY LABELS disabled)'
+                  .format(name))
 
 
 if __name__ == '__main__':
diff --git a/cmake/get_host_binary_tag.py b/cmake/get_host_binary_tag.py
index 434ee2c112c80ffa188cd8edddbc6f7b492873e7..52d695c3f97b395ba55fa4928c09f72efd5c2916 100755
--- a/cmake/get_host_binary_tag.py
+++ b/cmake/get_host_binary_tag.py
@@ -62,8 +62,8 @@ def _compiler_version(cmd=os.environ.get('CC', 'cc')):
     # prevent interference from localization
     env = dict(os.environ)
     env['LC_ALL'] = 'C'
-    m = re.search(r'(gcc|clang|icc|LLVM) version (\d+)\.(\d+)',
-                  check_output([cmd, '-v'], stderr=STDOUT, env=env))
+    output = check_output([cmd, '-v'], stderr=STDOUT, env=env).decode('utf-8')
+    m = re.search(r'(gcc|clang|icc|LLVM) version (\d+)\.(\d+)', output)
     if not m:  # prevent crashes if the compiler is not supported
         return 'unknown'
     comp = 'clang' if m.group(1) == 'LLVM' else m.group(1)
diff --git a/cmake/modules/scan_dict_deps.py b/cmake/modules/scan_dict_deps.py
index c59b6bb3cbfd184997edb38823336d2715e9872e..da012c846275cd79bcb8eacc3190eed4474d70a5 100755
--- a/cmake/modules/scan_dict_deps.py
+++ b/cmake/modules/scan_dict_deps.py
@@ -1,7 +1,12 @@
 #!/usr/bin/env python
 import re
 
-from itertools import imap, ifilter
+try:
+    from itertools import imap, ifilter
+except ImportError:
+    # In Python 3, `map` and `filter` return iterables
+    imap = map
+    ifilter = filter
 from os.path import join, exists, isabs
 
 
@@ -16,8 +21,8 @@ def find_file(filename, searchpath):
     if isabs(filename):
         return filename if exists(filename) else None
     try:
-        return ifilter(exists, imap(lambda x: join(x, filename),
-                                    searchpath)).next()
+        return next(ifilter(exists, imap(lambda x: join(x, filename),
+                                        searchpath)))
     except StopIteration:
         return None
 
@@ -76,7 +81,7 @@ def main():
     if new_deps != old_deps:  # write it only if it has changed
         open(output, 'w').write(new_deps)
         if old_deps:
-            print 'info: dependencies changed: next build will trigger a reconfigure'
+            print('info: dependencies changed: next build will trigger a reconfigure')
 
 
 if __name__ == '__main__':
diff --git a/cmake/tests/cmake_coverage.py b/cmake/tests/cmake_coverage.py
index fea87d997c4cd3282303ece5a790ff1a2ec47e3b..2d216e893d8da5f82c51ecce3fbd485d252172e9 100755
--- a/cmake/tests/cmake_coverage.py
+++ b/cmake/tests/cmake_coverage.py
@@ -1,9 +1,13 @@
 #!/usr/bin/env python
 # -*- coding: utf-8 -*-
+from __future__ import print_function
 import os
 import re
 import atexit
-import cPickle
+try:
+    import cPickle
+except ImportError:
+    import pickle as cPickle
 from subprocess import Popen, PIPE
 from collections import defaultdict
 
@@ -14,7 +18,7 @@ coverage = defaultdict(set)
 
 
 def update_coverage():
-    # print 'Updating CMake coverage reports'
+    # print('Updating CMake coverage reports')
     if os.path.exists(COVERAGE_FILE):
         data = cPickle.load(open(COVERAGE_FILE))
         if 'lines' not in data:
@@ -22,7 +26,7 @@ def update_coverage():
     else:
         data = {'lines': {}}
     lines = data['lines']
-    for filename, linenumbers in coverage.iteritems():
+    for filename, linenumbers in coverage.items():
         lines[filename] = sorted(linenumbers.union(lines.get(filename, [])))
     with open(COVERAGE_FILE, 'w') as report:
         cPickle.dump(data, report)
@@ -35,6 +39,7 @@ def cmake_script(name, cwd=None):
     proc = Popen(['cmake', '--trace', '-P', name],
                  stdout=PIPE, stderr=PIPE, cwd=cwd)
     out, err = proc.communicate()
+    out, err = out.decode('utf-8'), err.decode('utf-8')
     trace_line = re.compile(r'^(/.*)\(([0-9]+)\): ')
     new_err = []
     for line in err.splitlines():
@@ -55,7 +60,7 @@ def get_ranges(numbers):
     from itertools import groupby
     from operator import itemgetter
     for _key, group in groupby(enumerate(numbers),
-                               lambda (index, number): number - index):
+                               lambda index, number: number - index):
         group = map(itemgetter(1), group)
         yield group[0], group[-1]
 
@@ -82,9 +87,9 @@ if __name__ == '__main__':
     lines = data['lines']
     for filename in sorted(lines):
         if not os.path.exists(filename):
-            print 'Unknown file', filename
+            print('Unknown file', filename)
             continue
-        print filename
+        print(filename)
         active_lines = set(get_active_lines(filename))
         touched_lines = set(lines[filename])
         missed_lines = active_lines.difference(touched_lines)
@@ -93,8 +98,8 @@ if __name__ == '__main__':
         touched_count = len(touched_lines)
         active_count = len(active_lines)
         if touched_count == active_count:
-            print '   coverage 100%'
+            print('   coverage 100%')
         else:
-            print ('   coverage %3d%%, missed: %s' %
-                   (float(touched_count) / active_count * 100,
-                    ', '.join(ranges)))
+            print('   coverage %3d%%, missed: %s' %
+                  (float(touched_count) / active_count * 100,
+                   ', '.join(ranges)))
diff --git a/cmake/tests/cmake_test_utils.py b/cmake/tests/cmake_test_utils.py
index 05a914b9ac80dba9e12e375ccadc247006c9dba1..36c7ddfe1aa2950e5b6827b8892d48333b33a033 100644
--- a/cmake/tests/cmake_test_utils.py
+++ b/cmake/tests/cmake_test_utils.py
@@ -1,6 +1,6 @@
 #!/usr/bin/env python
 # -*- coding: utf-8 -*-
-
+from __future__ import print_function
 from cmake_coverage import cmake_script
 from os.path import join, curdir
 
@@ -21,10 +21,10 @@ class CMakeTestScripts(object):
     def run(self, name):
         script_name = join(self.scripts_dir, 'test_%s.cmake' % name)
         out, err, returncode = cmake_script(script_name, cwd=self.base_dir)
-        print "---------- stdout ----------"
-        print out
-        print "---------- stderr ----------"
-        print err
+        print("---------- stdout ----------")
+        print(out)
+        print("---------- stderr ----------")
+        print(err)
         assert returncode == 0
 
     def test_scripts(self):
diff --git a/cmake/tests/test_LBCORE_716.py b/cmake/tests/test_LBCORE_716.py
index 05dcb3c7315a6fb9c149922319de12d14f249513..66a881846bc93df2d8b37dada10a2bf7a4bdee3f 100644
--- a/cmake/tests/test_LBCORE_716.py
+++ b/cmake/tests/test_LBCORE_716.py
@@ -29,6 +29,7 @@ def build():
     build_proc = Popen(['make', 'VERBOSE=1'], cwd=base_dir,
                        stdout=PIPE, stderr=PIPE)
     build_log, build_err = build_proc.communicate()
+    build_log, build_err = build_log.decode('utf-8'), build_err.decode('utf-8')
     build_returncode = build_proc.returncode
 
 
@@ -61,7 +62,8 @@ def test_env():
     getenv = Popen([xenv_cmd, '--xml', envfile,
                     'printenv', 'ROOT_INCLUDE_PATH'],
                    stdout=PIPE, stderr=PIPE)
-    out, _err = getenv.communicate()
+    out, _ = getenv.communicate()
+    out = out.decode('utf-8')
     assert getenv.returncode == 0, getenv.returncode
 
     root_inc_path = [os.path.relpath(l, base_dir)[0]
diff --git a/cmake/tests/test_crashes.py b/cmake/tests/test_crashes.py
index 0181c7ca1e20f6f3f3846e8b1113240cadaf7952..120ef02a05c7fd29417c72bf9ea12d0b0a919e43 100644
--- a/cmake/tests/test_crashes.py
+++ b/cmake/tests/test_crashes.py
@@ -1,5 +1,5 @@
 # -*- coding: utf-8 -*-
-
+from __future__ import print_function
 from cmake_coverage import cmake_script
 from os.path import join, dirname
 
@@ -10,10 +10,10 @@ scripts_dir = join(base_dir, 'cmake_scripts')
 def test_loops():
     script_name = join(scripts_dir, 'test_loops.cmake')
     out, err, returncode = cmake_script(script_name, cwd=base_dir)
-    print "---------- stdout ----------"
-    print out
-    print "---------- stderr ----------"
-    print err
+    print("---------- stdout ----------")
+    print(out)
+    print("---------- stderr ----------")
+    print(err)
     assert returncode != 0
     assert 'Infinite recursion detected at project Loop' in err
 
@@ -21,9 +21,9 @@ def test_loops():
 def test_missing_base():
     script_name = join(scripts_dir, 'test_missing_base.cmake')
     out, err, returncode = cmake_script(script_name, cwd=base_dir)
-    print "---------- stdout ----------"
-    print out
-    print "---------- stderr ----------"
-    print err
+    print("---------- stdout ----------")
+    print(out)
+    print("---------- stderr ----------")
+    print(err)
     assert returncode != 0
     assert 'Cannot find project IDoNotExist v0r0' in err
diff --git a/toolchain.cmake b/toolchain.cmake
index f907284f955ed35f4cfc128eb55cede79ab3fc06..622ef62ef8b01ce4953e7c999d18a9b9e7047b6c 100644
--- a/toolchain.cmake
+++ b/toolchain.cmake
@@ -1,5 +1,5 @@
 # Special wrapper to load the declared version of the heptools toolchain.
-set(heptools_version 94)
+set(heptools_version 94python3)
 
 cmake_minimum_required(VERSION 3.6)
 if($ENV{HEPTOOLS_VERSION})