diff --git a/CMakeLists.txt b/CMakeLists.txt
index 7b76c4eeabb796d74a31491335e6be9ab53c2a2b..3908e11f66c7972fc6ef99b74105f36049e2fc22 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -177,7 +177,7 @@ if (ENABLE_STATIC AND UNIX AND NOT APPLE)
 endif()
 
 # Python needed before Boost
-find_package(Python 2.4 REQUIRED)
+find_package(Python 3.6 REQUIRED)
 # Split version string
 string(REPLACE "." ";" _python_version_list ${PYTHON_VERSION})
 list(GET _python_version_list 0 PYTHON_VERSION_MAJOR)
diff --git a/cmake_support/FindPython.cmake b/cmake_support/FindPython.cmake
index 673a802199b94de7b43a08b38a549eec275ae85e..3a2e690de0c6974211438d341749104ca392b691 100644
--- a/cmake_support/FindPython.cmake
+++ b/cmake_support/FindPython.cmake
@@ -17,7 +17,7 @@
 # Author: Marco Biasini
 #-------------------------------------------------------------------------------
 
-set(PYTHON_VERSIONS 2.7 2.6 2.5 2.4 2.3 2.2 )
+set(PYTHON_VERSIONS 3.6 2.7 2.6 2.5 2.4 2.3 2.2 )
 set(PYTHON_MIN_VERSION 2.2.1)
 
 #-------------------------------------------------------------------------------
diff --git a/cmake_support/OST.cmake b/cmake_support/OST.cmake
index 31c304804c033fc6f4cb142c726c618a45db1138..46c511d58328e95b52c3c0eecf2d076d123930a3 100644
--- a/cmake_support/OST.cmake
+++ b/cmake_support/OST.cmake
@@ -917,10 +917,10 @@ macro(setup_boost)
   #              python${PYTHON_VERSION_MAJOR}${PYTHON_VERSION_MINOR} REQUIRED)
   # set(BOOST_PYTHON_LIBRARIES ${Boost_LIBRARIES})
   # see https://cmake.org/cmake/help/v3.11/module/FindBoost.html
-  foreach(_python_lib_name python
-                           python${PYTHON_VERSION_MAJOR}${PYTHON_VERSION_MINOR}
+  foreach(_python_lib_name python${PYTHON_VERSION_MAJOR}${PYTHON_VERSION_MINOR}
                            python${PYTHON_VERSION_MAJOR}.${PYTHON_VERSION_MINOR}
-                           python${PYTHON_VERSION_MAJOR})
+                           python${PYTHON_VERSION_MAJOR}
+                           python)
     find_package(Boost ${_BOOST_MIN_VERSION} COMPONENTS ${_python_lib_name} QUIET)
     if(Boost_FOUND)
       message(STATUS "Found Boost package: " ${_python_lib_name})
@@ -1004,4 +1004,4 @@ macro(ost_action ACTION TARGET)
     set(_ACTION_NAMES "${stripped_action}")
   endif()
   set(OST_ACTION_NAMES "${_ACTION_NAMES}" CACHE INTERNAL "" FORCE)
-endmacro(ost_action)
\ No newline at end of file
+endmacro(ost_action)
diff --git a/deployment/macos/deps.py b/deployment/macos/deps.py
index 91ef55c9ada53c569dcf76fde350f57f8161f9e6..63fb51cf797d082f90433e535f6255fec6ae4501 100644
--- a/deployment/macos/deps.py
+++ b/deployment/macos/deps.py
@@ -37,7 +37,7 @@ def collect_deps(stage_dir, components, binaries, libexec_binaries,
     lib_name=os.path.abspath(os.path.join(stage_dir, 'lib', 
                                           _lib_name(component)))  
     if not os.path.exists(lib_name):
-      print 'WARNING:', lib_name, 'does not exist'
+      print('WARNING:', lib_name, 'does not exist')
     if lib_name not in pool:
       _deps_for_lib(lib_name, pool)
       pool.add(lib_name)    
@@ -45,7 +45,7 @@ def collect_deps(stage_dir, components, binaries, libexec_binaries,
     bin_name=os.path.abspath(os.path.join(stage_dir, 'bin', 
                                           bin))  
     if not os.path.exists(bin_name):
-      print 'WARNING:', bin_name, 'does not exist'
+      print('WARNING:', bin_name, 'does not exist')
       continue
     if bin_name not in pool:
       _deps_for_lib(bin_name, pool)
@@ -53,15 +53,15 @@ def collect_deps(stage_dir, components, binaries, libexec_binaries,
     bin_name=os.path.abspath(os.path.join(stage_dir, 'libexec/openstructure',
                                           bin))
     if not os.path.exists(bin_name):
-      print 'WARNING:', bin_name, 'does not exist'
+      print('WARNING:', bin_name, 'does not exist')
       continue
     if bin_name not in pool:
       _deps_for_lib(bin_name, pool)
   for site_package in site_packages:
     full_path=get_python_module_path(site_package)
-    print full_path
+    print(full_path)
     if not os.path.exists(full_path):
-      print 'WARNING:', site_package, 'does not exists'
+      print('WARNING:', site_package, 'does not exists')
       continue
     if os.path.isdir(full_path):
       for so_file in glob.glob(os.path.join(full_path, '*.so')):
@@ -99,7 +99,7 @@ def copy_binaries(stage_dir, outdir, binary_names, scripts, bin_dir,
     else:
       bin_name=os.path.join(stage_dir, binary_name)
     if not os.path.exists(bin_name):
-      print 'WARNING:', binary_name, 'does not exist'
+      print('WARNING:', binary_name, 'does not exist')
       continue
     dst_name=os.path.join(outdir, bin_dir, os.path.basename(bin_name))
     shutil.copy(bin_name, dst_name)
@@ -123,14 +123,14 @@ def split_framework_components(abs_path):
         return lead, trail
 
 def change_id(id, lib):
-  os.chmod(lib, 0666)
+  os.chmod(lib, 0o666)
   os.system(CHANGE_ID_RPATH % (id,lib))
-  os.chmod(lib, 0444)
+  os.chmod(lib, 0o444)
 
 def update_load_commands(lib, exe, exe_path):
   direct_deps=set()
   _deps_for_lib(lib, direct_deps, recursive=False)
-  os.chmod(lib, 0666)
+  os.chmod(lib, 0o666)
   for direct_dep in direct_deps:
     if direct_dep.endswith('.dylib'):
       new_name=os.path.basename(direct_dep)
@@ -142,9 +142,9 @@ def update_load_commands(lib, exe, exe_path):
       new_name=os.path.join(framework_name, rel_path)
       os.system(CHANGE_LOAD_CMD_RPATH % (direct_dep, new_name, lib))
   if exe:
-    os.chmod(lib, 0555)
+    os.chmod(lib, 0o555)
   else:
-    os.chmod(lib, 0444)
+    os.chmod(lib, 0o444)
 
 def copy_deps(dependencies, outdir):
   exe_path=os.path.join(outdir, 'bin')
@@ -218,7 +218,7 @@ def make_standalone(stage_dir, outdir, no_includes, force_no_rpath=False,
   os.system('mkdir -p "%s/lib"' % outdir)
   os.system('mkdir -p "%s/bin"' % outdir)
   os.system('mkdir -p "%s/libexec/openstructure"' % outdir)
-  print 'copying shared datafiles'
+  print('copying shared datafiles')
   shutil.copytree(os.path.join(stage_dir, 'share'), 
                   os.path.join(outdir, 'share'))
   scripts=SCRIPTS
@@ -233,7 +233,7 @@ def make_standalone(stage_dir, outdir, no_includes, force_no_rpath=False,
     components+=GUI_COMPONENTS
     libexec_binaries+=GUI_LIBEXEC_BINARIES
     site_packages+=GUI_SITE_PACKAGES
-  print 'collecting dependencies'
+  print('collecting dependencies')
   deps=collect_deps(stage_dir, components, binaries, libexec_binaries,
                     site_packages, site_packages_dir)
   # when running in non-gui mode, we are most likely missing the boost
@@ -242,14 +242,14 @@ def make_standalone(stage_dir, outdir, no_includes, force_no_rpath=False,
   pymod_dir='lib/python%d.%d/site-packages' % sys.version_info[0:2]
   _deps_for_lib(os.path.join(stage_dir, pymod_dir, 'ost/_ost_base.so'),
                 deps, recursive=False)
-  print 'copying dependencies'
+  print('copying dependencies')
   copy_deps(deps, outdir)
-  print 'copying libexec binaries'
+  print('copying libexec binaries')
   copy_binaries(stage_dir, outdir, libexec_binaries, libexec_scripts,
                 'libexec/openstructure')
-  print 'copying binaries'
+  print('copying binaries')
   copy_binaries(stage_dir, outdir, binaries, scripts, 'bin')
-  print 'copying pymod'
+  print('copying pymod')
   shutil.copytree(os.path.join(stage_dir,pymod_dir),
                   os.path.join(outdir, pymod_dir))
   copied_py_framework=False
@@ -261,10 +261,10 @@ def make_standalone(stage_dir, outdir, no_includes, force_no_rpath=False,
   if len(glob.glob(os.path.join(outdir, 'lib', 'libpython*')))>0:
     non_std_python=True
   if non_std_python:
-    print 'looks like we are using a non-standard python.'
+    print('looks like we are using a non-standard python.')
     python_home=get_python_home()    
     if not copied_py_framework:
-      print 'also copying python modules from %s' % python_home
+      print('also copying python modules from %s' % python_home)
       modules_dst=os.path.join(outdir, 'lib', os.path.basename(python_home))
       shutil.copytree(python_home, modules_dst)
       if os.path.exists(os.path.join(modules_dst, 'site-packages')):
@@ -298,23 +298,23 @@ def make_standalone(stage_dir, outdir, no_includes, force_no_rpath=False,
           shutil.rmtree(directory)
     # replace the python executable
     ost_script=os.path.join(outdir, 'libexec', 'openstructure', 'ost_config')
-    os.chmod(ost_script, 0666)
+    os.chmod(ost_script, 0o666)
     script=''.join(open(ost_script, 'r').readlines())
     script=script.replace(python_bin, '$BIN_DIR/python')
     open(ost_script, 'w').write(script)
-    os.chmod(ost_script, 0555)
+    os.chmod(ost_script, 0o555)
 
   if no_includes:
     os.system(REMOVE_HEADERS % outdir)
     os.system(REMOVE_CURRENT % outdir)  
-  print 'copying site-packages'
+  print('copying site-packages')
   for sp in SITE_PACKAGES:
     src=get_python_module_path(sp)
     if os.path.isdir(src):
       shutil.copytree(src, os.path.join(outdir, pymod_dir, sp))
     else:
       shutil.copy(src, os.path.join(outdir, pymod_dir, sp))
-  print 'updating link commands of python shared objects'
+  print('updating link commands of python shared objects')
   os.path.walk(os.path.join(outdir, 'lib'), 
                update_pymod_shared_objects, 
                os.path.join(outdir, 'lib'))
diff --git a/deployment/macos/pack.py b/deployment/macos/pack.py
index beb7d7d74932339475945cee35ccc59048e9f5e1..16b5165778c57433f3fdb505443f697827d7e470 100644
--- a/deployment/macos/pack.py
+++ b/deployment/macos/pack.py
@@ -42,7 +42,7 @@ def collect_deps(stage_dir, components, binaries, libexec_binaries,
     lib_name=os.path.abspath(os.path.join(stage_dir, 'lib', 
                                           _lib_name(component)))  
     if not os.path.exists(lib_name):
-      print 'WARNING:', lib_name, 'does not exist'
+      print('WARNING:', lib_name, 'does not exist')
     if lib_name not in pool:
       _deps_for_lib(lib_name, pool)
       pool.add(lib_name)    
@@ -50,7 +50,7 @@ def collect_deps(stage_dir, components, binaries, libexec_binaries,
     bin_name=os.path.abspath(os.path.join(stage_dir, 'bin', 
                                           bin))  
     if not os.path.exists(bin_name):
-      print 'WARNING:', bin_name, 'does not exist'
+      print('WARNING:', bin_name, 'does not exist')
       continue
     if bin_name not in pool:
       _deps_for_lib(bin_name, pool)
@@ -58,15 +58,15 @@ def collect_deps(stage_dir, components, binaries, libexec_binaries,
     bin_name=os.path.abspath(os.path.join(stage_dir, 'libexec', libexec_path,
                                           bin))
     if not os.path.exists(bin_name):
-      print 'WARNING:', bin_name, 'does not exist'
+      print('WARNING:', bin_name, 'does not exist')
       continue
     if bin_name not in pool:
       _deps_for_lib(bin_name, pool)
   for site_package in site_packages:
     full_path=get_python_module_path(site_package)
-    print full_path
+    print(full_path)
     if not os.path.exists(full_path):
-      print 'WARNING:', site_package, 'does not exists'
+      print('WARNING:', site_package, 'does not exists')
       continue
     if os.path.isdir(full_path):
       for so_file in glob.glob(os.path.join(full_path, '*.so')):
@@ -104,7 +104,7 @@ def copy_binaries(stage_dir, outdir, binary_names, scripts, bin_dir,
     else:
       bin_name=os.path.join(stage_dir, binary_name)
     if not os.path.exists(bin_name):
-      print 'WARNING:', binary_name, 'does not exist'
+      print('WARNING:', binary_name, 'does not exist')
       continue
     dst_name=os.path.join(outdir, bin_dir, os.path.basename(bin_name))
     shutil.copy(bin_name, dst_name)
@@ -128,14 +128,14 @@ def split_framework_components(abs_path):
         return lead, trail
 
 def change_id(id, lib):
-  os.chmod(lib, 0666)
+  os.chmod(lib, 0o666)
   os.system(CHANGE_ID_RPATH % (id,lib))
-  os.chmod(lib, 0444)
+  os.chmod(lib, 0o444)
 
 def update_load_commands(lib, exe, exe_path):
   direct_deps=set()
   _deps_for_lib(lib, direct_deps, recursive=False)
-  os.chmod(lib, 0666)
+  os.chmod(lib, 0o666)
   for direct_dep in direct_deps:
     if direct_dep.endswith('.dylib'):
       new_name=os.path.basename(direct_dep)
@@ -147,9 +147,9 @@ def update_load_commands(lib, exe, exe_path):
       new_name=os.path.join(framework_name, rel_path)
       os.system(CHANGE_LOAD_CMD_RPATH % (direct_dep, new_name, lib))
   if exe:
-    os.chmod(lib, 0555)
+    os.chmod(lib, 0o555)
   else:
-    os.chmod(lib, 0444)
+    os.chmod(lib, 0o444)
 
 def copy_deps(dependencies, outdir):
   exe_path=os.path.join(outdir, 'bin')
@@ -209,20 +209,20 @@ def merge_tree(src, dst):
             merge_tree(srcname, dstname)
         else:
             shutil.copy2(srcname, dstname)
-    except (IOError, os.error), why:
+    except (IOError, os.error) as why:
         errors.append((srcname, dstname, str(why)))
-    except shutil.Error, err:
+    except shutil.Error as err:
         errors.extend(err.args[0])
   try:
       shutil.copystat(src, dst)
-  except OSError, why:
+  except OSError as why:
       if WindowsError is not None and isinstance(why, WindowsError):
           # Copying file access times may fail on Windows
           pass
       else:
           errors.extend((src, dst, str(why)))
   if errors:
-      raise shutil.Error, errors
+      raise shutil.Error(errors)
 
 def get_site_package_dir():
   """
@@ -269,7 +269,7 @@ class Package(object):
     self.site_packages=[]
     self.site_packages_dir=''
   def status(self, message):
-    print '%s: %s' % (self.name, message)
+    print('%s: %s' % (self.name, message))
 
   def _prepare_output_dir(self, output_dir):
     """
@@ -287,7 +287,7 @@ class Package(object):
     if self.libexec_dir:
       out_exec_dir=os.path.join(output_dir, 'libexec', self.libexec_dir)
       if not os.path.exists(out_exec_dir):
-        print 'making...', out_exec_dir
+        print('making...', out_exec_dir)
         os.makedirs(out_exec_dir)
   def _copy_site_packages(self, output_dir):
     for sp in SITE_PACKAGES:
@@ -296,7 +296,7 @@ class Package(object):
         merge_tree(src, os.path.joini(output_dir, self.pymod_dir, sp))
       else:
         shutil.copy(src, os.path.join(output_dir, self.pymod_dir, sp))
-    print 'updating link commands of python shared objects'
+    print('updating link commands of python shared objects')
     os.path.walk(os.path.join(output_dir, 'lib'), 
                  update_pymod_shared_objects, 
                  os.path.join(output_dir, 'lib'))
diff --git a/doc/conf/conf.py b/doc/conf/conf.py
index 7a7fa741fbd498a62e8a9c47a8ae6cb77a7b4030..8da3b5569bb15b76ca3a871738af60d28265f31f 100644
--- a/doc/conf/conf.py
+++ b/doc/conf/conf.py
@@ -48,8 +48,8 @@ source_suffix = '.rst'
 master_doc = 'index'
 
 # General information about the project.
-project = u'OpenStructure'
-copyright = u'2019, OpenStructure authors'
+project = 'OpenStructure'
+copyright = '2019, OpenStructure authors'
 
 # The version info for the project you're documenting, acts as replacement for
 # |version| and |release|, also used in various other places throughout the
@@ -189,8 +189,8 @@ htmlhelp_basename = 'openstructure-doc'
 # Grouping the document tree into LaTeX files. List of tuples
 # (source start file, target name, title, author, documentclass [howto/manual]).
 latex_documents = [
-  ('index', 'OpenStructure.tex', u'OpenStructure Documentation',
-   u'OpenStructure authors', 'manual'),
+  ('index', 'OpenStructure.tex', 'OpenStructure Documentation',
+   'OpenStructure authors', 'manual'),
 ]
 
 # The name of an image file (relative to this directory) to place at the top of
diff --git a/doc/make.py b/doc/make.py
index 57da9479e30ff0c1d89adef2723f3c843177486b..1b9f43a1d026058780504a47ded87f8ce8dde472 100644
--- a/doc/make.py
+++ b/doc/make.py
@@ -48,7 +48,7 @@ def _CreateAndCopy(in_name, outdir):
   if not os.path.exists(out_dir):
     _MkDir(out_dir)
   if _RequireCopy(in_name, out_name):
-    print 'cp %s %s' % (in_name, out_name)
+    print('cp %s %s' % (in_name, out_name))
     os.system('cp %s %s' % (in_name, out_name))
 
 def _MkDir(dirname):
@@ -99,7 +99,7 @@ sphinx_bin=settings.Locate(['sphinx-build', 'sphinx-build-2.6','sphinx-build-2.7
 if opts.html:
   cmd='%s %s -b html -c %s %s %s' % (sphinx_bin, opt_str, 
                                      'doc/conf', 'doc/source', 'doc/build/html')
-  print cmd
+  print(cmd)
   _CheckCall(cmd, shell=True)
 
 if opts.doctest:
diff --git a/doc/to_sphinx.py b/doc/to_sphinx.py
index 09809d2351170a195c26aeada9f2c28b6d4a62fa..eab09effc055f273d7b2a89cafddd34ad999542d 100644
--- a/doc/to_sphinx.py
+++ b/doc/to_sphinx.py
@@ -6,9 +6,9 @@ class_name=parts[-1]
 __import__(module, globals(), locals())
 mod=sys.modules[module]
 the_class=getattr(mod, class_name)
-print '.. currentmodule:: %s' % module
-print ''
-print '.. class:: %s' % class_name
+print('.. currentmodule:: %s' % module)
+print('')
+print('.. class:: %s' % class_name)
 
 def _arg_type(arg_type):
   if arg_type not in ('str', 'int', 'bool', 'float', 'None'):
@@ -63,10 +63,10 @@ def parse_signature(signature):
     return Method(method_match.groupdict()['name'], 
                   _arg_type(method_match.groupdict()['rtype']), 
                   args[1:], opt_args)
-  print signature, 'not matched'
+  print(signature, 'not matched')
 
 if '--no-derived' in sys.argv:
-  members=the_class.__dict__.keys()
+  members=list(the_class.__dict__.keys())
 else:
   members=dir(the_class)
 for m in members:
@@ -75,4 +75,4 @@ for m in members:
   member_doc=getattr(the_class, m).__doc__
   if member_doc:
     method=parse_signature(member_doc.split('\n')[1])
-    print method.to_sphinx()
\ No newline at end of file
+    print(method.to_sphinx())
\ No newline at end of file
diff --git a/docker/test_docker.py b/docker/test_docker.py
index e191217e63f9f8de3ff979e54c787a422428394f..38fae4a6093ba8b437d1ca91e63992a6f12a068b 100644
--- a/docker/test_docker.py
+++ b/docker/test_docker.py
@@ -16,4 +16,4 @@ except qsscoring.QSscoreError as ex:
     ost.LogError('QSscore failed:', str(ex))
     qs_score = 0
 
-print "OST is working!"
+print("OST is working!")
diff --git a/examples/demos/charmm_trj_blur.py b/examples/demos/charmm_trj_blur.py
index f65d0dbccb9f7fb51c4329b5668ec35fd66be378..b6f848ffb6607c9e1772fb42a7a85f9f916753fd 100644
--- a/examples/demos/charmm_trj_blur.py
+++ b/examples/demos/charmm_trj_blur.py
@@ -41,5 +41,5 @@ scene.AutoAutoslab(True)
 
 # create an animation timer and start it
 anim=Anim(cg,go)
-print 'Demo 6: Import of a CHARMM trajectory. Type anim.stop() to halt animation, anim.start(100) to start it again with stepsize 100!Starting animation now....'
+print('Demo 6: Import of a CHARMM trajectory. Type anim.stop() to halt animation, anim.start(100) to start it again with stepsize 100!Starting animation now....')
 anim.start(50)
diff --git a/examples/demos/correlate.py b/examples/demos/correlate.py
index 55bb4d068002e6de2d4f38ece15a8236fa467b37..1b65722d65df83a500e3c2a76346c70c346a978d 100644
--- a/examples/demos/correlate.py
+++ b/examples/demos/correlate.py
@@ -42,7 +42,7 @@ scene.CenterOn(map_go)
 candidates=io.LoadPDB('data/loop-candidates.pdb', 
                       load_multi=True)
 
-print len(candidates)
+print(len(candidates))
 #-------------------------------------------------------------------------------
 def Correlate(candidates):
   # this function converts the candidate loop into a density and calculates the 
diff --git a/examples/demos/gfx_mapslab.py b/examples/demos/gfx_mapslab.py
index 8cc8ca334d2abf5f4dd7911bcb0b77f422da70b1..d7ca61c30dbbb9ea4ae2672a99075b186b3729af 100644
--- a/examples/demos/gfx_mapslab.py
+++ b/examples/demos/gfx_mapslab.py
@@ -12,7 +12,7 @@ for p in img.ExtentIterator(mh.GetExtent()):
   mh.SetReal(p,val)
   vmin=min(vmin,val)
   vmax=max(vmax,val)
-print vmin, vmax
+print(vmin, vmax)
 for p in img.ExtentIterator(mh.GetExtent()):
   mh.SetReal(p,(mh.GetReal(p)-vmin)/(vmax-vmin))
 
@@ -55,4 +55,4 @@ scene.Add(go2)
 
 go2.ColorBy('HEAT_MAP',0.2,0.8)
 
-print 'Demo 4: Projecting the density of a map onto a plane...' 
+print('Demo 4: Projecting the density of a map onto a plane...') 
diff --git a/examples/demos/load_and_display.py b/examples/demos/load_and_display.py
index 6ce59673c3918dcf0b4bc933af7b98b46541a1d6..b4e140520b5942cb4613ae65d692b5d369894155 100644
--- a/examples/demos/load_and_display.py
+++ b/examples/demos/load_and_display.py
@@ -18,4 +18,4 @@ scene.Add(go)
 # center the scene on the geometric center of the SDH
 scene.CenterOn(go)
 
-print 'Demo 1: loading and displaying a pdb file'
+print('Demo 1: loading and displaying a pdb file')
diff --git a/examples/demos/the_hammer.py b/examples/demos/the_hammer.py
index 1c308b2d7ae8ad20d3b0413d783e07b2db3dfd43..114e2baa21e7207e28b1daa2af1141f1fb2335e0 100644
--- a/examples/demos/the_hammer.py
+++ b/examples/demos/the_hammer.py
@@ -82,4 +82,4 @@ grad.SetColorAt(0.5, gfx.Color(1.0, 0.0, 1.0))
 grad.SetColorAt(1.0, gfx.Color(1.0, 0.0, 0.0))
 b_go.ColorBy('clash', gfx.Color(0,1,0), gfx.Color(1,0,0), 0.0, 10.0, mol.Prop.Level.ATOM)
 scene.CenterOn(b_go)
-print 'Demo 7: Illustrating a clash score'
+print('Demo 7: Illustrating a clash score')
diff --git a/modules/base/pymod/table.py b/modules/base/pymod/table.py
index 994f01000658969c6cffe5291ffe9d67a374a16b..4a4a55934395398e6532945197f41e260eecc101 100644
--- a/modules/base/pymod/table.py
+++ b/modules/base/pymod/table.py
@@ -4,7 +4,7 @@ import math
 from ost import stutil
 import itertools
 import operator
-import cPickle
+import pickle
 import weakref
 from ost import LogError, LogWarning, LogInfo, LogVerbose
 
@@ -228,7 +228,7 @@ class Table(object):
     self.rows=[]    
     if len(kwargs)>=0:
       if not col_names:
-        self.col_names=[v for v in kwargs.keys()]
+        self.col_names=[v for v in list(kwargs.keys())]
       if not self.col_types:
         self.col_types=['string' for u in range(len(self.col_names))]
       if len(kwargs)>0:
@@ -249,8 +249,8 @@ class Table(object):
       return None
     
     short2long = {'s' : 'string', 'i': 'int', 'b' : 'bool', 'f' : 'float'}
-    allowed_short = short2long.keys()
-    allowed_long = short2long.values()
+    allowed_short = list(short2long.keys())
+    allowed_long = list(short2long.values())
     
     type_list = []
     
@@ -359,7 +359,7 @@ class Table(object):
     if ty=='string':
       return str(value)
     if ty=='bool':
-      if isinstance(value, str) or isinstance(value, unicode):
+      if isinstance(value, str) or isinstance(value, str):
         if value.upper() in ('FALSE', 'NO',):
           return False
         return True
@@ -525,11 +525,11 @@ Statistics for column %(col)s
              data items is different for different columns.
     '''
     # get column indices
-    idxs = [self.GetColIndex(k) for k in d.keys()]
+    idxs = [self.GetColIndex(k) for k in list(d.keys())]
     
     # convert scalar values to list
     old_len = None
-    for k,v in d.iteritems():
+    for k,v in d.items():
       if IsScalar(v):
         v = [v]
         d[k] = v
@@ -540,7 +540,7 @@ Statistics for column %(col)s
                          "for all columns in %s"%str(d))
     
     # convert column based dict to row based dict and create row and add data
-    for i,data in enumerate(zip(*d.values())):
+    for i,data in enumerate(zip(*list(d.values()))):
       new_row = [None for a in range(len(self.col_names))]
       for idx,v in zip(idxs,data):
         new_row[idx] = self._Coerce(v, self.col_types[idx])
@@ -815,7 +815,7 @@ Statistics for column %(col)s
         if not func(row):
           matches=False
           break
-      for key, val in kwargs.iteritems():
+      for key, val in kwargs.items():
         if row[self.GetColIndex(key)]!=val:
           matches=False
           break
@@ -947,13 +947,13 @@ Statistics for column %(col)s
       stream=open(stream_or_filename, 'rb')
     else:
       stream=stream_or_filename
-    return cPickle.load(stream)
+    return pickle.load(stream)
 
   @staticmethod
   def _GuessFormat(filename):
     try:
       filename = filename.name
-    except AttributeError, e:
+    except AttributeError as e:
       pass
     if filename.endswith('.csv'):
       return 'csv'
@@ -1085,7 +1085,7 @@ Statistics for column %(col)s
       for col1, col2 in zip(tab['col1'], tab['col2']):
         print col1, col2
     """
-    return zip(*[self[arg] for arg in args])
+    return list(zip(*[self[arg] for arg in args]))
 
   def Plot(self, x, y=None, z=None, style='.', x_title=None, y_title=None,
            z_title=None, x_range=None, y_range=None, z_range=None,
@@ -1561,7 +1561,7 @@ Statistics for column %(col)s
                          'specify the parameter rows with a list of row indices '\
                          '(max 7)')
       else:
-        rows=range(len(self.rows))
+        rows=list(range(len(self.rows)))
     else:
       if not isinstance(rows,list):
         rows=[rows]
@@ -2197,7 +2197,7 @@ Statistics for column %(col)s
   def _SavePickle(self, stream):
     if not hasattr(stream, 'write'):
       stream=open(stream, 'wb')
-    cPickle.dump(self, stream, cPickle.HIGHEST_PROTOCOL)
+    pickle.dump(self, stream, pickle.HIGHEST_PROTOCOL)
 
   def _SaveHTML(self, stream_or_filename):
     def _escape(s):
@@ -2459,7 +2459,7 @@ Statistics for column %(col)s
       a = self.GetNumpyMatrix(*args)
       
       if len(kwargs)!=0:
-        if kwargs.has_key('weights'):
+        if 'weights' in kwargs:
           w = self.GetNumpyMatrix(kwargs['weights'])
           b = np.multiply(b,w)
           a = np.multiply(a,w)
@@ -3048,7 +3048,7 @@ Statistics for column %(col)s
     num_rows = len(tab.rows)
     for i in range(0,num_rows):
       row = tab.rows[i]
-      data = dict(zip(tab.col_names,row))
+      data = dict(list(zip(tab.col_names,row)))
       self.AddRow(data, overwrite)
     
 
@@ -3133,7 +3133,7 @@ def Merge(table1, table2, by, only_matching=False):
       raise ValueError('duplicate key "%s" in second table' % (str(key)))
     common2[key]=row
   new_tab=Table(table1.col_names+col_names, table1.col_types+col_types)
-  for k, v in common1.iteritems():
+  for k, v in common1.items():
     row=v+[None for i in range(len(table2.col_names)-len(common2_indices))]
     matched=False
     if k in common2:
@@ -3146,7 +3146,7 @@ def Merge(table1, table2, by, only_matching=False):
     new_tab.AddRow(row)
   if only_matching:
     return new_tab
-  for k, v in common2.iteritems():
+  for k, v in common2.items():
     if not k in common1:
       v2=[v[i] for i in new_index]
       row=[None for i in range(len(table1.col_names))]+v2
diff --git a/modules/base/pymod/testutils.py b/modules/base/pymod/testutils.py
index 5dd8762e2dc31ccc6cdee9ef5af34631e4d7cfd8..4b701fe08fa6ae71e1648bd1330a2a907c777bb9 100644
--- a/modules/base/pymod/testutils.py
+++ b/modules/base/pymod/testutils.py
@@ -41,7 +41,7 @@ def RunTests():
       import __main__
       from ost import xmlrunner
       for name, obj in inspect.getmembers(__main__):
-        if (isinstance(obj, (type, types.ClassType)) and
+        if (isinstance(obj, type) and
                             issubclass(obj, unittest.TestCase)):
           suite = unittest.TestLoader().loadTestsFromTestCase(obj)
           stream = open('PYTEST-%s.xml'%name, 'w')
@@ -50,8 +50,8 @@ def RunTests():
 
     else:
       unittest.main()
-  except Exception, e:
-    print e
+  except Exception as e:
+    print(e)
 
 
 def SetDefaultCompoundLib():
@@ -92,7 +92,7 @@ def SetDefaultCompoundLib():
     # try to get the shared data path?
     try:
       shared_data_path = GetSharedDataPath()
-    except Exception, e:
+    except Exception as e:
       SetPrefixPath(os.path.abspath(os.path.join(conop.__path__[0], os.pardir,
                                                  os.pardir, os.pardir,
                                                  os.pardir, os.pardir)))
diff --git a/modules/base/pymod/wrap_base.cc b/modules/base/pymod/wrap_base.cc
index b0cfa84655e8574cad01d64f370d8e4ab5001163..2b43f842e5725b90f954cff36cf7aa0a71221b9e 100644
--- a/modules/base/pymod/wrap_base.cc
+++ b/modules/base/pymod/wrap_base.cc
@@ -58,15 +58,21 @@ struct stringref_from_python_string
   
   static void* convertible(PyObject* obj_ptr)
   {
-    if (!PyString_Check(obj_ptr)) return 0;
+    if (!PyUnicode_Check(obj_ptr)) return 0;
     return obj_ptr;
   }
   
   static void construct(PyObject* obj_ptr,
                  boost::python::converter::rvalue_from_python_stage1_data* data)
   {
-    const char* value = PyString_AsString(obj_ptr);
-    if (value == 0) boost::python::throw_error_already_set();
+    PyObject* temp_bytes = PyUnicode_AsEncodedString(obj_ptr, "UTF-8", "strict");
+    char* value = NULL;
+    if (temp_bytes != NULL) {
+      value = PyBytes_AS_STRING(temp_bytes);
+      value = strdup(value);
+      Py_DECREF(temp_bytes);
+    }
+    if (value == NULL) boost::python::throw_error_already_set();
     void* storage = (
          (boost::python::converter::rvalue_from_python_storage<ost::StringRef>*)
                      data)->storage.bytes;
diff --git a/modules/base/pymod/xmlrunner.py b/modules/base/pymod/xmlrunner.py
index e2bcae76df26efef15363d6775dca6b8bbd0bc34..0ea3332628d3a85f26a55d140fcb5172dc379f5d 100644
--- a/modules/base/pymod/xmlrunner.py
+++ b/modules/base/pymod/xmlrunner.py
@@ -16,7 +16,7 @@ import unittest
 from xml.sax.saxutils import escape
 
 try:
-    from StringIO import StringIO
+    from io import StringIO
 except ImportError:
     from io import StringIO
 
@@ -297,7 +297,7 @@ class XMLTestRunnerTest(unittest.TestCase):
         """
         class TestTest(unittest.TestCase):
             def test_foo(self):
-                self.assert_(False)
+                self.assertTrue(False)
         self._try_test_run(TestTest, """<testsuite errors="0" failures="1" name="unittest.TestSuite" tests="1" time="0.000">
   <testcase classname="__main__.TestTest" name="test_foo" time="0.000">
     <failure type="exceptions.AssertionError">Foobar</failure>
diff --git a/modules/base/tests/test_table.py b/modules/base/tests/test_table.py
index 347183262b12b7881631d952da072652c5407996..ec2e5fcf0d4f8190fc92b5a6c40d82cf9ed2165b 100644
--- a/modules/base/tests/test_table.py
+++ b/modules/base/tests/test_table.py
@@ -19,19 +19,19 @@ try:
   import numpy as np
 except ImportError:
   HAS_NUMPY=False
-  print "Could not find numpy: ignoring some table class unit tests"
+  print("Could not find numpy: ignoring some table class unit tests")
 
 try:
   import scipy.stats.mstats
 except ImportError:
   HAS_SCIPY_STATS=False
-  print "Could not find scipy.stats.mstats: ignoring some table class unit tests"
+  print("Could not find scipy.stats.mstats: ignoring some table class unit tests")
   
 try:
   import scipy.ndimage
 except ImportError:
   HAS_SCIPY_NDIMG=False
-  print "Could not find scipy.ndimage: ignoring some table class unit tests"
+  print("Could not find scipy.ndimage: ignoring some table class unit tests")
   
 
 try:
@@ -39,14 +39,14 @@ try:
   matplotlib.use('Agg')
 except ImportError:
   HAS_MPL=False
-  print "Could not find matplotlib: ignoring some table class unit tests"
+  print("Could not find matplotlib: ignoring some table class unit tests")
   
 try:
   from PIL import Image
   from PIL import ImageChops
 except ImportError:
   HAS_PIL=False
-  print "Could not find python imagine library: ignoring some table class unit tests"
+  print("Could not find python imagine library: ignoring some table class unit tests")
 
 # setting up an OST LogSink to capture error messages
 class _FetchLog(ost.LogSink):
@@ -57,7 +57,7 @@ class _FetchLog(ost.LogSink):
   def LogMessage(self, message, severity):
     levels=['ERROR', 'WARNING', 'INFO', 'VERBOSE', 'DEBUG', 'TRACE']
     level=levels[severity]
-    if not level in self.messages.keys():
+    if not level in list(self.messages.keys()):
       self.messages[level] = list()
     self.messages[level].append(message.strip())
   
@@ -128,7 +128,7 @@ class TestTable(unittest.TestCase):
     dictionary containing a list of values for each column.
     '''
     self.CompareColCount(t, len(data_dict))
-    for k, v in data_dict.iteritems():
+    for k, v in data_dict.items():
       self.CompareDataForCol(t, k, v)
       
   def CompareDataForCol(self, t, col_name, ref_data):
@@ -186,8 +186,8 @@ class TestTable(unittest.TestCase):
 
   def testAllowsToSearchColNames(self):
     tab = self.CreateTestTable()
-    self.assertEquals(tab.SearchColNames('d$'), ['second', 'third'])
-    self.assertEquals(tab.SearchColNames('(first|third)'), ['first','third'])
+    self.assertEqual(tab.SearchColNames('d$'), ['second', 'third'])
+    self.assertEqual(tab.SearchColNames('(first|third)'), ['first','third'])
 
   def testProvidesDirectAccessToColumns(self):
     tab = Table(['x', 'two'], 'ii')
@@ -218,10 +218,10 @@ class TestTable(unittest.TestCase):
     self.assertEqual(z[1][0], 'x')
     self.assertEqual(z[1][1], 'x')
     z=tab.Zip('col1', 'col4')
-    self.assertEquals(type(z[0][0]),str)
-    self.assertEquals(type(z[1][0]),str)
-    self.assertEquals(type(z[0][1]),int)
-    self.assertEquals(type(z[1][1]),int)
+    self.assertEqual(type(z[0][0]),str)
+    self.assertEqual(type(z[1][0]),str)
+    self.assertEqual(type(z[0][1]),int)
+    self.assertEqual(type(z[1][1]),int)
     self.assertRaises(ValueError, tab.Zip, 'col5', 'col3')
   def testPercentiles(self):
     tab = Table(['nums'], 'i')
@@ -340,7 +340,7 @@ class TestTable(unittest.TestCase):
       4.000
 
     '''
-    tab = Table(['x'], 'f', x=range(5))
+    tab = Table(['x'], 'f', x=list(range(5)))
     self.CompareColCount(tab, 1)
     self.CompareRowCount(tab, 5)
     self.CompareColNames(tab, ['x'])
@@ -377,7 +377,7 @@ class TestTable(unittest.TestCase):
 
     '''
     
-    tab = Table(['foo', 'bar'], 'si', bar=range(10,14), foo=['i','love','unit','tests'])
+    tab = Table(['foo', 'bar'], 'si', bar=list(range(10,14)), foo=['i','love','unit','tests'])
     self.CompareColCount(tab, 2)
     self.CompareRowCount(tab, 4)
     self.CompareColNames(tab, ['foo','bar'])
@@ -390,7 +390,7 @@ class TestTable(unittest.TestCase):
     '''
     
     self.assertRaises(ValueError, Table, ['foo', 'bar'], 'si',
-                      bar=range(10,14), foo=['i','love','tests'])
+                      bar=list(range(10,14)), foo=['i','love','tests'])
     
     
   def testTableInitMultiColMultiValueAndNoneNonEmpty(self):
@@ -672,40 +672,40 @@ class TestTable(unittest.TestCase):
     
   def testParseColumnTypes(self):
     types = Table._ParseColTypes(['i','f','s','b'])
-    self.assertEquals(types, ['int','float','string','bool'])
+    self.assertEqual(types, ['int','float','string','bool'])
     
     types = Table._ParseColTypes(['int','float','string','bool'])
-    self.assertEquals(types, ['int','float','string','bool'])
+    self.assertEqual(types, ['int','float','string','bool'])
     
     types = Table._ParseColTypes(['i','float','s','bool'])
-    self.assertEquals(types, ['int','float','string','bool'])
+    self.assertEqual(types, ['int','float','string','bool'])
 
     types = Table._ParseColTypes(['i','fLOAT','S','bool'])
-    self.assertEquals(types, ['int','float','string','bool'])
+    self.assertEqual(types, ['int','float','string','bool'])
     
     types = Table._ParseColTypes('ifsb')
-    self.assertEquals(types, ['int','float','string','bool'])
+    self.assertEqual(types, ['int','float','string','bool'])
     
     types = Table._ParseColTypes('int,float,string,bool')
-    self.assertEquals(types, ['int','float','string','bool'])
+    self.assertEqual(types, ['int','float','string','bool'])
     
     types = Table._ParseColTypes('int,f,s,bool')
-    self.assertEquals(types, ['int','float','string','bool'])
+    self.assertEqual(types, ['int','float','string','bool'])
     
     types = Table._ParseColTypes('INT,F,s,bOOL')
-    self.assertEquals(types, ['int','float','string','bool'])
+    self.assertEqual(types, ['int','float','string','bool'])
 
     types = Table._ParseColTypes('boOl')
-    self.assertEquals(types, ['bool'])
+    self.assertEqual(types, ['bool'])
     
     types = Table._ParseColTypes('S')
-    self.assertEquals(types, ['string'])
+    self.assertEqual(types, ['string'])
     
     types = Table._ParseColTypes(['i'])
-    self.assertEquals(types, ['int'])
+    self.assertEqual(types, ['int'])
     
     types = Table._ParseColTypes(['FLOAT'])
-    self.assertEquals(types, ['float'])
+    self.assertEqual(types, ['float'])
 
     self.assertRaises(ValueError, Table._ParseColTypes, 'bfstring')
     self.assertRaises(ValueError, Table._ParseColTypes, ['b,f,string'])
@@ -765,23 +765,23 @@ class TestTable(unittest.TestCase):
     tab = Table()
     
     # None values
-    self.assertEquals(tab._Coerce('NA', 'x'), None)
-    self.assertEquals(tab._Coerce(None, 'x'), None)
+    self.assertEqual(tab._Coerce('NA', 'x'), None)
+    self.assertEqual(tab._Coerce(None, 'x'), None)
     
     # int type
     self.assertTrue(isinstance(tab._Coerce(2 ,'int'), int))
-    self.assertEquals(tab._Coerce(2 ,'int'), 2)
+    self.assertEqual(tab._Coerce(2 ,'int'), 2)
     self.assertTrue(isinstance(tab._Coerce(2.2 ,'int'), int))
-    self.assertEquals(tab._Coerce(2.2 ,'int'), 2)
-    self.assertEquals(tab._Coerce(True ,'int'), 1)
-    self.assertEquals(tab._Coerce(False ,'int'), 0)
+    self.assertEqual(tab._Coerce(2.2 ,'int'), 2)
+    self.assertEqual(tab._Coerce(True ,'int'), 1)
+    self.assertEqual(tab._Coerce(False ,'int'), 0)
     self.assertRaises(ValueError, tab._Coerce, "foo" , 'int')
     
     # float type
     self.assertTrue(isinstance(tab._Coerce(2 ,'float'), float))
-    self.assertEquals(tab._Coerce(2 ,'float'), 2.000)
+    self.assertEqual(tab._Coerce(2 ,'float'), 2.000)
     self.assertTrue(isinstance(tab._Coerce(3.141 ,'float'), float))
-    self.assertEquals(tab._Coerce(3.141 ,'float'), 3.141)
+    self.assertEqual(tab._Coerce(3.141 ,'float'), 3.141)
     self.assertRaises(ValueError, tab._Coerce, "foo" , 'float')
     
     # string type
@@ -793,13 +793,13 @@ class TestTable(unittest.TestCase):
     self.assertTrue(isinstance(tab._Coerce(False ,'string'), str))
     
     # bool type
-    self.assertEquals(tab._Coerce(True ,'bool'), True)
-    self.assertEquals(tab._Coerce(False ,'bool'), False)
-    self.assertEquals(tab._Coerce('falSE' ,'bool'), False)
-    self.assertEquals(tab._Coerce('no' ,'bool'), False)
-    self.assertEquals(tab._Coerce('not false and not no','bool'), True)
-    self.assertEquals(tab._Coerce(0, 'bool'), False)
-    self.assertEquals(tab._Coerce(1, 'bool'), True)
+    self.assertEqual(tab._Coerce(True ,'bool'), True)
+    self.assertEqual(tab._Coerce(False ,'bool'), False)
+    self.assertEqual(tab._Coerce('falSE' ,'bool'), False)
+    self.assertEqual(tab._Coerce('no' ,'bool'), False)
+    self.assertEqual(tab._Coerce('not false and not no','bool'), True)
+    self.assertEqual(tab._Coerce(0, 'bool'), False)
+    self.assertEqual(tab._Coerce(1, 'bool'), True)
     
     # unknown type
     self.assertRaises(ValueError, tab._Coerce, 'bla', 'abc')
@@ -880,7 +880,7 @@ class TestTable(unittest.TestCase):
     
   def testLoadOSTDifficultHeaders(self):
     tab = Table.Load(os.path.join('testfiles','ost-table-difficult-headers.tab'))
-    self.assertEquals(tab.col_types, ['float','float','float','float','float'])
+    self.assertEqual(tab.col_types, ['float','float','float','float','float'])
 
   def testSaveLoadTableOST(self):
     tab = self.CreateTestTable()
@@ -920,15 +920,15 @@ class TestTable(unittest.TestCase):
     tab_loaded_fname = Table.Load('saveloadtable_withspaces_filename_out.tab')
     self.CompareDataFromDict(tab_loaded_fname, {'first': ['x','foo',None,'hello spaces'], 'second': [3,None,9,10], 'third': [None,2.2,3.3,10.1]})
   def testSaveTableHTML(self):
-    import StringIO
+    import io
     tab = self.CreateTestTable()
-    stream = StringIO.StringIO()
+    stream = io.StringIO()
     tab.Save(stream, format='html')
     self.assertEqual(stream.getvalue(), '<table><tr><th>first</th><th>second</th><th>third</th></tr><tr><td>x</td><td>3</td><td></td></tr><tr><td>foo</td><td></td><td>2.200</td></tr><tr><td></td><td>9</td><td>3.300</td></tr></table>')
   def testSaveTableContext(self):
-    import StringIO
+    import io
     tab = self.CreateTestTable()
-    stream = StringIO.StringIO()
+    stream = io.StringIO()
     tab.Save(stream, format='context')
     self.assertEqual(stream.getvalue(), 
                      '\\starttable[l|r|i3r|]\n\\HL\n\\NC \\bf first\\NC \\bf second\\NC \\bf third \\AR\\HL\n\\NC x\\NC 3\\NC --- \\AR\n\\NC foo\NC ---\NC 2.200 \\AR\n\\NC ---\\NC 9\\NC 3.300 \\AR\n\\HL\n\\stoptable')
@@ -1042,44 +1042,44 @@ class TestTable(unittest.TestCase):
     tab = self.CreateTestTable()
     tab.AddCol('fourth','bool',[True,True,False])
 
-    self.assertEquals(tab.Min('first'),'foo')
-    self.assertEquals(tab.Min('second'),3)
-    self.assertAlmostEquals(tab.Min('third'),2.2)
-    self.assertEquals(tab.Min('fourth'),False)
+    self.assertEqual(tab.Min('first'),'foo')
+    self.assertEqual(tab.Min('second'),3)
+    self.assertAlmostEqual(tab.Min('third'),2.2)
+    self.assertEqual(tab.Min('fourth'),False)
     self.assertRaises(ValueError,tab.Min,'fifth')
     
-    self.assertEquals(tab.MinIdx('first'),1)
-    self.assertEquals(tab.MinIdx('second'),0)
-    self.assertAlmostEquals(tab.MinIdx('third'),1)
-    self.assertEquals(tab.MinIdx('fourth'),2)
+    self.assertEqual(tab.MinIdx('first'),1)
+    self.assertEqual(tab.MinIdx('second'),0)
+    self.assertAlmostEqual(tab.MinIdx('third'),1)
+    self.assertEqual(tab.MinIdx('fourth'),2)
     self.assertRaises(ValueError,tab.MinIdx,'fifth')
     
-    self.assertEquals(tab.MinRow('first'),['foo', None, 2.20, True])
-    self.assertEquals(tab.MinRow('second'),['x', 3, None, True])
-    self.assertEquals(tab.MinRow('third'),['foo', None, 2.20, True])
-    self.assertEquals(tab.MinRow('fourth'),[None, 9, 3.3, False])
+    self.assertEqual(tab.MinRow('first'),['foo', None, 2.20, True])
+    self.assertEqual(tab.MinRow('second'),['x', 3, None, True])
+    self.assertEqual(tab.MinRow('third'),['foo', None, 2.20, True])
+    self.assertEqual(tab.MinRow('fourth'),[None, 9, 3.3, False])
     self.assertRaises(ValueError,tab.MinRow,'fifth')
     
   def testMaxTable(self):
     tab = self.CreateTestTable()
     tab.AddCol('fourth','bool',[False,True,True])
     
-    self.assertEquals(tab.Max('first'),'x')
-    self.assertEquals(tab.Max('second'),9)
-    self.assertAlmostEquals(tab.Max('third'),3.3)
-    self.assertEquals(tab.Max('fourth'),True)
+    self.assertEqual(tab.Max('first'),'x')
+    self.assertEqual(tab.Max('second'),9)
+    self.assertAlmostEqual(tab.Max('third'),3.3)
+    self.assertEqual(tab.Max('fourth'),True)
     self.assertRaises(ValueError,tab.Max,'fifth')
     
-    self.assertEquals(tab.MaxIdx('first'),0)
-    self.assertEquals(tab.MaxIdx('second'),2)
-    self.assertAlmostEquals(tab.MaxIdx('third'),2)
-    self.assertEquals(tab.MaxIdx('fourth'),1)
+    self.assertEqual(tab.MaxIdx('first'),0)
+    self.assertEqual(tab.MaxIdx('second'),2)
+    self.assertAlmostEqual(tab.MaxIdx('third'),2)
+    self.assertEqual(tab.MaxIdx('fourth'),1)
     self.assertRaises(ValueError,tab.MaxIdx,'fifth')
     
-    self.assertEquals(tab.MaxRow('first'),['x', 3, None, False])
-    self.assertEquals(tab.MaxRow('second'),[None, 9, 3.3, True])
-    self.assertEquals(tab.MaxRow('third'),[None, 9, 3.3, True])
-    self.assertEquals(tab.MaxRow('fourth'),['foo', None, 2.2, True])
+    self.assertEqual(tab.MaxRow('first'),['x', 3, None, False])
+    self.assertEqual(tab.MaxRow('second'),[None, 9, 3.3, True])
+    self.assertEqual(tab.MaxRow('third'),[None, 9, 3.3, True])
+    self.assertEqual(tab.MaxRow('fourth'),['foo', None, 2.2, True])
     self.assertRaises(ValueError,tab.MaxRow,'fifth')
     
   def testSumTable(self):
@@ -1088,9 +1088,9 @@ class TestTable(unittest.TestCase):
     tab.AddCol('fifth','string',['foo','bar',None])
     
     self.assertRaises(TypeError,tab.Sum,'first')
-    self.assertEquals(tab.Sum('second'),12)
-    self.assertAlmostEquals(tab.Sum('third'),5.5)
-    self.assertEquals(tab.Sum('fourth'),1)
+    self.assertEqual(tab.Sum('second'),12)
+    self.assertAlmostEqual(tab.Sum('third'),5.5)
+    self.assertEqual(tab.Sum('fourth'),1)
     self.assertRaises(TypeError,tab.Sum,'fifth')
     self.assertRaises(ValueError,tab.Sum,'sixth')
     
@@ -1100,9 +1100,9 @@ class TestTable(unittest.TestCase):
     tab.AddCol('fifth','string',['foo','bar',None])
     
     self.assertRaises(TypeError,tab.Median,'first')
-    self.assertEquals(tab.Median('second'),6.0)
-    self.assertAlmostEquals(tab.Median('third'),2.75)
-    self.assertEquals(tab.Median('fourth'),False)
+    self.assertEqual(tab.Median('second'),6.0)
+    self.assertAlmostEqual(tab.Median('third'),2.75)
+    self.assertEqual(tab.Median('fourth'),False)
     self.assertRaises(TypeError,tab.Median,'fifth')
     self.assertRaises(ValueError,tab.Median,'sixth')
     
@@ -1112,9 +1112,9 @@ class TestTable(unittest.TestCase):
     tab.AddCol('fifth','string',['foo','bar',None])
     
     self.assertRaises(TypeError,tab.Mean,'first')
-    self.assertAlmostEquals(tab.Mean('second'),6.0)
-    self.assertAlmostEquals(tab.Mean('third'),2.75)
-    self.assertAlmostEquals(tab.Mean('fourth'),0.33333333)
+    self.assertAlmostEqual(tab.Mean('second'),6.0)
+    self.assertAlmostEqual(tab.Mean('third'),2.75)
+    self.assertAlmostEqual(tab.Mean('fourth'),0.33333333)
     self.assertRaises(TypeError,tab.Mean,'fifth')
     self.assertRaises(ValueError,tab.Mean,'sixth')
     
@@ -1146,9 +1146,9 @@ class TestTable(unittest.TestCase):
     tab.AddCol('fifth','string',['foo','bar',None])
     
     self.assertRaises(TypeError,tab.StdDev,'first')
-    self.assertAlmostEquals(tab.StdDev('second'),3.0)
-    self.assertAlmostEquals(tab.StdDev('third'),0.55)
-    self.assertAlmostEquals(tab.StdDev('fourth'),0.47140452079)
+    self.assertAlmostEqual(tab.StdDev('second'),3.0)
+    self.assertAlmostEqual(tab.StdDev('third'),0.55)
+    self.assertAlmostEqual(tab.StdDev('fourth'),0.47140452079)
     self.assertRaises(TypeError,tab.StdDev,'fifth')
     self.assertRaises(ValueError,tab.StdDev,'sixth')
     
@@ -1156,14 +1156,14 @@ class TestTable(unittest.TestCase):
     tab = self.CreateTestTable()
     tab.AddCol('fourth','bool',[False,True,False])
     
-    self.assertEquals(tab.Count('first'),2)
-    self.assertEquals(tab.Count('second'),2)
-    self.assertEquals(tab.Count('third'),2)
-    self.assertEquals(tab.Count('fourth'),3)
-    self.assertEquals(tab.Count('first', ignore_nan=False),3)
-    self.assertEquals(tab.Count('second', ignore_nan=False),3)
-    self.assertEquals(tab.Count('third', ignore_nan=False),3)
-    self.assertEquals(tab.Count('fourth', ignore_nan=False),3)
+    self.assertEqual(tab.Count('first'),2)
+    self.assertEqual(tab.Count('second'),2)
+    self.assertEqual(tab.Count('third'),2)
+    self.assertEqual(tab.Count('fourth'),3)
+    self.assertEqual(tab.Count('first', ignore_nan=False),3)
+    self.assertEqual(tab.Count('second', ignore_nan=False),3)
+    self.assertEqual(tab.Count('third', ignore_nan=False),3)
+    self.assertEqual(tab.Count('fourth', ignore_nan=False),3)
     self.assertRaises(ValueError,tab.Count,'fifth')
     
   def testCalcEnrichment(self):
@@ -1180,15 +1180,15 @@ class TestTable(unittest.TestCase):
                                       class_dir='-')
     
     for x,y,refx,refy in zip(enrx,enry,enrx_ref,enry_ref):
-      self.assertAlmostEquals(x,refx)
-      self.assertAlmostEquals(y,refy)
+      self.assertAlmostEqual(x,refx)
+      self.assertAlmostEqual(y,refy)
     
     enrx,enry = tab.ComputeEnrichment(score_col='score', score_dir='-',
                                       class_col='classific')
     
     for x,y,refx,refy in zip(enrx,enry,enrx_ref,enry_ref):
-      self.assertAlmostEquals(x,refx)
-      self.assertAlmostEquals(y,refy)
+      self.assertAlmostEqual(x,refx)
+      self.assertAlmostEqual(y,refy)
     
     tab.AddCol('bad','string','col')
     
@@ -1275,7 +1275,7 @@ class TestTable(unittest.TestCase):
                                    class_col='rmsd', class_cutoff=2.0,
                                    class_dir='-')
     
-    self.assertAlmostEquals(auc, auc_ref)
+    self.assertAlmostEqual(auc, auc_ref)
 
   def testPlotROC(self):
     if not HAS_MPL or not HAS_PIL:
@@ -1297,7 +1297,7 @@ class TestTable(unittest.TestCase):
     pl = tab.PlotROC(score_col='score', score_dir='+',
                      class_col='classific',
                      save=os.path.join("testfiles","roc-out.png"))
-    self.assertEquals(pl, None)
+    self.assertEqual(pl, None)
 
   def testPlotLogROC(self):
     if not HAS_MPL or not HAS_PIL:
@@ -1319,7 +1319,7 @@ class TestTable(unittest.TestCase):
     pl = tab.PlotLogROC(score_col='score', score_dir='+',
                         class_col='classific',
                         save=os.path.join("testfiles","logroc-out.png"))
-    self.assertEquals(pl, None)
+    self.assertEqual(pl, None)
 
   def testPlotROCSameValues(self):
     if not HAS_MPL or not HAS_PIL:
@@ -1361,14 +1361,14 @@ class TestTable(unittest.TestCase):
                 classific=[True, True, False, True, True, True, False, False, True, False, True, False, True, False, False, False, True, False, True, False],
                 score=[0.9, 0.8, 0.7, 0.6, 0.55, 0.54, 0.53, 0.52, 0.51, 0.505, 0.4, 0.39, 0.38, 0.37, 0.36, 0.35, 0.34, 0.33, 0.30, 0.1])
     auc = tab.ComputeROCAUC(score_col='score', score_dir='+', class_col='classific')
-    self.assertAlmostEquals(auc, auc_ref)
+    self.assertAlmostEqual(auc, auc_ref)
 
     # no true positives
     tab = Table(['classific', 'score'], 'bf',
                 classific=[False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False],
                 score=[0.9, 0.8, 0.7, 0.6, 0.55, 0.54, 0.53, 0.52, 0.51, 0.505, 0.4, 0.39, 0.38, 0.37, 0.36, 0.35, 0.34, 0.33, 0.30, 0.1])
     auc = tab.ComputeROCAUC(score_col='score', score_dir='+', class_col='classific')
-    self.assertEquals(auc, None)
+    self.assertEqual(auc, None)
     
   def testLogROCAUCforPerfectCurve(self):
     if not HAS_NUMPY:
@@ -1380,11 +1380,11 @@ class TestTable(unittest.TestCase):
     
     # test logAUC
     auc = tab.ComputeLogROCAUC(score_col='score', score_dir='+', class_col='classific')
-    self.assertAlmostEquals(auc, auc_ref)
+    self.assertAlmostEqual(auc, auc_ref)
     
     # test linear AUC
     auc = tab.ComputeROCAUC(score_col='score', score_dir='+', class_col='classific')
-    self.assertAlmostEquals(auc, auc_ref)
+    self.assertAlmostEqual(auc, auc_ref)
       
   def testCalcLogROCAUCRandomCurve(self):
     if not HAS_NUMPY:
@@ -1396,12 +1396,12 @@ class TestTable(unittest.TestCase):
     # test logAUC
     auc_ref = 0.1440197405305
     auc = tab.ComputeLogROCAUC(score_col='score', score_dir='+', class_col='classific')
-    self.assertAlmostEquals(auc, auc_ref)
+    self.assertAlmostEqual(auc, auc_ref)
     
     # test linear AUC    
     auc_ref = 0.5
     auc = tab.ComputeROCAUC(score_col='score', score_dir='+', class_col='classific')
-    self.assertAlmostEquals(auc, auc_ref)
+    self.assertAlmostEqual(auc, auc_ref)
 
 
   def testCalcROCAUCWithCutoff(self):
@@ -1411,18 +1411,18 @@ class TestTable(unittest.TestCase):
                 classific=[0.9, 0.8, 0.7, 0.6, 0.55, 0.54, 0.53, 0.52, 0.51, 0.505, 0.4, 0.39, 0.38, 0.37, 0.36, 0.35, 0.34, 0.33, 0.30, 0.1],
                 score=[0.9, 0.8, 0.7, 0.6, 0.55, 0.54, 0.53, 0.52, 0.51, 0.505, 0.4, 0.39, 0.38, 0.37, 0.36, 0.35, 0.34, 0.33, 0.30, 0.1])
     auc = tab.ComputeROCAUC(score_col='score', class_col='classific', class_cutoff=0.5)
-    self.assertEquals(auc, 1.0)
+    self.assertEqual(auc, 1.0)
 
     # no true positives
     auc = tab.ComputeROCAUC(score_col='score', class_col='classific', class_cutoff=1.0)
-    self.assertEquals(auc, None)
+    self.assertEqual(auc, None)
 
   def testCalcROCFromFile(self):
     if not HAS_NUMPY:
       return
     tab = Table.Load(os.path.join('testfiles','roc_table.dat'))
     auc = tab.ComputeROCAUC(score_col='prediction', class_col='reference', class_cutoff=0.4)
-    self.assertEquals(auc, 1.0)
+    self.assertEqual(auc, 1.0)
       
 
   def testCalcROCAUCSameValues(self):
@@ -1433,7 +1433,7 @@ class TestTable(unittest.TestCase):
                 classific=[True, True, False, True, True, True, False, False, True, False, True, False, True, False, False, False, True, False, True, False],
                 score=[0.9, 0.8, 0.7, 0.7, 0.7, 0.7, 0.53, 0.52, 0.51, 0.505, 0.4, 0.4, 0.4, 0.4, 0.36, 0.35, 0.34, 0.33, 0.30, 0.1])
     auc = tab.ComputeROCAUC(score_col='score', score_dir='+', class_col='classific')
-    self.assertAlmostEquals(auc, auc_ref)
+    self.assertAlmostEqual(auc, auc_ref)
 
   def testCalcMCC(self):
     log = _FetchLog()
@@ -1447,18 +1447,18 @@ class TestTable(unittest.TestCase):
                 class_wrong=[False,False,False,False,False,False, False,False,False, False,False,False,False,False,False, False,False,False,False, False,False,False,False,False])
     
     mcc = tab.ComputeMCC(score_col='score', score_dir='-', class_col='rmsd', class_dir='-', score_cutoff=1.0, class_cutoff=2.0)
-    self.assertAlmostEquals(mcc, 0.1490711984)
+    self.assertAlmostEqual(mcc, 0.1490711984)
     mcc = tab.ComputeMCC(score_col='class_score', class_col='class_rmsd')
-    self.assertAlmostEquals(mcc, 0.1490711984)
+    self.assertAlmostEqual(mcc, 0.1490711984)
     mcc = tab.ComputeMCC(score_col='score', score_dir='+', class_col='rmsd', class_dir='+', score_cutoff=1.0, class_cutoff=2.0)
-    self.assertAlmostEquals(mcc, 0.1490711984)
+    self.assertAlmostEqual(mcc, 0.1490711984)
     mcc = tab.ComputeMCC(score_col='score', score_dir='-', class_col='rmsd', class_dir='+', score_cutoff=1.0, class_cutoff=2.0)
-    self.assertAlmostEquals(mcc, -0.1490711984)
+    self.assertAlmostEqual(mcc, -0.1490711984)
     mcc = tab.ComputeMCC(score_col='score', score_dir='+', class_col='rmsd', class_dir='-', score_cutoff=1.0, class_cutoff=2.0)
-    self.assertAlmostEquals(mcc, -0.1490711984)
+    self.assertAlmostEqual(mcc, -0.1490711984)
     mcc = tab.ComputeMCC(score_col='class_wrong', class_col='class_rmsd')
-    self.assertEquals(mcc,None)
-    self.assertEquals(log.messages['WARNING'][0],
+    self.assertEqual(mcc,None)
+    self.assertEqual(log.messages['WARNING'][0],
     'Could not compute MCC: MCC is not defined since factor (tp + fp) is zero')
 
   def testCalcMCCPreclassified(self):
@@ -1467,9 +1467,9 @@ class TestTable(unittest.TestCase):
                 prediction1=[False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, True,  False, True, False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, True, False, False, True, False, True,  False, False, False, False, False, False],
                 prediction2=[False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, True,  False, False, True,  False, False, False, False, False, False, False, False, False, False, False, False, False, False, True, False, True, False, False, True,  False, True,  True,  False, False, False, False, False, False, False, False, False, False, True, False, False, True, False, False, False, True,  False, False, False, False])
     mcc = tab.ComputeMCC(score_col='prediction1', class_col='reference')
-    self.assertAlmostEquals(mcc, 0.538389277)
+    self.assertAlmostEqual(mcc, 0.538389277)
     mcc = tab.ComputeMCC(score_col='prediction2', class_col='reference')
-    self.assertAlmostEquals(mcc, 0.882089673321)
+    self.assertAlmostEqual(mcc, 0.882089673321)
 
   def testTableAsNumpyMatrix(self):
     if not HAS_NUMPY:
@@ -1519,24 +1519,24 @@ class TestTable(unittest.TestCase):
                 f=[9,9,9,9,9,9,9,9,9])
     
     pref = tab.GetOptimalPrefactors('c','a','b')
-    self.assertAlmostEquals(pref[0],0.799999999)
-    self.assertAlmostEquals(pref[1],0.166666666666)
+    self.assertAlmostEqual(pref[0],0.799999999)
+    self.assertAlmostEqual(pref[1],0.166666666666)
     
     pref = tab.GetOptimalPrefactors('c','b','a')
-    self.assertAlmostEquals(pref[0],0.166666666666)
-    self.assertAlmostEquals(pref[1],0.799999999)
+    self.assertAlmostEqual(pref[0],0.166666666666)
+    self.assertAlmostEqual(pref[1],0.799999999)
     
     pref = tab.GetOptimalPrefactors('c','b','a',weights='e')
-    self.assertAlmostEquals(pref[0],0.166666666666)
-    self.assertAlmostEquals(pref[1],0.799999999)
+    self.assertAlmostEqual(pref[0],0.166666666666)
+    self.assertAlmostEqual(pref[1],0.799999999)
     
     pref = tab.GetOptimalPrefactors('c','b','a',weights='f')
-    self.assertAlmostEquals(pref[0],0.166666666666)
-    self.assertAlmostEquals(pref[1],0.799999999)
+    self.assertAlmostEqual(pref[0],0.166666666666)
+    self.assertAlmostEqual(pref[1],0.799999999)
     
     pref = tab.GetOptimalPrefactors('c','a','b',weights='d')
-    self.assertAlmostEquals(pref[0],0.6078825445851)
-    self.assertAlmostEquals(pref[1],0.3394613806088)
+    self.assertAlmostEqual(pref[0],0.6078825445851)
+    self.assertAlmostEqual(pref[1],0.3394613806088)
     
     self.assertRaises(RuntimeError, tab.GetOptimalPrefactors, 'c','a','b',weight='d')
     self.assertRaises(RuntimeError, tab.GetOptimalPrefactors, 'c',weights='d')
@@ -1580,12 +1580,12 @@ class TestTable(unittest.TestCase):
         tab_list[i].append(v)
 
     for i in range(len(ref_list[0])):
-      self.assertAlmostEquals(tab_list[0][i],ref_list[0][i])
-      self.assertAlmostEquals(tab_list[1][i],ref_list[1][i])
-      self.assertAlmostEquals(tab_list[2][i],ref_list[2][i])
-      self.assertAlmostEquals(tab_list[3][i],ref_list[3][i])
-      self.assertAlmostEquals(tab_list[4][i],ref_list[4][i])
-      self.assertAlmostEquals(tab_list[5][i],ref_list[5][i])
+      self.assertAlmostEqual(tab_list[0][i],ref_list[0][i])
+      self.assertAlmostEqual(tab_list[1][i],ref_list[1][i])
+      self.assertAlmostEqual(tab_list[2][i],ref_list[2][i])
+      self.assertAlmostEqual(tab_list[3][i],ref_list[3][i])
+      self.assertAlmostEqual(tab_list[4][i],ref_list[4][i])
+      self.assertAlmostEqual(tab_list[5][i],ref_list[5][i])
      
 
   def testIsEmpty(self):
@@ -1641,30 +1641,30 @@ class TestTable(unittest.TestCase):
     tab = self.CreateTestTable()
     tab.AddRow(['foo',4, 3.3])
     tab.AddRow([None,5, 6.3])
-    self.assertEquals(tab.GetUnique('first'), ['x','foo'])
-    self.assertEquals(tab.GetUnique('first', ignore_nan=False), ['x','foo', None])
-    self.assertEquals(tab.GetUnique('second'), [3,9,4,5])
-    self.assertEquals(tab.GetUnique('second', ignore_nan=False), [3,None,9,4,5])
-    self.assertEquals(tab.GetUnique('third'), [2.2, 3.3, 6.3])
-    self.assertEquals(tab.GetUnique('third', ignore_nan=False), [None, 2.2, 3.3, 6.3])
+    self.assertEqual(tab.GetUnique('first'), ['x','foo'])
+    self.assertEqual(tab.GetUnique('first', ignore_nan=False), ['x','foo', None])
+    self.assertEqual(tab.GetUnique('second'), [3,9,4,5])
+    self.assertEqual(tab.GetUnique('second', ignore_nan=False), [3,None,9,4,5])
+    self.assertEqual(tab.GetUnique('third'), [2.2, 3.3, 6.3])
+    self.assertEqual(tab.GetUnique('third', ignore_nan=False), [None, 2.2, 3.3, 6.3])
     
   def testCorrel(self):
     tab = self.CreateTestTable()
-    self.assertEquals(tab.Correl('second','third'), None)
+    self.assertEqual(tab.Correl('second','third'), None)
     tab.AddRow(['foo',4, 3.3])
     tab.AddRow([None,5, 6.3])
     tab.AddRow([None,8, 2])
-    self.assertAlmostEquals(tab.Correl('second','third'), -0.4954982578)
+    self.assertAlmostEqual(tab.Correl('second','third'), -0.4954982578)
     
   def testSpearmanCorrel(self):
     if not HAS_SCIPY_STATS:
       return
     tab = self.CreateTestTable()
-    self.assertEquals(tab.SpearmanCorrel('second','third'), None)
+    self.assertEqual(tab.SpearmanCorrel('second','third'), None)
     tab.AddRow(['foo',4, 3.3])
     tab.AddRow([None,5, 6.3])
     tab.AddRow([None,8, 2])
-    self.assertAlmostEquals(tab.SpearmanCorrel('second','third'), -0.316227766)
+    self.assertAlmostEqual(tab.SpearmanCorrel('second','third'), -0.316227766)
     
   def testExtend(self):
     '''
@@ -1804,8 +1804,8 @@ class TestTable(unittest.TestCase):
     split_exp_two=selector._ExpressionLexer(query_two)
     parsed_exp_two=selector._ParseExpression(split_exp_two)
     rpn_two=['a', 1, '=', 'a', 2, '=', 'or', 'b', 5.0, 'a', '+', '>', 'b', 2.0, '<', 'or', 'or']
-    self.assertEquals(selector._ShuntingYard(parsed_exp_one),rpn_one)
-    self.assertEquals(selector._ShuntingYard(parsed_exp_two),rpn_two)
+    self.assertEqual(selector._ShuntingYard(parsed_exp_one),rpn_one)
+    self.assertEqual(selector._ShuntingYard(parsed_exp_two),rpn_two)
 
     #check operator evaluations
     self.assertTrue(selector._EvaluateOperator('=',False,False))
diff --git a/modules/bindings/pymod/align_3dcomb.py b/modules/bindings/pymod/align_3dcomb.py
index ca709fc15fcadfe8dbb0765466cf874e3b35c231..f6a4b49ef8ed21efada63e0917eb672806ba54c5 100644
--- a/modules/bindings/pymod/align_3dcomb.py
+++ b/modules/bindings/pymod/align_3dcomb.py
@@ -44,7 +44,7 @@ def _SetupFiles(structure_list):
   
   #write out temporary pdb files
   if not all([ev.IsValid() for ev in structure_list]):
-    raise RuntimeError, "Invalid EntityView in structure_list"
+    raise RuntimeError("Invalid EntityView in structure_list")
   tpd=utils.TempDirWithFiles(structure_list)
   
   #Write out the file containing the list of all structures
@@ -71,7 +71,7 @@ def _Run3DCOMB(command,tpd):
   outfile.close()
   #check for successful completion of 3DCOMB
   if returncode!=0:
-    print "WARNING: 3DCOMB error\n"
+    print("WARNING: 3DCOMB error\n")
     raise subprocess.CalledProcessError
   return returncode
 
@@ -87,7 +87,7 @@ def _ParseOutput(tpd):
     if l.startswith('>'):
       fl=ost.FloatList()
       for i in range(3):
-        l=f.next()
+        l=next(f)
         sl=l.split(',')
         fl.extend([float(el) for el in sl[0].split()+[sl[1]]])
       fl.extend([0,0,0,1])
@@ -115,7 +115,7 @@ def AlignStructures(structure_list,apply_transform=False,name_list=None,comb_exe
   returncode=_Run3DCOMB(command,tpd)
   try:ali,Tl,results=_ParseOutput(tpd)
   except:
-    print 'could not parse output'
+    print('could not parse output')
     raise RuntimeError
   if apply_transform:
     for T,ev in zip(Tl,structure_list):
diff --git a/modules/bindings/pymod/blast.py b/modules/bindings/pymod/blast.py
index e63007f40d5b9feb0fb0c838166f35b3142ad24b..788d460255e20913a24a785e854a48e077f94ef5 100644
--- a/modules/bindings/pymod/blast.py
+++ b/modules/bindings/pymod/blast.py
@@ -103,12 +103,12 @@ def ParseBlastOutput(string, seqid_thres=0, evalue_thres=float("infinity")):
         aln=seq.CreateAlignment(query_seq, hit_seq)
         return AlignedPatch(aln, bit_score, score, evalue, seqid)
 
-    except Exception, e:
-      print str(e), query_seq, hit_seq
+    except Exception as e:
+      print(str(e), query_seq, hit_seq)
 
   try:
     doc=minidom.parseString(string)
-  except Exception, e:
+  except Exception as e:
     ost.LogError('Error while parsing BLAST output: %s' % str(e))
     return None
   hits=[]
diff --git a/modules/bindings/pymod/cadscore.py b/modules/bindings/pymod/cadscore.py
index 3b0fd0a0c1e1c04daa620d8a503cc67afbea4cf0..6d5bb0b9a84a3dbbdabc549fe0dd1a5e62064f42 100644
--- a/modules/bindings/pymod/cadscore.py
+++ b/modules/bindings/pymod/cadscore.py
@@ -246,16 +246,16 @@ def _RunCAD(tmp_dir, mode, cad_bin_path, old_regime):
 def _HasInsertionCodes(model, reference):
   for r in model.residues:
     if r.GetNumber().GetInsCode() != "\0":
-      print r
+      print(r)
       return True
   for r in reference.residues:
     if r.GetNumber().GetInsCode() != "\0":
-      print r
+      print(r)
       return True
   return False
 
 def _MapLabels(model, cad_results, label):
-  for k,v in cad_results.localAA.iteritems():
+  for k,v in cad_results.localAA.items():
     r = model.FindResidue(k[0], k[1])
     if not r.IsValid():
       raise RuntimeError("Failed to map cadscore on residues: " +
diff --git a/modules/bindings/pymod/dssp.py b/modules/bindings/pymod/dssp.py
index 7fe206d0a10e0e6d701be941ef63a10eb32aa4ff..a43696c4eefbcd80221a5b01e9e63e9b19e1bab4 100644
--- a/modules/bindings/pymod/dssp.py
+++ b/modules/bindings/pymod/dssp.py
@@ -116,9 +116,9 @@ def AssignDSSP(ent, pdb_path="", extract_burial_status=False, tmp_dir=None,
   try:
     LoadDSSP(temp_dssp_path, ent, extract_burial_status,
              entity_saved)
-  except Exception, e:
+  except Exception as e:
     # clean up
-    print "Exception in DSSP:", e
+    print("Exception in DSSP:", e)
     _Cleanup(pdb_path, temp_dssp_path, entity_saved)
     raise RuntimeError(e)
 
@@ -203,8 +203,8 @@ def LoadDSSP(file_name, model, extract_burial_status=False,
               residue.SetStringProp("burial_status", 'b')
             else:
               residue.SetStringProp("burial_status", 'e')
-      except Exception, e:
-        print "ERROR:",e
+      except Exception as e:
+        print("ERROR:",e)
         continue
 
       rtype=line[16:17]
diff --git a/modules/bindings/pymod/hbplus.py b/modules/bindings/pymod/hbplus.py
index 5a97df2c9ed42138a1fe99da3365e9ae0a45e396..39b9033597dec4cae75621e2e746f94e57774aee 100644
--- a/modules/bindings/pymod/hbplus.py
+++ b/modules/bindings/pymod/hbplus.py
@@ -108,5 +108,5 @@ def HBondScore(ent1, ent2, hbplus_bin=None):
     return 0.0
 
 if __name__=='__main__':
-  print 'HBond Score:', HBondScore(io.LoadPDB(sys.argv[1]), 
-                                   io.LoadPDB(sys.argv[2]))
\ No newline at end of file
+  print('HBond Score:', HBondScore(io.LoadPDB(sys.argv[1]), 
+                                   io.LoadPDB(sys.argv[2])))
\ No newline at end of file
diff --git a/modules/bindings/pymod/hhblits.py b/modules/bindings/pymod/hhblits.py
index 6177847980ef1aaf711d73d9854c6d0933ed83a9..94c1a643589a121db50cf3b637d826d415746104 100644
--- a/modules/bindings/pymod/hhblits.py
+++ b/modules/bindings/pymod/hhblits.py
@@ -161,34 +161,34 @@ def ParseHHblitsOutput(output):
         value_start_column = 14
         date_pattern = '%a %b %d %H:%M:%S %Y'
         header = HHblitsHeader()
-        line = lines.next()
+        line = next(lines)
         assert line.startswith('Query')
         header.query = line[value_start_column:].strip()
-        line = lines.next()
+        line = next(lines)
         assert line.startswith('Match_columns')
         header.match_columns = int(line[value_start_column:].strip())
 
-        line = lines.next()
+        line = next(lines)
         assert line.startswith('No_of_seqs')
 
-        line = lines.next()
+        line = next(lines)
         assert line.startswith('Neff')
         header.n_eff = float(line[value_start_column:].strip())
 
-        line = lines.next()
+        line = next(lines)
         assert line.startswith('Searched_HMMs')
         header.searched_hmms = int(line[value_start_column:].strip())
 
-        line = lines.next()
+        line = next(lines)
         assert line.startswith('Date')
         value = line[value_start_column:].strip()
         header.date = datetime.datetime.strptime(value, date_pattern)
 
-        line = lines.next()
+        line = next(lines)
         assert line.startswith('Command')
         header.command = line[value_start_column:].strip()
 
-        line = lines.next()
+        line = next(lines)
         assert len(line.strip()) == 0
         return header
 
@@ -196,7 +196,7 @@ def ParseHHblitsOutput(output):
         assert lines.next().startswith(' No Hit')
         hits = []
         while True:
-            line = lines.next()
+            line = next(lines)
             if len(line.strip()) == 0:
                 return hits
             hits.append(ParseHeaderLine(line))
@@ -221,7 +221,7 @@ def ParseHHblitsOutput(output):
                 # - "T <hit_id> <start> <data> <end>"
                 # - "Q <query_id> <start> <data> <end>"
                 # -> rest is to be skipped
-                line = lines.next()
+                line = next(lines)
                 if len(line.strip()) == 0:
                     continue
                 if line.startswith('Done!'):
@@ -241,7 +241,7 @@ def ParseHHblitsOutput(output):
                     templ_str = ''
                     # skip the next line. It doesn't contain information we
                     # don't already know
-                    lines.next()
+                    next(lines)
                     continue
                 assert entry_index != None
                 # Skip all "T ..." and "Q ..." lines besides the one we want
@@ -758,7 +758,7 @@ def _ParseOptions(opts):
     """
     opt_cmd = list()
     opt_str = list()
-    for k, val in opts.iteritems():
+    for k, val in opts.items():
         if type(val) == type(True):
             if val == True:
                 opt_cmd.append('-%s' % str(k))
diff --git a/modules/bindings/pymod/ialign.py b/modules/bindings/pymod/ialign.py
index d5c9f570d3a6abbaef00e2f8d1070727349b28f9..2c6090102018617b9e727afcdea3f9d722c8f87a 100644
--- a/modules/bindings/pymod/ialign.py
+++ b/modules/bindings/pymod/ialign.py
@@ -125,7 +125,7 @@ def _RuniAlign(ialign, tmp_dir, options={}):
          } 
   opts.update(options)
   cmd_opts = []
-  for k, v in opts.iteritems():
+  for k, v in opts.items():
     if type(v) == type(True):
       if v == True:
         cmd_opts.append('-%s' % str(k))
diff --git a/modules/bindings/pymod/kclust.py b/modules/bindings/pymod/kclust.py
index 5ecdf11d01a72ff8d9ba5812888b8674511ab5ba..edcbc4797f6a33669587701652b0871c728e2c38 100644
--- a/modules/bindings/pymod/kclust.py
+++ b/modules/bindings/pymod/kclust.py
@@ -84,7 +84,7 @@ def _ParseOutput(tmp_dir_name):
   #translate into final output
 
   res=list()
-  for k, v in clusters.iteritems():
+  for k, v in clusters.items():
     res.append(cluster(v, header_mapper[k]))
 
   return res
diff --git a/modules/bindings/pymod/lga.py b/modules/bindings/pymod/lga.py
index 0f5410fe3ba543a960595808ae589c9bd9cfa879..7ab23e8541c7ee59b85e6f2a5ae5b9bb3ef332e4 100644
--- a/modules/bindings/pymod/lga.py
+++ b/modules/bindings/pymod/lga.py
@@ -52,8 +52,8 @@ def _ParseGDTSection(section, residue_count):
   cutoffs=[float(e) for e in section[0].split()[2:]]
   num_ca=[int(e) for e in section[1].split()[2:]]
   gdt_ts=[float(e) for e in section[2].split()[2:]]
-  scores=dict(zip(cutoffs, gdt_ts))
-  numbers=dict(zip(cutoffs, num_ca))
+  scores=dict(list(zip(cutoffs, gdt_ts)))
+  numbers=dict(list(zip(cutoffs, num_ca)))
   factor=(1.0/(4*residue_count))*100
   ts_cutoffs=(1.0, 2.0, 4.0, 8.0)
   ha_cutoffs=(0.5, 1.0, 2.0, 4.0)  
diff --git a/modules/bindings/pymod/msms.py b/modules/bindings/pymod/msms.py
index 534cd3d8e0be5c80fcfa4638896ffbfa23bcdbf9..21ccf5125046bbbbdfebb4b18ec1ff64c17b47a4 100644
--- a/modules/bindings/pymod/msms.py
+++ b/modules/bindings/pymod/msms.py
@@ -86,7 +86,7 @@ def _SetupFiles(entity, selection):
   # select only heavy atoms if no_hydrogens is true
   entity_view=entity.Select(selection)
   if not entity_view.IsValid():
-    raise RuntimeError, "Could not create view for selection (%s)"%(selection)
+    raise RuntimeError("Could not create view for selection (%s)"%(selection))
 
   # write entity to tmp file
   tmp_file_name=os.path.join(tmp_dir_name,"entity")
@@ -118,7 +118,7 @@ def _ParseAreaFile(entity, selection, file, asa_prop, esa_prop):
   # shift first line
   area_lines = area_lines[1:]
   if view.GetAtomCount() != len(area_lines):
-      raise RuntimeError, "Atom count (%d) unequeal to number of atoms in area file (%d)" % (view.GetAtomCount(), len(area_lines))
+      raise RuntimeError("Atom count (%d) unequeal to number of atoms in area file (%d)" % (view.GetAtomCount(), len(area_lines)))
   for l in area_lines:
       atom_no, sesa, sasa = l.split()
       a = view.atoms[int(atom_no)]
@@ -154,7 +154,7 @@ def _RunMSMS(command):
 
   #check for successful completion of msms
   if proc.returncode!=0:
-    print "WARNING: msms error\n", stdout_value
+    print("WARNING: msms error\n", stdout_value)
     raise MsmsProcessError(proc.returncode, command)
 
   return stdout_value
diff --git a/modules/bindings/pymod/naccess.py b/modules/bindings/pymod/naccess.py
index 0d5566ea257d5ac69302cdcdd41ee0dc2feee088..dc65ab4ef2a0c6adaac50c452fb126c3474735d5 100644
--- a/modules/bindings/pymod/naccess.py
+++ b/modules/bindings/pymod/naccess.py
@@ -74,9 +74,9 @@ def _SetupFiles(entity, selection, scratch_dir, max_number_of_atoms):
   else:
     entity_view = entity
   if len(entity_view.atoms) > max_number_of_atoms:
-    raise RuntimeError, "Too much atoms for NACCESS (> %s)" % max_number_of_atoms
+    raise RuntimeError("Too much atoms for NACCESS (> %s)" % max_number_of_atoms)
   if not entity_view.IsValid():
-    raise RuntimeError, "Could not create view for selection (%s)"%(selection)
+    raise RuntimeError("Could not create view for selection (%s)"%(selection))
   
   # write entity to tmp file
   tmp_file_name = "entity.pdb"
@@ -162,7 +162,7 @@ def _ParseRsaFile(entity, file, asa_abs, asa_rel):
         res.SetFloatProp(asa_rel, float(rel_all) )
         res.SetFloatProp(asa_abs, float(abs_all) )
       else:
-        raise RuntimeError, "Residue Names are not the same for ResNumb: %s (%s vs %s)" % (res_number, res.name, res_name)
+        raise RuntimeError("Residue Names are not the same for ResNumb: %s (%s vs %s)" % (res_number, res.name, res_name))
       
 
 def __CleanupFiles(dir_name):
diff --git a/modules/bindings/tests/test_blast.py b/modules/bindings/tests/test_blast.py
index 18311abd9342e4b8408fc4c2a2d30534ef08ed12..fbadfccb1dd17559f9dd2db309f87fd20ff776f8 100644
--- a/modules/bindings/tests/test_blast.py
+++ b/modules/bindings/tests/test_blast.py
@@ -102,7 +102,7 @@ if __name__ == "__main__":
   try:
     blastpath=settings.Locate(('blastp','blastall'))
   except(settings.FileNotFound):
-    print "Could not find blast executable: ignoring unit tests"
+    print("Could not find blast executable: ignoring unit tests")
     sys.exit(0)
   from ost import testutils
   testutils.RunTests()
diff --git a/modules/bindings/tests/test_clustalw.py b/modules/bindings/tests/test_clustalw.py
index 8f295088ed235ab3ad401eff4525b985df51280d..222effed8a6b6924ad746a2f31eb2da9507cce15 100644
--- a/modules/bindings/tests/test_clustalw.py
+++ b/modules/bindings/tests/test_clustalw.py
@@ -76,7 +76,7 @@ if __name__ == "__main__":
   try:
     clustalw_path=settings.Locate(('clustalw', 'clustalw2'))
   except(settings.FileNotFound):
-    print "Could not find clustalw executable: ignoring unit tests"
+    print("Could not find clustalw executable: ignoring unit tests")
     sys.exit(0)
   from ost import testutils
   testutils.RunTests()
diff --git a/modules/bindings/tests/test_hhblits.py b/modules/bindings/tests/test_hhblits.py
index 2ba099ced0c4e7f5dd7d0856431cd48d22a326b2..22a4faf204033f5938aa96e5ba8b5140cc853a02 100644
--- a/modules/bindings/tests/test_hhblits.py
+++ b/modules/bindings/tests/test_hhblits.py
@@ -399,8 +399,8 @@ class TestHHblitsBindings(unittest.TestCase):
 if __name__ == "__main__":
     hhsuite_root_dir =  os.getenv('EBROOTHHMINSUITE')
     if not hhsuite_root_dir:
-        print "No environment variable 'EBROOTHHMINSUITE'. To enable the "+\
-            "unit test, this needs to point to your HHsuite installation."
+        print("No environment variable 'EBROOTHHMINSUITE'. To enable the "+\
+            "unit test, this needs to point to your HHsuite installation.")
         sys.exit(0)
     from ost import testutils
     testutils.RunTests()
diff --git a/modules/bindings/tests/test_kclust.py b/modules/bindings/tests/test_kclust.py
index fe7251613484b55ad248f21edb2df12f61c0ca3e..707288d0757ae1453b6e254348b0bfab5b26a935 100644
--- a/modules/bindings/tests/test_kclust.py
+++ b/modules/bindings/tests/test_kclust.py
@@ -38,7 +38,7 @@ if __name__ == "__main__":
   try:
     blastpath=settings.Locate(('kClust'))
   except(settings.FileNotFound):
-    print "Could not find kClust executable: ignoring unit tests"
+    print("Could not find kClust executable: ignoring unit tests")
     sys.exit(0)
   from ost import testutils
   testutils.RunTests()
diff --git a/modules/bindings/tests/test_msms.py b/modules/bindings/tests/test_msms.py
index 9041f94fab817441da03ae794fe63d793ad7d208..d0cc2c42e7ee510c3c2637afb52662191c6ba0d2 100755
--- a/modules/bindings/tests/test_msms.py
+++ b/modules/bindings/tests/test_msms.py
@@ -37,10 +37,10 @@ if __name__ == "__main__":
   try:
     msms._GetExecutable(msms_exe=None, msms_env='MSMSSERVER')
   except(settings.FileNotFound):
-    print "Could not find msms executable: ignoring unit tests"
+    print("Could not find msms executable: ignoring unit tests")
     exit(0)
   version = msms.GetVersion(msms_exe=None, msms_env='MSMSSERVER')
   if version!=VERSION_REQUIRED:
-    print "MSMS version (%s) does not match required version %s: ignoring unit tests"%(version, VERSION_REQUIRED)
+    print("MSMS version (%s) does not match required version %s: ignoring unit tests"%(version, VERSION_REQUIRED))
   from ost import testutils
   testutils.RunTests()
diff --git a/modules/bindings/tests/test_naccess.py b/modules/bindings/tests/test_naccess.py
index c2b33b06315340b10eecca93c9adfab25cf37bb7..95949301da14c109872c5b0bafba6822bf35eb68 100644
--- a/modules/bindings/tests/test_naccess.py
+++ b/modules/bindings/tests/test_naccess.py
@@ -25,7 +25,7 @@ class TestNaccessBindings(unittest.TestCase):
         except:
             excp_raised = True
             raise
-        self.assertEquals(excp_raised, False, msg="Naccess raised an "+
+        self.assertEqual(excp_raised, False, msg="Naccess raised an "+
                           "exception on a path containing a '.'. This is not "+
                           "supposed to happen.")
 
@@ -33,7 +33,7 @@ if __name__ == "__main__":
     try:
         settings.Locate("naccess")
     except:
-        print "Could not find NACCESS, could not test binding..."
+        print("Could not find NACCESS, could not test binding...")
         sys.exit(0)
     from ost import testutils
     testutils.RunTests()
diff --git a/modules/conop/pymod/cleanup.py b/modules/conop/pymod/cleanup.py
index 1a51673d778e3e51237a3d576db8e67fca75279f..6866a913aecab022914a963cf743bc45428dea72 100644
--- a/modules/conop/pymod/cleanup.py
+++ b/modules/conop/pymod/cleanup.py
@@ -79,7 +79,7 @@ def _CanonicalizeResidues(clean_entity, ed, compound_lib) :
           _DeleteSidechain(res, ed)
           for atom in res.atoms:
             atom.is_hetatom = False
-          print "Removing sidechain of %s, beacuse it has not been found in the compound library"% parent_tlc
+          print("Removing sidechain of %s, beacuse it has not been found in the compound library"% parent_tlc)
         else:
           #collect atom's names
           modif_atom_names = set([atom.name for atom in res.atoms
diff --git a/modules/conop/tests/test_cleanup.py b/modules/conop/tests/test_cleanup.py
index b0579127a404330c7de3e2b14eea8c9a840ae5e8..54d33a8bc67bf1b95d2df425ea76b8ac5927a09c 100644
--- a/modules/conop/tests/test_cleanup.py
+++ b/modules/conop/tests/test_cleanup.py
@@ -163,4 +163,4 @@ if __name__ == "__main__":
   if testutils.SetDefaultCompoundLib():
     testutils.RunTests()
   else:
-    print 'No compound library available. Ignoring test_cleanup.py tests.'
+    print('No compound library available. Ignoring test_cleanup.py tests.')
diff --git a/modules/conop/tests/test_compound.py b/modules/conop/tests/test_compound.py
index bd54b7dfaf94dda8fc2112eb56f58d831df74f44..01c59a233e263e1c9d538c6ae303a7cf5442a8d3 100644
--- a/modules/conop/tests/test_compound.py
+++ b/modules/conop/tests/test_compound.py
@@ -28,4 +28,4 @@ if __name__=='__main__':
     if testutils.SetDefaultCompoundLib():
         testutils.RunTests()
     else:
-        print 'No compound library available. Ignoring test_compound.py tests.'
\ No newline at end of file
+        print('No compound library available. Ignoring test_compound.py tests.')
\ No newline at end of file
diff --git a/modules/geom/tests/test_geom.py b/modules/geom/tests/test_geom.py
index 46063eb4fb1dab5c1f28796bb4d077710f08a097..f84daaa8407d0608c9df3fb525ecdfe9e49ccb24 100644
--- a/modules/geom/tests/test_geom.py
+++ b/modules/geom/tests/test_geom.py
@@ -30,7 +30,7 @@ import ost.geom as geom
 # which do not convert std::out_of_range to IndexError correctly.
 try:
   geom.Vec2()[3]
-except Exception, e:
+except Exception as e:
   pass
   IndexError = type(e)
 
diff --git a/modules/gfx/pymod/__init__.py b/modules/gfx/pymod/__init__.py
index d1b59f1a7cd7e59807cd7d16b7c3ce5a68d623e8..a0525f126bd47c2015efc0784c0472561892080b 100644
--- a/modules/gfx/pymod/__init__.py
+++ b/modules/gfx/pymod/__init__.py
@@ -17,7 +17,7 @@
 # 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 #------------------------------------------------------------------------------
 from _ost_gfx import *
-from py_gfx_obj import PyGfxObj
+from .py_gfx_obj import PyGfxObj
 
 WHITE=RGB(1.0,1.0,1.0)
 BLACK=RGB(0.0,0.0,0.0)
@@ -282,7 +282,7 @@ def _entity_reset(self,*args,**kwargs):
     else:
       raise TypeError("Reset: unknown option of type '%s' given"%type(a))
 
-  for key,val in kwargs.iteritems():
+  for key,val in kwargs.items():
     if key=="entity":
       if not isinstance(val,mol.EntityHandle):
         raise TypeError("Reset: expected mol.EntityHandle for 'entity' option")
diff --git a/modules/gfx/tests/test_gfx.py b/modules/gfx/tests/test_gfx.py
index fcd0f8510660904a4f07459fa4f28c6391b8489d..3889c8e2ffe62a0c64e6546f979bf5fdc9d0712b 100644
--- a/modules/gfx/tests/test_gfx.py
+++ b/modules/gfx/tests/test_gfx.py
@@ -32,7 +32,7 @@ if ost.WITH_NUMPY:
   has_numpy=True
   try:
     import numpy
-  except ImportError, e:
+  except ImportError as e:
     has_numpy=False
 else:
   has_numpy=False
diff --git a/modules/gfx/tests/test_gost_import.py b/modules/gfx/tests/test_gost_import.py
index deebd2d31d805c7c0cc190d5ad273262dc7728bf..357a0678d0e60f3b9b1aecb1d1e852340e123bb5 100644
--- a/modules/gfx/tests/test_gost_import.py
+++ b/modules/gfx/tests/test_gost_import.py
@@ -9,7 +9,7 @@ with open("test.gost","rb") as gf:
 
   while raw:
     (type, subtype,size) = struct.unpack("iiL",raw)
-    print "found type=%d, subtype=%d and blocksize=%u"%(type,subtype,size)
+    print("found type=%d, subtype=%d and blocksize=%u"%(type,subtype,size))
     if size>0:
       data = gf.read(size)
     raw = gf.read(16)
diff --git a/modules/gui/pymod/__init__.py b/modules/gui/pymod/__init__.py
index da6b1599a0b8378ba6153054c1ce72d0ca5f1581..4343829dca4a6b7a12b3215f9f0cbbad20ca20eb 100644
--- a/modules/gui/pymod/__init__.py
+++ b/modules/gui/pymod/__init__.py
@@ -24,9 +24,9 @@ import sip
 ## \brief Opens a DataViewer 
 # \sa \example fft_li.py "View Fourier Transform Example" \sa \ref modulate_image.py "Modulate Image Example"
 def _close_event_override_(event):
-  print "close event"
+  print("close event")
 def _set_data_override_(data):
-  print "set data"
+  print("set data")
 
 def CreateDataViewer(ih,flag=False):
       viewer=GostyApp.Instance().CreateDataViewer(ih)
diff --git a/modules/gui/pymod/dng/init.py b/modules/gui/pymod/dng/init.py
index 070cace7b8fbca2f9a157c1236490ebbcb605704..ed274b5d733553759d2d4b44528c0210e2d80be1 100644
--- a/modules/gui/pymod/dng/init.py
+++ b/modules/gui/pymod/dng/init.py
@@ -13,7 +13,7 @@ except ImportError:
   _img_present=False
   pass
 
-import httplib
+import http.client
 
 from PyQt5 import QtGui, QtWidgets, QtCore
 from ost.gui.scene.init_inspector import _InitInspector
@@ -116,7 +116,7 @@ def _execute_script():
   sys_argv_backup=sys.argv
   sys.argv=script_argv
   try:
-    execfile(script, __main__.__dict__)
+    exec(compile(open(script).read(), script, 'exec'), __main__.__dict__)
   finally:
     sys.argv=sys_argv_backup     
 
@@ -150,7 +150,7 @@ class OstOptionParser(optparse.OptionParser):
   def __init__(self, **kwargs):
     optparse.OptionParser.__init__(self, **kwargs)
   def exit(self, status_code, error_message):
-    print error_message,
+    print(error_message, end=' ')
     QtWidgets.QApplication.instance().exit()
     sys.exit(-1)
 
@@ -171,7 +171,7 @@ if len(parser.rargs)!=0:
     if not rargs_string.endswith('.py'):  
       loading_list.append(rargs_string)
     else:
-      print 'Error:  one of the files to load is a Python script, use -s flag to execute it\n'
+      print('Error:  one of the files to load is a Python script, use -s flag to execute it\n')
       QtWidgets.QApplication.instance().exit()
       sys.exit(-1)    
 
@@ -190,15 +190,15 @@ _ostrc=os.path.join(home, '.ostrc')
 if os.path.exists(_ostrc):
   try:
     exec(open(_ostrc))
-  except Exception, e:
-    print e
+  except Exception as e:
+    print(e)
 else:
   rcfile=open(_ostrc,"w")
-  print >> rcfile, '# This python file is parsed by ost and dng at startup'
-  print >> rcfile, '# Its content is made available in the global namespace'
-  print >> rcfile, '# It can be used to define custom variables and functions'
-  print >> rcfile, '# For example:'
-  print >> rcfile, '# IMPORTANT_DIR="path/to/important/dir"'
+  print('# This python file is parsed by ost and dng at startup', file=rcfile)
+  print('# Its content is made available in the global namespace', file=rcfile)
+  print('# It can be used to define custom variables and functions', file=rcfile)
+  print('# For example:', file=rcfile)
+  print('# IMPORTANT_DIR="path/to/important/dir"', file=rcfile)
   rcfile.close()
 
 ost.gui.PushVerbosityLevel(options.vlevel)
diff --git a/modules/gui/pymod/dng/menu.py b/modules/gui/pymod/dng/menu.py
index 425cf6ca75ff6666938fa2cc1d45e4b754f5275e..487eec75c1b28fb7e1b414f197a0ec1e44c7c7fe 100644
--- a/modules/gui/pymod/dng/menu.py
+++ b/modules/gui/pymod/dng/menu.py
@@ -52,8 +52,8 @@ class ClipWidget(QWidget):
     self.far_.setMinimum(int(bounds_near.text()))
     self.far_.setMaximum(int(bounds_far.text()))
     far = int(gfx.Scene().far)
-    if far>sys.maxint:
-      far = sys.maxint
+    if far>sys.maxsize:
+      far = sys.maxsize
     self.far_.setValue(far)
     self.auto_ = QCheckBox("Continuous Automatic Clipping")
     self.auto_.setChecked(gfx.Scene().GetAutoAutoslab())
diff --git a/modules/gui/pymod/export_message_widget.cc b/modules/gui/pymod/export_message_widget.cc
index 1893c7e874e2c05cf40f12939fa4862e99e101b1..69dd6bb3891f8333ae26bc1306bca16e6a518adb 100644
--- a/modules/gui/pymod/export_message_widget.cc
+++ b/modules/gui/pymod/export_message_widget.cc
@@ -39,9 +39,9 @@ void log_message_a(MessageWidget* message_widget, const QString& message, QMessa
 void log_message_b(MessageWidget * message_widget, object py_object)
 {
   if(py_object.ptr() != Py_None){
-    if(PyObject_IsInstance(py_object.ptr(), (PyObject*)&PyString_Type)){
-    String message = extract < std::string > (py_object);
-    message_widget->LogMessage(QString(message.c_str()));
+    if(PyObject_IsInstance(py_object.ptr(), (PyObject*)&PyUnicode_Type)){
+      String message = extract < std::string > (py_object);
+      message_widget->LogMessage(QString(message.c_str()));
     }
     else if(QStandardItem* item = get_cpp_qobject<QStandardItem>(py_object)){
       message_widget->LogMessage(item);
diff --git a/modules/gui/pymod/helpwidget.py b/modules/gui/pymod/helpwidget.py
index 9ac28e2059c2805d276b798572f48b4ecd5ebb83..aee9b19ac45e7477bbe1b4203a0bbea99d7df810 100644
--- a/modules/gui/pymod/helpwidget.py
+++ b/modules/gui/pymod/helpwidget.py
@@ -30,7 +30,7 @@ class Help:
     else:
       try:
         s=d.document(pydoc.resolve(what)[0])
-      except ImportError, detail:
+      except ImportError as detail:
         s=str(detail)
       doc_widget.doctext.setHtml(s)
       doc_widget.searchbox.setText('')
diff --git a/modules/gui/pymod/scene/color_options_widget.py b/modules/gui/pymod/scene/color_options_widget.py
index c99eb41e2993459d5fb062353b15e7cc278af9a9..da2deb806bccf7902e6c9a32d90aa59e78e78481 100644
--- a/modules/gui/pymod/scene/color_options_widget.py
+++ b/modules/gui/pymod/scene/color_options_widget.py
@@ -30,10 +30,10 @@ except ImportError:
   pass
 
 from PyQt5 import QtCore, QtWidgets, QtWidgets
-from scene_selection_helper import SelHelper
-from gradient_editor_widget import GradientEditor
-from uniform_color_widget import UniformColorWidget
-from combo_options_widget import ComboOptionsWidget
+from .scene_selection_helper import SelHelper
+from .gradient_editor_widget import GradientEditor
+from .uniform_color_widget import UniformColorWidget
+from .combo_options_widget import ComboOptionsWidget
 
 class ColorOptionsWidget(ComboOptionsWidget):
   def __init__(self, parent=None):
diff --git a/modules/gui/pymod/scene/combo_options_widget.py b/modules/gui/pymod/scene/combo_options_widget.py
index 28bacce3f156cfc6ecde2bb4d23d7db8a78a15d3..b15a8a490b619ec0ac127c3b88d92d01e874597b 100644
--- a/modules/gui/pymod/scene/combo_options_widget.py
+++ b/modules/gui/pymod/scene/combo_options_widget.py
@@ -90,7 +90,7 @@ class ComboOptionsWidget(QtWidgets.QWidget):
      This abstract method must be implemented by all subclasses. 
      It can be used to do something ;-) whenever the combobox changes its value.
     """
-    raise NotImplementedError, "Subclasses must define OnComboChange()"
+    raise NotImplementedError("Subclasses must define OnComboChange()")
 
   def OnActivate(self, item):
     return self.OnComboChange(self, item)
diff --git a/modules/gui/pymod/scene/cpk_widget.py b/modules/gui/pymod/scene/cpk_widget.py
index c40819abe636a81a94279b3f14fa96d6e3f08cd7..3ce8d05a2261679502177113aa8c10e14407dc69 100644
--- a/modules/gui/pymod/scene/cpk_widget.py
+++ b/modules/gui/pymod/scene/cpk_widget.py
@@ -21,7 +21,7 @@
 from ost import gui
 from ost import gfx
 from PyQt5 import QtCore, QtWidgets
-from render_mode_widget import RenderModeWidget
+from .render_mode_widget import RenderModeWidget
 
 #CPK Render Options
 class CPKWidget(RenderModeWidget):
diff --git a/modules/gui/pymod/scene/custom_widget.py b/modules/gui/pymod/scene/custom_widget.py
index e94eabd2d1bb9dafb173f8db951ef21b4d5ba7d1..2319e066c29888917c779405d9d383287d7bee27 100644
--- a/modules/gui/pymod/scene/custom_widget.py
+++ b/modules/gui/pymod/scene/custom_widget.py
@@ -21,7 +21,7 @@
 from ost import gui
 from ost import gfx
 from PyQt5 import QtCore, QtWidgets
-from render_mode_widget import RenderModeWidget
+from .render_mode_widget import RenderModeWidget
 
 #Custom Render Options
 class CustomWidget(RenderModeWidget):
diff --git a/modules/gui/pymod/scene/gradient_editor_widget.py b/modules/gui/pymod/scene/gradient_editor_widget.py
index 370bd51cd21eb5029e60bee9bf26c05f4fd669ca..17ea07f0da55e8602b8b15c0975b0d8ad0eac4d0 100644
--- a/modules/gui/pymod/scene/gradient_editor_widget.py
+++ b/modules/gui/pymod/scene/gradient_editor_widget.py
@@ -22,8 +22,8 @@ from ost import gui
 from ost import gfx
 from ost import mol
 from PyQt5 import QtCore, QtGui, QtWidgets
-from color_select_widget import ColorSelectWidget
-from gradient_preset_widget import GradientPresetWidget
+from .color_select_widget import ColorSelectWidget
+from .gradient_preset_widget import GradientPresetWidget
 
 #Gradient Editor
 class GradientEditor(QtWidgets.QWidget):
diff --git a/modules/gui/pymod/scene/gradient_info_handler.py b/modules/gui/pymod/scene/gradient_info_handler.py
index 747b058605f243e20c71310e5383e1f5b2ae3d42..5c741450442ab422dd057a7b23e1967cbe95a439 100644
--- a/modules/gui/pymod/scene/gradient_info_handler.py
+++ b/modules/gui/pymod/scene/gradient_info_handler.py
@@ -22,7 +22,7 @@ from ost import gui
 from ost import gfx
 from ost import info
 from PyQt5 import QtCore
-from immutable_gradient_info_handler import ImmutableGradientInfoHandler
+from .immutable_gradient_info_handler import ImmutableGradientInfoHandler
 
 #Gradient Info Handler
 class GradientInfoHandler(ImmutableGradientInfoHandler):
diff --git a/modules/gui/pymod/scene/gradient_list_model.py b/modules/gui/pymod/scene/gradient_list_model.py
index 43f3f95dbdaea38bc37d7c5a6bfdcd75a379d402..5195e7f9f5962a6be26ec8bfbe45be149dd5a497 100644
--- a/modules/gui/pymod/scene/gradient_list_model.py
+++ b/modules/gui/pymod/scene/gradient_list_model.py
@@ -3,8 +3,8 @@ from ost import gfx
 import os
 import ost
 from PyQt5 import QtCore, QtWidgets, QtGui
-from immutable_gradient_info_handler import ImmutableGradientInfoHandler
-from gradient_info_handler import GradientInfoHandler
+from .immutable_gradient_info_handler import ImmutableGradientInfoHandler
+from .gradient_info_handler import GradientInfoHandler
 
 class GradientListModel(QtCore.QAbstractListModel):
   
@@ -57,11 +57,11 @@ class GradientListModel(QtCore.QAbstractListModel):
   def LoadGradientFromInfo(self):
     if self.immutable_infoh_:
       qgradients = self.immutable_infoh_.GetQGradients()
-      for k, v in qgradients.iteritems():
+      for k, v in qgradients.items():
         self.AddItem(k, v, self.GetLastRow(), False, False)
     
     qgradients = self.infoh_.GetQGradients()
-    for k, v in qgradients.iteritems():
+    for k, v in qgradients.items():
       self.AddItem(k, v, self.GetLastRow(), True, False)
 
   def GetGradient(self, model_index):
diff --git a/modules/gui/pymod/scene/gradient_preset_widget.py b/modules/gui/pymod/scene/gradient_preset_widget.py
index 998714befd2ad9c900d634bfc55f37cfce1873cd..e1717c98fd16298a41dbd3af2be4a95055ef7615 100644
--- a/modules/gui/pymod/scene/gradient_preset_widget.py
+++ b/modules/gui/pymod/scene/gradient_preset_widget.py
@@ -27,8 +27,8 @@ from datetime import datetime
 
 from datetime import datetime
 from PyQt5 import QtCore, QtWidgets, QtGui
-from gradient_info_handler import GradientInfoHandler
-from gradient_list_model import GradientListModel
+from .gradient_info_handler import GradientInfoHandler
+from .gradient_list_model import GradientListModel
 
 
 #Gradient Preset Widget
diff --git a/modules/gui/pymod/scene/hsc_widget.py b/modules/gui/pymod/scene/hsc_widget.py
index 442639a7107e82e1224135ddfa4197e14079c24b..8ea2d4edeacfaa2aeee7b3cd3caad2f770dbcd51 100644
--- a/modules/gui/pymod/scene/hsc_widget.py
+++ b/modules/gui/pymod/scene/hsc_widget.py
@@ -21,7 +21,7 @@
 from ost import gui
 from ost import gfx
 from PyQt5 import QtCore, QtWidgets
-from render_mode_widget import RenderModeWidget
+from .render_mode_widget import RenderModeWidget
 
 #Tube Render Options
 class HSCWidget(RenderModeWidget):
diff --git a/modules/gui/pymod/scene/inspector_widget.py b/modules/gui/pymod/scene/inspector_widget.py
index 503d3acdceddf27afb5248f5d842706a66e530a6..d3d78d034d2e235f2b4c0106e331fdbec9ca7f13 100644
--- a/modules/gui/pymod/scene/inspector_widget.py
+++ b/modules/gui/pymod/scene/inspector_widget.py
@@ -24,12 +24,12 @@ from ost import gfx
 import ost
 import os
 from PyQt5 import QtCore, QtWidgets
-from toolbar_options_widget import ToolBarOptionsWidget 
-from render_options_widget import RenderOptionsWidget
-from color_options_widget import ColorOptionsWidget
+from .toolbar_options_widget import ToolBarOptionsWidget 
+from .render_options_widget import RenderOptionsWidget
+from .color_options_widget import ColorOptionsWidget
 from ost.gui.scene.scene_observer_impl import SceneObserverImpl
-from map_level_widget import AdditionalSettingsWidget
-from scene_selection_helper import SelHelper
+from .map_level_widget import AdditionalSettingsWidget
+from .scene_selection_helper import SelHelper
 
 class InspectorWidget(ToolBarOptionsWidget):
   ICONS_PATH = os.path.join(ost.GetSharedDataPath(), "scene", "icons/")
diff --git a/modules/gui/pymod/scene/line_trace_widget.py b/modules/gui/pymod/scene/line_trace_widget.py
index 569f6dfcc904cce41a41e0177000e685c6b99f05..b9c6d769572884270a79e7ae386cc6363ad742fa 100644
--- a/modules/gui/pymod/scene/line_trace_widget.py
+++ b/modules/gui/pymod/scene/line_trace_widget.py
@@ -21,7 +21,7 @@
 from ost import gui
 from ost import gfx
 from PyQt5 import QtCore, QtWidgets
-from render_mode_widget import RenderModeWidget
+from .render_mode_widget import RenderModeWidget
 
 #Simple Render Options
 class LineTraceWidget(RenderModeWidget):
diff --git a/modules/gui/pymod/scene/map_level_widget.py b/modules/gui/pymod/scene/map_level_widget.py
index b5c0905de1472dc4dbef3d122caaa3cda608074b..e9a890be8e161f3fa38764d659a808926c2e6030 100644
--- a/modules/gui/pymod/scene/map_level_widget.py
+++ b/modules/gui/pymod/scene/map_level_widget.py
@@ -28,7 +28,7 @@ except ImportError:
   pass
 from PyQt5 import QtCore, QtWidgets
 
-from preset_widget import PresetWidget
+from .preset_widget import PresetWidget
 
 class AdditionalSettingsWidget(QtWidgets.QStackedWidget):
   def __init__(self, parent=None):
diff --git a/modules/gui/pymod/scene/preset.py b/modules/gui/pymod/scene/preset.py
index 279765d7cb6f609c93a44a68e4ccdb02c66b6620..f06a477dfe1a7759cb382ebc7d73a51d1b99d03d 100644
--- a/modules/gui/pymod/scene/preset.py
+++ b/modules/gui/pymod/scene/preset.py
@@ -106,7 +106,7 @@ class Preset:
             op = op_class.FromInfo(op_group)
             class_order_dict[index]=op
     for i in range(0, len(class_order_dict)):
-      if(class_order_dict.has_key(i)):
+      if(i in class_order_dict):
         preset.AddOp(class_order_dict[i])
     return preset
     
diff --git a/modules/gui/pymod/scene/preset_editor_list_model.py b/modules/gui/pymod/scene/preset_editor_list_model.py
index 35fac9836008178d43be9e80849bcbdc907f8249..a878006956656e70f2bb8e5e41fb7491cbdd694a 100644
--- a/modules/gui/pymod/scene/preset_editor_list_model.py
+++ b/modules/gui/pymod/scene/preset_editor_list_model.py
@@ -3,7 +3,7 @@ from ost import gfx
 import os
 import ost
 from PyQt5 import QtCore
-from preset import Preset
+from .preset import Preset
 
 class PresetEditorListModel(QtCore.QAbstractListModel):
   def __init__(self, preset, parent=None, *args): 
diff --git a/modules/gui/pymod/scene/preset_editor_widget.py b/modules/gui/pymod/scene/preset_editor_widget.py
index c5a314efb6129d1db2d3b24d7b1da9f326d9c034..5c3ce6aa68be45de566eaaeaf75e24ed43e30384 100644
--- a/modules/gui/pymod/scene/preset_editor_widget.py
+++ b/modules/gui/pymod/scene/preset_editor_widget.py
@@ -24,21 +24,21 @@ from ost import mol
 from datetime import datetime
 from datetime import datetime
 from PyQt5 import QtCore, QtWidgets, QtGui
-from color_select_widget import ColorSelectWidget
-from gradient_preset_widget import GradientPresetWidget
-from gradient_editor_widget import GradientPreview
-from gradient_editor_widget import GradientEdit
-from preset_editor_list_model import PresetEditorListModel
-from immutable_gradient_info_handler import ImmutableGradientInfoHandler
-from query_editor import QueryEditorWidget
+from .color_select_widget import ColorSelectWidget
+from .gradient_preset_widget import GradientPresetWidget
+from .gradient_editor_widget import GradientPreview
+from .gradient_editor_widget import GradientEdit
+from .preset_editor_list_model import PresetEditorListModel
+from .immutable_gradient_info_handler import ImmutableGradientInfoHandler
+from .query_editor import QueryEditorWidget
 from ost.mol import Prop
 from ost.gfx import ByElementColorOp
 from ost.gfx import ByChainColorOp
 from ost.gfx import GradientLevelColorOp
 from ost.gfx import UniformColorOp
-from preset import Preset
-from render_op import RenderOp
-from visibility_op import VisibilityOp
+from .preset import Preset
+from .render_op import RenderOp
+from .visibility_op import VisibilityOp
 
 #Preset Editor
 class PresetEditor(QtWidgets.QDialog):
diff --git a/modules/gui/pymod/scene/preset_info_handler.py b/modules/gui/pymod/scene/preset_info_handler.py
index 25237b3bd96072905b4578791d1177b71614345e..de133166829bee1314d02763eabe9591f6ba7e07 100644
--- a/modules/gui/pymod/scene/preset_info_handler.py
+++ b/modules/gui/pymod/scene/preset_info_handler.py
@@ -21,7 +21,7 @@
 from ost import gui
 from ost import gfx
 from ost import info
-from immutable_preset_info_handler import ImmutablePresetInfoHandler
+from .immutable_preset_info_handler import ImmutablePresetInfoHandler
 
 #Preset Info Handler
 class PresetInfoHandler(ImmutablePresetInfoHandler):
diff --git a/modules/gui/pymod/scene/preset_list_model.py b/modules/gui/pymod/scene/preset_list_model.py
index dbe1926ade00e007ccffa8fb051d4a5f4730b81d..6bdf05db722822ad3e0227dde2315eaaa8e7a8a0 100644
--- a/modules/gui/pymod/scene/preset_list_model.py
+++ b/modules/gui/pymod/scene/preset_list_model.py
@@ -3,8 +3,8 @@ from ost import gfx
 import os
 import ost
 from PyQt5 import QtCore, QtWidgets, QtGui
-from immutable_preset_info_handler import ImmutablePresetInfoHandler
-from preset_info_handler import PresetInfoHandler
+from .immutable_preset_info_handler import ImmutablePresetInfoHandler
+from .preset_info_handler import PresetInfoHandler
 class PresetListModel(QtCore.QAbstractListModel):
   
   IMMUTABLE_PRESET_PATH = os.path.join(ost.GetSharedDataPath(),"scene", 
@@ -64,11 +64,11 @@ class PresetListModel(QtCore.QAbstractListModel):
   def LoadPresetsFromInfo(self):
     if self.immutable_infoh_:
       presets = self.immutable_infoh_.GetPresets()
-      for k, v in presets.iteritems():
+      for k, v in presets.items():
         self.AddItem(v, self.GetLastRow(), False, False)
     
     presets = self.infoh_.GetPresets()
-    for k, v in presets.iteritems():
+    for k, v in presets.items():
       self.AddItem(v, self.GetLastRow(), True, False)
 
   def GetPreset(self, model_index):
diff --git a/modules/gui/pymod/scene/preset_widget.py b/modules/gui/pymod/scene/preset_widget.py
index aec2f2d29e1bc8ef51c39f5357a7d4d6f15c6c5c..878e5268c87119cbb9a55f733c8914bd7cb392c8 100644
--- a/modules/gui/pymod/scene/preset_widget.py
+++ b/modules/gui/pymod/scene/preset_widget.py
@@ -23,10 +23,10 @@ import ost
 import os
 from datetime import datetime
 from PyQt5 import QtCore, QtWidgets, QtGui
-from scene_selection_helper import SelHelper
-from preset_list_model import PresetListModel
-from preset_editor_widget import PresetEditor
-from preset import Preset
+from .scene_selection_helper import SelHelper
+from .preset_list_model import PresetListModel
+from .preset_editor_widget import PresetEditor
+from .preset import Preset
 
 class PresetWidget(QtWidgets.QWidget):
   PRESET_XML_FILE = os.path.join(ost.GetSharedDataPath(), "scene", "presets.xml")
diff --git a/modules/gui/pymod/scene/remote.py b/modules/gui/pymod/scene/remote.py
index 3f4459914957e68e201f163d6a880726fd51102a..dea7de08d7ec9db00b309c92db634941ee2ecae2 100644
--- a/modules/gui/pymod/scene/remote.py
+++ b/modules/gui/pymod/scene/remote.py
@@ -28,7 +28,7 @@ class RemoteLoader(QtWidgets.QWidget):
   def _RemoteMenu(self):
     menu = QtWidgets.QMenu()
     action_group = QtWidgets.QActionGroup(menu)
-    for k,v in REMOTE_REPOSITORIES.iteritems():
+    for k,v in REMOTE_REPOSITORIES.items():
       action = menu.addAction(v.name)
       action.setCheckable(True)
       if k == 'pdb':
@@ -56,13 +56,13 @@ class RemoteLoader(QtWidgets.QWidget):
     for split_id in split_ids:
       try:
         ent = RemoteLoad(split_id, from_repo=self._current_repo)
-      except Exception, e:
+      except Exception as e:
         LogError(str(e))
         continue
       g = gfx.Entity(split_id, ent)
       try:
         gfx.Scene().Add(g)
-      except Exception, e:
+      except Exception as e:
         LogError(str(e))
 
 remote_loader=RemoteLoader()
diff --git a/modules/gui/pymod/scene/render_mode_widget.py b/modules/gui/pymod/scene/render_mode_widget.py
index 31b33ddd7ce25071f0387d460e2b04a3dfead903..520f6be6524741ccc1cc706cba0a15de88bb25e5 100644
--- a/modules/gui/pymod/scene/render_mode_widget.py
+++ b/modules/gui/pymod/scene/render_mode_widget.py
@@ -31,10 +31,10 @@ class RenderModeWidget(QtWidgets.QWidget):
     self.entities_ = set()
 
   def GetText(self):
-    raise NotImplementedError, "Subclasses must define GetText()"
+    raise NotImplementedError("Subclasses must define GetText()")
   
   def GetRenderMode(self):
-    raise NotImplementedError, "Subclasses must define GetRenderMode()"
+    raise NotImplementedError("Subclasses must define GetRenderMode()")
     
   def UpdateGui(self, options):
     pass
diff --git a/modules/gui/pymod/scene/render_options_widget.py b/modules/gui/pymod/scene/render_options_widget.py
index 90cc213e827c648244487c9e23737a37d43ad635..069b87ddaac82af33c7cd732230081a85fe65027 100644
--- a/modules/gui/pymod/scene/render_options_widget.py
+++ b/modules/gui/pymod/scene/render_options_widget.py
@@ -23,22 +23,22 @@ from ost import gui
 from ost import gfx
 try: 
   from ost import img
-  from wireframe_widget import WireframeWidget
+  from .wireframe_widget import WireframeWidget
   _img_present=True
 except ImportError:
   _img_present=False
   pass
 from PyQt5 import QtCore, QtWidgets
-from scene_selection_helper import SelHelper
-from combo_options_widget import ComboOptionsWidget
-from custom_widget import CustomWidget
-from cpk_widget import CPKWidget
-from tube_widget import TubeWidget
-from simple_widget import SimpleWidget
-from sline_widget import SlineWidget
-from hsc_widget import HSCWidget
-from trace_widget import TraceWidget
-from line_trace_widget import LineTraceWidget
+from .scene_selection_helper import SelHelper
+from .combo_options_widget import ComboOptionsWidget
+from .custom_widget import CustomWidget
+from .cpk_widget import CPKWidget
+from .tube_widget import TubeWidget
+from .simple_widget import SimpleWidget
+from .sline_widget import SlineWidget
+from .hsc_widget import HSCWidget
+from .trace_widget import TraceWidget
+from .line_trace_widget import LineTraceWidget
 
 class RenderOptionsWidget(ComboOptionsWidget):
   def __init__(self, parent=None):
diff --git a/modules/gui/pymod/scene/simple_widget.py b/modules/gui/pymod/scene/simple_widget.py
index 53e172afd94d9b9620830dc9dd841c3f8d702478..198426dd1100bdacde9fa520a05aeb99754972fd 100644
--- a/modules/gui/pymod/scene/simple_widget.py
+++ b/modules/gui/pymod/scene/simple_widget.py
@@ -21,7 +21,7 @@
 from ost import gui
 from ost import gfx
 from PyQt5 import QtCore, QtWidgets
-from render_mode_widget import RenderModeWidget
+from .render_mode_widget import RenderModeWidget
 
 #Simple Render Options
 class SimpleWidget(RenderModeWidget):
diff --git a/modules/gui/pymod/scene/sline_widget.py b/modules/gui/pymod/scene/sline_widget.py
index c42f2f98ffd5875ab7501110beeb552a03d26a5c..890506beda1da1febaeccb1c3c63fc2ec60103df 100644
--- a/modules/gui/pymod/scene/sline_widget.py
+++ b/modules/gui/pymod/scene/sline_widget.py
@@ -21,7 +21,7 @@
 from ost import gui
 from ost import gfx
 from PyQt5 import QtCore, QtWidgets
-from render_mode_widget import RenderModeWidget
+from .render_mode_widget import RenderModeWidget
 
 #Simple Render Options
 class SlineWidget(RenderModeWidget):
diff --git a/modules/gui/pymod/scene/toolbar_options_widget.py b/modules/gui/pymod/scene/toolbar_options_widget.py
index 55e45b04652bd35f57e7a89983fb346bd7c0f663..638ee2228b25638c3142b1c732bea8ecd5954c99 100644
--- a/modules/gui/pymod/scene/toolbar_options_widget.py
+++ b/modules/gui/pymod/scene/toolbar_options_widget.py
@@ -100,7 +100,7 @@ class ToolBarOptionsWidget(QtWidgets.QWidget):
      This abstract method must be implemented by all subclasses. 
      It can be used to do something ;-) whenever the combobox changes its value.
     """
-    raise NotImplementedError, "Subclasses must define OnComboChange()"
+    raise NotImplementedError("Subclasses must define OnComboChange()")
   
   def DoResize(self):
     item = self.__GetCurrentWidget()
diff --git a/modules/gui/pymod/scene/trace_widget.py b/modules/gui/pymod/scene/trace_widget.py
index 8705d18d8628e92c7a7d1d4ed6033883c02feb5f..b68663e700eb5091eb8150f92faf959b671e25a8 100644
--- a/modules/gui/pymod/scene/trace_widget.py
+++ b/modules/gui/pymod/scene/trace_widget.py
@@ -21,7 +21,7 @@
 from ost import gui
 from ost import gfx
 from PyQt5 import QtCore, QtWidgets
-from render_mode_widget import RenderModeWidget
+from .render_mode_widget import RenderModeWidget
 
 #Trace Render Options
 class TraceWidget(RenderModeWidget):
diff --git a/modules/gui/pymod/scene/tube_widget.py b/modules/gui/pymod/scene/tube_widget.py
index 682fd0b05796c8bec84639204d0b3faf95eb4b85..2d7538f4d36473982a08e3cd4536410f1a539a40 100644
--- a/modules/gui/pymod/scene/tube_widget.py
+++ b/modules/gui/pymod/scene/tube_widget.py
@@ -21,7 +21,7 @@
 from ost import gui
 from ost import gfx
 from PyQt5 import QtCore, QtWidgets
-from render_mode_widget import RenderModeWidget
+from .render_mode_widget import RenderModeWidget
 
 #Tube Render Options
 class TubeWidget(RenderModeWidget):
diff --git a/modules/gui/pymod/scene/uniform_color_widget.py b/modules/gui/pymod/scene/uniform_color_widget.py
index 2459cbc4c1a3db3e8b01cb5b6abe7e250145630e..fb97cb85366cad309d6bbcbe7a41c45d7e73f5c0 100644
--- a/modules/gui/pymod/scene/uniform_color_widget.py
+++ b/modules/gui/pymod/scene/uniform_color_widget.py
@@ -28,7 +28,7 @@ except ImportError:
   _img_present=False
   pass
 from PyQt5 import QtCore, QtWidgets, QtGui
-from color_select_widget import ColorSelectWidget
+from .color_select_widget import ColorSelectWidget
 
 #Uniform Color Widget
 class UniformColorWidget(QtWidgets.QWidget):
diff --git a/modules/gui/pymod/scene/wireframe_widget.py b/modules/gui/pymod/scene/wireframe_widget.py
index 458a8f09b1d466b36b75e92a482626f16c9adb73..76f2662f313ee72e659346e14ab041b905bd8701 100644
--- a/modules/gui/pymod/scene/wireframe_widget.py
+++ b/modules/gui/pymod/scene/wireframe_widget.py
@@ -27,7 +27,7 @@ try:
 except ImportError:
   _img_present=False
   pass
-from scene_selection_helper import SelHelper
+from .scene_selection_helper import SelHelper
 
 #Wireframe Options
 class WireframeWidget(QtWidgets.QWidget):
diff --git a/modules/gui/pymod/table.py b/modules/gui/pymod/table.py
index d45b5d74e2b98c928f139273f4fc0ef9e8170b49..cc6537a9b941acd4df1276fb97457269640a5e50 100644
--- a/modules/gui/pymod/table.py
+++ b/modules/gui/pymod/table.py
@@ -51,7 +51,7 @@ class Table(QTableView):
      QObject.connect(self, SIGNAL('doubleClicked(QModelIndex)'), 
                      self.OnDoubleClick)
   def OnDoubleClick(self, model_index):
-    print 'DOUBLE'
+    print('DOUBLE')
     if not self.double_click:
       return
     row = table.TableRow(self._model.table.rows[model_index.row()],
diff --git a/modules/gui/pymod/trajectory_viewer.py b/modules/gui/pymod/trajectory_viewer.py
index 133091fce1b3b1a4c7b6e876f7789c1c3a8b846c..32960ffa1fec180d66c9ce115c33ae247689fe72 100644
--- a/modules/gui/pymod/trajectory_viewer.py
+++ b/modules/gui/pymod/trajectory_viewer.py
@@ -255,9 +255,9 @@ class TrajWidget(_QWidget):
     ref_eh=self.ehlist_[self.ref_index_]
     ref_v=ref_eh.Select(str(self._align_selection.text()))
     if ref_v.GetAtomCount()<=3:
-      print 'not enough atoms for alignment'
+      print('not enough atoms for alignment')
       return
-    for i,t,eh in zip(range(len(self.trajlist_)),self.trajlist_,self.ehlist_):
+    for i,t,eh in zip(list(range(len(self.trajlist_))),self.trajlist_,self.ehlist_):
       t=_ost.mol.alg.SuperposeFrames(t,eh.Select(str(self._align_selection.text())),ref_v)
       self.trajlist_[i]=t
       
@@ -342,10 +342,10 @@ class TrajWidget(_QWidget):
   def SetSpeed(self,val):
   #Value should be between 0 and 1
     if not (val<=1. and val >=0.):
-      print 'Speed should be set between 0 and 1'
+      print('Speed should be set between 0 and 1')
       return
     else:
       val=self._speed_slider_min-val*(self._speed_slider_min-self._speed_slider_max)
       self._SetSpeedSliderPos(val)
-      print val
+      print(val)
       
diff --git a/modules/gui/pymod/wrap_gui.cc b/modules/gui/pymod/wrap_gui.cc
index b24863ca8b04e04d9b6125e77e4074fb988c57e2..64952c82925fe797eedac34d6c21922d48b3a634 100644
--- a/modules/gui/pymod/wrap_gui.cc
+++ b/modules/gui/pymod/wrap_gui.cc
@@ -77,7 +77,7 @@ namespace {
 
     static void* convertible(PyObject* obj_ptr)
     {
-      if (!PyString_Check(obj_ptr)) return 0;
+      if (!PyUnicode_Check(obj_ptr)) return 0;
       return obj_ptr;
     }
 
@@ -85,8 +85,15 @@ namespace {
       PyObject* obj_ptr,
       boost::python::converter::rvalue_from_python_stage1_data* data)
     {
-      const char* value = PyString_AsString(obj_ptr);
-      if (value == 0) boost::python::throw_error_already_set();
+      PyObject* temp_bytes = PyUnicode_AsEncodedString(obj_ptr, "UTF-8", "strict");
+      char* value = NULL;
+      if (temp_bytes != NULL) {
+        value = PyBytes_AS_STRING(temp_bytes);
+        value = strdup(value);
+        Py_DECREF(temp_bytes);
+      }
+      if (value == NULL) boost::python::throw_error_already_set();
+
       void* storage = (
         (boost::python::converter::rvalue_from_python_storage<QString>*)
           data)->storage.bytes;
diff --git a/modules/gui/src/python_shell/python_interpreter_worker.hh b/modules/gui/src/python_shell/python_interpreter_worker.hh
index 557604900e046dee0c46fec77550a4b1e2292ad4..66639a2cdb118324ca2a03e8bd42235492d1a51c 100644
--- a/modules/gui/src/python_shell/python_interpreter_worker.hh
+++ b/modules/gui/src/python_shell/python_interpreter_worker.hh
@@ -23,8 +23,12 @@
 
 // workaround for QTBUG-22829: https://bugreports.qt-project.org/browse/QTBUG-22829
 #ifndef Q_MOC_RUN
+  // https://stackoverflow.com/questions/23068700/embedding-python3-in-qt-5
+  #pragma push_macro("slots")
+  #undef slots
   #include <boost/python.hpp>
   #include <boost/shared_ptr.hpp>
+  #pragma pop_macro("slots")
 #endif
 #include "output_redirector.hh"
 #include <csignal>
diff --git a/modules/io/pymod/__init__.py b/modules/io/pymod/__init__.py
index 2a823211858250b03cb7d0a0bcc63da3927ef7b5..eee7af12d9d8ea7c710f53f73b41e902ac7de2b3 100644
--- a/modules/io/pymod/__init__.py
+++ b/modules/io/pymod/__init__.py
@@ -16,7 +16,7 @@
 # along with this library; if not, write to the Free Software Foundation, Inc.,
 # 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 #------------------------------------------------------------------------------
-import os, tempfile, ftplib, httplib
+import os, tempfile, ftplib, http.client
 
 from _ost_io import *
 from ost import mol, geom, conop, seq
diff --git a/modules/io/pymod/remote.py b/modules/io/pymod/remote.py
index 1f593060f1f0b583996253be6e8015db11bc4aa0..c0d8990e0902dbc9cde84d04522852b53b022abe 100644
--- a/modules/io/pymod/remote.py
+++ b/modules/io/pymod/remote.py
@@ -17,7 +17,7 @@
 # 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 #------------------------------------------------------------------------------
 
-import urllib2
+import urllib.request, urllib.error, urllib.parse
 import tempfile
 
 from ost.io import LoadPDB, LoadMMCIF
@@ -49,12 +49,12 @@ class RemoteRepository:
       tmp_file_suffix+='.gz'
 
     try:
-      connection = urllib2.urlopen(remote_url)
+      connection = urllib.request.urlopen(remote_url)
       if hasattr(connection, 'code'):
         status = connection.code
       else:
         status = connection.getcode()
-    except urllib2.HTTPError, e:
+    except urllib.error.HTTPError as e:
       status = e.code
     if status != 200:
       raise IOError('Could not load %s from %s (status code %d, url %s)' \
diff --git a/modules/io/tests/test_io_mmcif.py b/modules/io/tests/test_io_mmcif.py
index e0672fc39e30c87f07288cef61c6ba321d9d3971..8a3773b0aca0322ac5782f414fd73ba68fa93658 100644
--- a/modules/io/tests/test_io_mmcif.py
+++ b/modules/io/tests/test_io_mmcif.py
@@ -10,51 +10,51 @@ class TestMMCifInfo(unittest.TestCase):
     c = io.MMCifInfoCitation()
     # test ID setting/ getting
     c.SetID('ID')
-    self.assertEquals(c.GetID(), 'ID')
+    self.assertEqual(c.GetID(), 'ID')
     # test CAS setting/ getting
     c.SetCAS('FOO')
-    self.assertEquals(c.GetCAS(), 'FOO')
+    self.assertEqual(c.GetCAS(), 'FOO')
     # test ISBN setting/ getting
     c.SetISBN('0-0-0-0-0-0')
-    self.assertEquals(c.GetISBN(), '0-0-0-0-0-0')
+    self.assertEqual(c.GetISBN(), '0-0-0-0-0-0')
     # test published_in setting/ getting
     c.SetPublishedIn('Best Book Ever')
-    self.assertEquals(c.GetPublishedIn(), 'Best Book Ever')
+    self.assertEqual(c.GetPublishedIn(), 'Best Book Ever')
     # test volume setting/ getting
     c.SetVolume('3')
-    self.assertEquals(c.GetVolume(), '3')
+    self.assertEqual(c.GetVolume(), '3')
     # test page setting/ getting
     c.SetPageFirst('1')
-    self.assertEquals(c.GetPageFirst(), '1')
+    self.assertEqual(c.GetPageFirst(), '1')
     c.SetPageLast('10')
-    self.assertEquals(c.GetPageLast(), '10')
+    self.assertEqual(c.GetPageLast(), '10')
     # test doi setting/ getting
     c.SetDOI('HERE')
-    self.assertEquals(c.GetDOI(), 'HERE')
+    self.assertEqual(c.GetDOI(), 'HERE')
     # test PubMed setting/ getting
     c.SetPubMed(815)
-    self.assertEquals(c.GetPubMed(), 815)
+    self.assertEqual(c.GetPubMed(), 815)
     # test year setting/ getting
     c.SetYear(815)
-    self.assertEquals(c.GetYear(), 815)
+    self.assertEqual(c.GetYear(), 815)
     # test title setting/ getting
     c.SetTitle('Foo')
-    self.assertEquals(c.GetTitle(), 'Foo')
+    self.assertEqual(c.GetTitle(), 'Foo')
     # test book_publisher set & get
     c.SetBookPublisher("Hugo")
-    self.assertEquals(c.GetBookPublisher(), "Hugo")
+    self.assertEqual(c.GetBookPublisher(), "Hugo")
     # test book_publisher_city set & get
     c.SetBookPublisherCity("Basel")
-    self.assertEquals(c.book_publisher_city, "Basel")
+    self.assertEqual(c.book_publisher_city, "Basel")
     # test citation type
     self.assertTrue(c.IsCitationTypeUnknown())
-    self.assertEquals(c.citation_type, io.MMCifInfoCType.Unknown)
+    self.assertEqual(c.citation_type, io.MMCifInfoCType.Unknown)
     # test auhtors setting/ getting
     s = ost.StringList()
     s.append('Foo')
     c.SetAuthorList(s)
     s2 = c.GetAuthorList()
-    self.assertEquals(s2[0], 'Foo')
+    self.assertEqual(s2[0], 'Foo')
 
     i = io.MMCifInfo()
     i.SetMethod('Deep-Fry')
@@ -64,31 +64,31 @@ class TestMMCifInfo(unittest.TestCase):
     i.AddAuthorsToCitation('ID', s)
 
     cl = i.GetCitations()
-    self.assertEquals(len(cl), 1)
+    self.assertEqual(len(cl), 1)
     al = cl[0].GetAuthorList()
-    self.assertEquals(len(al), 2)
-    self.assertEquals(al[0], 'Foo')
-    self.assertEquals(al[1], 'Bar')
+    self.assertEqual(len(al), 2)
+    self.assertEqual(al[0], 'Foo')
+    self.assertEqual(al[1], 'Bar')
 
-    self.assertEquals(i.GetMethod(), 'Deep-Fry')
-    self.assertEquals(i.GetResolution(), 2.0)
+    self.assertEqual(i.GetMethod(), 'Deep-Fry')
+    self.assertEqual(i.GetResolution(), 2.0)
 
 
   def test_mmcifinfo_biounit(self):
     b = io.MMCifInfoBioUnit()
     b.SetDetails('Details')
     b.SetMethodDetails('MethodDetails')
-    self.assertEquals(b.GetDetails(), 'Details')
-    self.assertEquals(b.GetMethodDetails(), 'MethodDetails')
+    self.assertEqual(b.GetDetails(), 'Details')
+    self.assertEqual(b.GetMethodDetails(), 'MethodDetails')
     b.method_details = 'AttrMethodDetails'
-    self.assertEquals(b.method_details, 'AttrMethodDetails')
+    self.assertEqual(b.method_details, 'AttrMethodDetails')
     b.AddChain('A')
     b.AddChain('B')
     cl = b.GetChainList()
     il = b.GetChainIntervalList()
-    self.assertEquals(cl[0], 'A')
-    self.assertEquals(il[0][0], 0)
-    self.assertEquals(il[0][1], 2)
+    self.assertEqual(cl[0], 'A')
+    self.assertEqual(il[0][0], 0)
+    self.assertEqual(il[0][1], 2)
     s = ost.StringList()
     s.append('B')
     s.append('C')
@@ -96,11 +96,11 @@ class TestMMCifInfo(unittest.TestCase):
     b.SetChainList(s)
     cl = b.GetChainList()
     il = b.GetChainIntervalList()
-    self.assertEquals(il[0][0], 0)
-    self.assertEquals(il[0][1], 3)
-    self.assertEquals(cl[0], 'B')
-    self.assertEquals(cl[1], 'C')
-    self.assertEquals(cl[2], 'D')
+    self.assertEqual(il[0][0], 0)
+    self.assertEqual(il[0][1], 3)
+    self.assertEqual(cl[0], 'B')
+    self.assertEqual(cl[1], 'C')
+    self.assertEqual(cl[2], 'D')
 
     i = io.MMCifInfo()
     i.AddBioUnit(b)
@@ -110,40 +110,40 @@ class TestMMCifInfo(unittest.TestCase):
 
     bl = i.GetBioUnits()
     il = bl[0].GetChainIntervalList()
-    self.assertEquals(il[0][0], 0)
-    self.assertEquals(il[0][1], 3)
-    self.assertEquals(il[1][0], 3)
-    self.assertEquals(il[1][1], 6)
-    self.assertEquals(len(bl), 2)
+    self.assertEqual(il[0][0], 0)
+    self.assertEqual(il[0][1], 3)
+    self.assertEqual(il[1][0], 3)
+    self.assertEqual(il[1][1], 6)
+    self.assertEqual(len(bl), 2)
 
 
   def test_mmcifinfo_transoperation(self):
     o = io.MMCifInfoTransOp()
     o.SetID("1")
-    self.assertEquals(o.GetID(), '1')
+    self.assertEqual(o.GetID(), '1')
     o.SetType("identity operation")
-    self.assertEquals(o.GetType(), 'identity operation')
+    self.assertEqual(o.GetType(), 'identity operation')
     o.SetVector(1.0, 2.0, 3.0)
-    self.assertEquals(o.GetVector().x, 1.0)
-    self.assertEquals(o.GetVector().y, 2.0)
-    self.assertEquals(o.GetVector().z, 3.0)
+    self.assertEqual(o.GetVector().x, 1.0)
+    self.assertEqual(o.GetVector().y, 2.0)
+    self.assertEqual(o.GetVector().z, 3.0)
     o.SetMatrix(1, 2, 3, 4, 5, 6, 7, 8, 9)
-    self.assertEquals(geom.Equal(o.GetMatrix(),
+    self.assertEqual(geom.Equal(o.GetMatrix(),
                                  geom.Mat3(1, 2, 3, 4, 5, 6, 7, 8, 9)), True)
 
     i = io.MMCifInfo()
     i.AddOperation(o)
     ol = i.GetOperations()
-    self.assertEquals(ol[0].GetID(), '1')
+    self.assertEqual(ol[0].GetID(), '1')
 
     b = io.MMCifInfoBioUnit()
     b.AddOperations(ol)
     oll = b.GetOperations()
-    self.assertEquals(oll[0][0].GetID(), '1')
+    self.assertEqual(oll[0][0].GetID(), '1')
     tr_ol = b.GetOperationsIntervalList()
-    self.assertEquals(len(tr_ol), 1)
-    self.assertEquals(tr_ol[0][0], 0)
-    self.assertEquals(tr_ol[0][1], 1)
+    self.assertEqual(len(tr_ol), 1)
+    self.assertEqual(tr_ol[0][0], 0)
+    self.assertEqual(tr_ol[0][1], 1)
 
   def test_mmcifinfo_biounit_pdbize_with_multiple_transforms(self):
     ent, seqres, info = io.LoadMMCIF("testfiles/mmcif/multiple_transforms.cif.gz",
@@ -151,11 +151,11 @@ class TestMMCifInfo(unittest.TestCase):
                                      info=True)
     pdb_ent = info.GetBioUnits()[0].PDBize(ent)
     chains = pdb_ent.chains
-    self.assertEquals(''.join([c.name for c in chains]), 
+    self.assertEqual(''.join([c.name for c in chains]), 
                       'A_-BCD')
     ligand_chain = chains[1]
     ligand_residues = ligand_chain.residues
-    self.assertEquals([r.number for r in ligand_residues],
+    self.assertEqual([r.number for r in ligand_residues],
                       [mol.ResNum(1), mol.ResNum(2), mol.ResNum(3), mol.ResNum(4)])
 
   def test_mmcifinfo_biounit_pdbize(self):
@@ -165,22 +165,22 @@ class TestMMCifInfo(unittest.TestCase):
     pdb_ent = info.GetBioUnits()[0].PDBize(ent)
     pdb_seqres_ent = info.GetBioUnits()[0].PDBize(ent, seqres)
 
-    self.assertEquals(''.join([c.name for c in pdb_ent.chains]),
+    self.assertEqual(''.join([c.name for c in pdb_ent.chains]),
                       'AB_-CDEFGH')
-    self.assertEquals([c.residue_count for c in pdb_ent.chains],
+    self.assertEqual([c.residue_count for c in pdb_ent.chains],
                       [415, 414, 64, 3816, 415, 414, 415, 414, 415, 414])
-    self.assertEquals([c.atom_count for c in pdb_ent.chains],
+    self.assertEqual([c.atom_count for c in pdb_ent.chains],
                       [3231, 3223, 268, 3816, 3231, 3223, 3231, 3223, 3231, 3223])
-    self.assertEquals([c.GetBondCount() for c in pdb_ent.chains],
+    self.assertEqual([c.GetBondCount() for c in pdb_ent.chains],
                       [3311, 3303, 204, 0, 3311, 3303, 3311, 3303, 3311, 3303])
 
-    self.assertEquals(''.join([c.name for c in pdb_seqres_ent.chains]),
+    self.assertEqual(''.join([c.name for c in pdb_seqres_ent.chains]),
                       ''.join([c.name for c in pdb_ent.chains]))
-    self.assertEquals([c.residue_count for c in pdb_seqres_ent.chains],
+    self.assertEqual([c.residue_count for c in pdb_seqres_ent.chains],
                       [c.residue_count for c in pdb_ent.chains])
-    self.assertEquals([c.atom_count for c in pdb_seqres_ent.chains],
+    self.assertEqual([c.atom_count for c in pdb_seqres_ent.chains],
                       [c.atom_count for c in pdb_ent.chains])
-    self.assertEquals([c.GetBondCount() for c in pdb_seqres_ent.chains],
+    self.assertEqual([c.GetBondCount() for c in pdb_seqres_ent.chains],
                       [c.GetBondCount() for c in pdb_ent.chains])
 
   def test_mmcifinfo_biounit_pdbize_transformation(self):
@@ -188,10 +188,10 @@ class TestMMCifInfo(unittest.TestCase):
                                      seqres=True,
                                      info=True)
     pdb_ent, t = info.GetBioUnits()[0].PDBize(ent, transformation=True)
-    self.assertAlmostEquals(pdb_ent.GetCenterOfAtoms()[0], -915.759, 1)
-    self.assertAlmostEquals(pdb_ent.GetCenterOfAtoms()[1], -952.345, 1)
-    self.assertAlmostEquals(pdb_ent.GetCenterOfAtoms()[2], 3221.75, 1)
-    self.assertEquals(geom.Equal(t,
+    self.assertAlmostEqual(pdb_ent.GetCenterOfAtoms()[0], -915.759, 1)
+    self.assertAlmostEqual(pdb_ent.GetCenterOfAtoms()[1], -952.345, 1)
+    self.assertAlmostEqual(pdb_ent.GetCenterOfAtoms()[2], 3221.75, 1)
+    self.assertEqual(geom.Equal(t,
                                  geom.Mat4(1,0,0,-920.462,
                                            0,1,0,-966.654,
                                            0,0,1,1703,
@@ -209,26 +209,26 @@ class TestMMCifInfo(unittest.TestCase):
     d.SetMassMethod('Good Guess')
     d.SetModelDetails('Created with SwissModel')
     d.SetModelTypeDetails('Average')
-    self.assertEquals(d.GetEntryID(), '1BAR')
-    self.assertEquals(d.GetTitle(), 'A Title')
-    self.assertEquals(d.GetCASPFlag(), 'N')
-    self.assertEquals(d.GetDescriptor(), 'FooBar')
-    self.assertEquals(d.GetMass(), 1.0)
-    self.assertEquals(d.GetMassMethod(), 'Good Guess')
-    self.assertEquals(d.GetModelDetails(), 'Created with SwissModel')  
-    self.assertEquals(d.GetModelTypeDetails(), 'Average') 
+    self.assertEqual(d.GetEntryID(), '1BAR')
+    self.assertEqual(d.GetTitle(), 'A Title')
+    self.assertEqual(d.GetCASPFlag(), 'N')
+    self.assertEqual(d.GetDescriptor(), 'FooBar')
+    self.assertEqual(d.GetMass(), 1.0)
+    self.assertEqual(d.GetMassMethod(), 'Good Guess')
+    self.assertEqual(d.GetModelDetails(), 'Created with SwissModel')  
+    self.assertEqual(d.GetModelTypeDetails(), 'Average') 
 
     i = io.MMCifInfo()
     i.SetStructDetails(d)
-    self.assertEquals(i.GetStructDetails().GetEntryID(), '1BAR')
-    self.assertEquals(i.GetStructDetails().GetTitle(), 'A Title')
-    self.assertEquals(i.GetStructDetails().GetCASPFlag(), 'N')
-    self.assertEquals(i.GetStructDetails().GetDescriptor(), 'FooBar')
-    self.assertEquals(i.GetStructDetails().GetMass(), 1.0)
-    self.assertEquals(i.GetStructDetails().GetMassMethod(), 'Good Guess')
-    self.assertEquals(i.GetStructDetails().GetModelDetails(),
+    self.assertEqual(i.GetStructDetails().GetEntryID(), '1BAR')
+    self.assertEqual(i.GetStructDetails().GetTitle(), 'A Title')
+    self.assertEqual(i.GetStructDetails().GetCASPFlag(), 'N')
+    self.assertEqual(i.GetStructDetails().GetDescriptor(), 'FooBar')
+    self.assertEqual(i.GetStructDetails().GetMass(), 1.0)
+    self.assertEqual(i.GetStructDetails().GetMassMethod(), 'Good Guess')
+    self.assertEqual(i.GetStructDetails().GetModelDetails(),
                       'Created with SwissModel')
-    self.assertEquals(i.GetStructDetails().GetModelTypeDetails(), 'Average')
+    self.assertEqual(i.GetStructDetails().GetModelTypeDetails(), 'Average')
 
   def test_mmcifinfo_obsolete(self):
     obs = io.MMCifInfoObsolete()
@@ -236,18 +236,18 @@ class TestMMCifInfo(unittest.TestCase):
     obs.SetID('SPRSDE')
     obs.SetPDBID('1FOO')
     obs.SetReplacedPDBID('2BAR')
-    self.assertEquals(obs.GetDate(), '2011-08-31')
-    self.assertEquals(obs.GetID(), 'Supersede')
-    self.assertEquals(obs.GetPDBID(), '1FOO')
-    self.assertEquals(obs.GetReplacedPDBID(), '2BAR')
+    self.assertEqual(obs.GetDate(), '2011-08-31')
+    self.assertEqual(obs.GetID(), 'Supersede')
+    self.assertEqual(obs.GetPDBID(), '1FOO')
+    self.assertEqual(obs.GetReplacedPDBID(), '2BAR')
 
     i = io.MMCifInfo()
     obs.id = 'OBSLTE'
     i.SetObsoleteInfo(obs)
-    self.assertEquals(i.GetObsoleteInfo().GetDate(), '2011-08-31')
-    self.assertEquals(i.GetObsoleteInfo().GetID(), 'Obsolete')
-    self.assertEquals(i.GetObsoleteInfo().GetPDBID(), '1FOO')
-    self.assertEquals(i.GetObsoleteInfo().GetReplacedPDBID(), '2BAR')
+    self.assertEqual(i.GetObsoleteInfo().GetDate(), '2011-08-31')
+    self.assertEqual(i.GetObsoleteInfo().GetID(), 'Obsolete')
+    self.assertEqual(i.GetObsoleteInfo().GetPDBID(), '1FOO')
+    self.assertEqual(i.GetObsoleteInfo().GetReplacedPDBID(), '2BAR')
 
 if __name__== '__main__':
   from ost import testutils
diff --git a/modules/io/tests/test_io_pdb.py b/modules/io/tests/test_io_pdb.py
index f3f3acbdf8593fe6c238b0ef2da1636840291d0b..3ba3539dc337055e4d1237e1004d3474e38f281e 100644
--- a/modules/io/tests/test_io_pdb.py
+++ b/modules/io/tests/test_io_pdb.py
@@ -7,9 +7,9 @@ class TestPDB(unittest.TestCase):
 
   def test_compnd_parser(self):
     e=io.LoadPDB('testfiles/pdb/compnd.pdb', restrict_chains="A")
-    self.assertEquals(e.GetChainCount(), 1)
+    self.assertEqual(e.GetChainCount(), 1)
     ch = e.FindChain("A");
-    self.assertEquals(ch.GetIntProp("mol_id"), 1)
+    self.assertEqual(ch.GetIntProp("mol_id"), 1)
 
   def test_properly_assigns_profile_properties(self):
     io.profiles['TEST'] = io.IOProfile()
diff --git a/modules/mol/alg/pymod/hbond.py b/modules/mol/alg/pymod/hbond.py
index 33545bd2f5430aac82e65cbb9023bece29608634..0807fe833801de466a66460b2ff35b649993bd47 100644
--- a/modules/mol/alg/pymod/hbond.py
+++ b/modules/mol/alg/pymod/hbond.py
@@ -139,10 +139,10 @@ class HBondDonor:
     _donor=res.FindAtom(donor_name).handle
     _hydrogen=res.FindAtom(hydrogen_name).handle
     if not _donor.IsValid():
-      if verbose:print 'Could not find '+donor_name+' in residue '+str(res)
+      if verbose:print('Could not find '+donor_name+' in residue '+str(res))
       return
     if not _hydrogen.IsValid():
-      if verbose:print 'Could not find '+hydrogen_name+' in residue '+str(res)
+      if verbose:print('Could not find '+hydrogen_name+' in residue '+str(res))
       return
     return cls(_donor,_hydrogen)
   
@@ -164,11 +164,11 @@ class HBondAcceptor:
     _acceptor=res.FindAtom(acceptor_name).handle
     _antecedent_list=_ost.mol.AtomHandleList([res.FindAtom(name).handle for name in antecedent_name_list])
     if not _acceptor.IsValid():
-      if verbose:print 'Could not find '+acceptor_name+' in residue '+str(res)
+      if verbose:print('Could not find '+acceptor_name+' in residue '+str(res))
       return
     for i,a in enumerate(_antecedent_list):
       if not a.IsValid():
-        if verbose:print 'Could not find '+antecedent_name_list[i]+' in residue '+str(res)
+        if verbose:print('Could not find '+antecedent_name_list[i]+' in residue '+str(res))
         return
     return cls(_acceptor,_antecedent_list)
   
@@ -223,13 +223,13 @@ def GetHbondDonorAcceptorList(eh,hbond_donor_acceptor_dict={},verbose=True):
   These names are given in a dictionary, which defaults to CHARMM.
   """
   if not hbond_donor_acceptor_dict:
-    print 'Using default CHARMM atom namings'
+    print('Using default CHARMM atom namings')
     hbond_donor_acceptor_dict=BuildCHARMMHBondDonorAcceptorDict()
   donor_list=[]
   acceptor_list=[]
   for r in eh.residues:
     if not r.name in hbond_donor_acceptor_dict:
-      print 'donors and acceptors for',r,'are not defined in the dictionary and will not be included'
+      print('donors and acceptors for',r,'are not defined in the dictionary and will not be included')
       continue
     res_da_dict=hbond_donor_acceptor_dict[r.name]
     for acceptor in res_da_dict.acceptors:
@@ -274,10 +274,10 @@ def GetHbondListFromTraj(t,eh,cutoff=0.7,stride=1,swap=False,donor_swap_dict={},
   """
   if swap:
     if not donor_swap_dict:
-      print 'use of standard CHARMM HBond donor swap dictionary'
+      print('use of standard CHARMM HBond donor swap dictionary')
       donor_swap_dict=BuildCHARMMHBondDonorEquivalenceDict()
     if not acceptor_swap_dict:
-      print 'use of standard CHARMM HBond acceptor swap dictionary'  
+      print('use of standard CHARMM HBond acceptor swap dictionary')  
       acceptor_swap_dict=BuildCHARMMHBondAcceptorEquivalenceDict()
   [donor_list,acceptor_list]=GetHbondDonorAcceptorList(eh,hbond_donor_acceptor_dict,verbose)
   hb_list=[]
@@ -339,10 +339,10 @@ def GetEquivalentHBonds(ref_hbond_list,eh,swap=False,donor_swap_dict={},acceptor
       hbond_list.append(set([HBond(donor,acceptor)]))
   else:
     if not donor_swap_dict:
-      print 'use of standard CHARMM HBond donor swap dictionary'
+      print('use of standard CHARMM HBond donor swap dictionary')
       donor_swap_dict=BuildCHARMMHBondDonorEquivalenceDict()
     if not acceptor_swap_dict:
-      print 'use of standard CHARMM HBond acceptor swap dictionary'  
+      print('use of standard CHARMM HBond acceptor swap dictionary')  
       acceptor_swap_dict=BuildCHARMMHBondAcceptorEquivalenceDict()
     for hbond in ref_hbond_list:
       res1=eh.FindResidue(hbond.donor.heavy_atom.chain.name,hbond.donor.heavy_atom.residue.number.num)
@@ -374,7 +374,7 @@ def CalculateHBondScore(ref_eh,eh2,ref_eh2=None,hbond_donor_acceptor_dict={},swa
   else:hbond_list1=GetHbondListFromView(ref_eh,hbond_donor_acceptor_dict,verbose)
   nbonds=float(len(hbond_list1))
   if nbonds==0:
-    print 'No HBonds in reference view'
+    print('No HBonds in reference view')
     return None
   hbond_list2=GetEquivalentHBonds(hbond_list1,eh2,swap,donor_swap_dict,acceptor_swap_dict,verbose)
   c=0
@@ -392,19 +392,19 @@ def AnalyzeHBondScore(ref_eh,t,eh2,ref_eh2=None,hbond_donor_acceptor_dict={},swa
   """
   if swap:
     if not donor_swap_dict:
-      print 'use of standard CHARMM HBond donor swap dictionary'
+      print('use of standard CHARMM HBond donor swap dictionary')
       donor_swap_dict=BuildCHARMMHBondDonorEquivalenceDict()
     if not acceptor_swap_dict:
-      print 'use of standard CHARMM HBond acceptor swap dictionary'  
+      print('use of standard CHARMM HBond acceptor swap dictionary')  
       acceptor_swap_dict=BuildCHARMMHBondAcceptorEquivalenceDict()
   if ref_eh2:hbond_list1=GetHbondListBetweenViews(ref_eh,ref_eh2,hbond_donor_acceptor_dict,verbose)
   elif type(ref_eh)==list:hbond_list1=ref_eh
   else:hbond_list1=GetHbondListFromView(ref_eh,hbond_donor_acceptor_dict,verbose)
   nbonds=float(len(hbond_list1))
   if nbonds==0:
-    print 'No HBonds in reference view'
+    print('No HBonds in reference view')
     return None
-  print 'number of hbonds in ref_eh:',nbonds
+  print('number of hbonds in ref_eh:',nbonds)
   hbond_list2=GetEquivalentHBonds(hbond_list1,eh2,swap,donor_swap_dict,acceptor_swap_dict,verbose)
   if last==-1:last=t.GetFrameCount()
   score=FloatList()
@@ -423,10 +423,10 @@ def AnalyzeHBondScore(ref_eh,t,eh2,ref_eh2=None,hbond_donor_acceptor_dict={},swa
 def GetHBondListIntersection(ref_hbond_list,ref_eh,hbond_list,swap=False,donor_swap_dict={},acceptor_swap_dict={}):
   if swap:
     if not donor_swap_dict:
-      print 'use of standard CHARMM HBond donor swap dictionary'
+      print('use of standard CHARMM HBond donor swap dictionary')
       donor_swap_dict=BuildCHARMMHBondDonorEquivalenceDict()
     if not acceptor_swap_dict:
-      print 'use of standard CHARMM HBond acceptor swap dictionary'  
+      print('use of standard CHARMM HBond acceptor swap dictionary')  
       acceptor_swap_dict=BuildCHARMMHBondAcceptorEquivalenceDict()
   hbond_list=GetEquivalentHBonds(hbond_list,ref_eh,swap,donor_swap_dict,acceptor_swap_dict)
   ref_hbond_list=GetEquivalentHBonds(ref_hbond_list,ref_eh,swap,donor_swap_dict,acceptor_swap_dict)
diff --git a/modules/mol/alg/pymod/helix_kinks.py b/modules/mol/alg/pymod/helix_kinks.py
index 3bfb86047ef167f3824b6f1e4e78c75e7f374f52..7d9de7da00f3092f7b03de229367163306a2417b 100644
--- a/modules/mol/alg/pymod/helix_kinks.py
+++ b/modules/mol/alg/pymod/helix_kinks.py
@@ -9,17 +9,17 @@ import ost
 
 def __FindProline(sele,proline):
   if not sele.IsValid():
-    print 'selection is not valid'
+    print('selection is not valid')
     raise RuntimeError
   if proline==False:
     proline=sele.Select('rname=PRO')
   if not proline.GetResidueCount()==1:
-    print proline.GetResidueCount(),'prolines in the selection. One proline is needed'
+    print(proline.GetResidueCount(),'prolines in the selection. One proline is needed')
     raise RuntimeError
   proline=proline.residues[0]
   proline_ca=proline.FindAtom('CA')
   if not proline_ca.IsValid():
-    print 'proline has no CA atom'
+    print('proline has no CA atom')
     raise RuntimeError
   return (proline,proline_ca)
   proline.GetNumber().num
@@ -27,14 +27,14 @@ def __FindProline(sele,proline):
 def __SelectPreAndPostProline(sele,proline_num):
   pre_proline=sele.Select('rnum<'+str(proline_num))
   post_proline=sele.Select('rnum>'+str(proline_num))
-  print 'pre-proline residues'
+  print('pre-proline residues')
   for res in pre_proline.residues:
-    print res
-  print 'post-proline residues'
+    print(res)
+  print('post-proline residues')
   for res in post_proline.residues:
-    print res
+    print(res)
   if pre_proline.GetResidueCount()<4 or post_proline.GetResidueCount()<4:
-    print 'pre and post proline helices should be at least 4 residues long, 7 for better stability'
+    print('pre and post proline helices should be at least 4 residues long, 7 for better stability')
     raise RuntimeError
   return (pre_proline,post_proline)
 
@@ -42,7 +42,7 @@ def __FindCa3AndCa4(sele,proline_ca,proline_num):
   ca_3=sele.FindAtom(proline_ca.GetHandle().GetChain().GetName(),proline_num-3,'CA')
   ca_4=sele.FindAtom(proline_ca.GetHandle().GetChain().GetName(),proline_num-4,'CA')
   if not (ca_3.IsValid() and ca_4.IsValid()):
-    print 'CA not found in (i-4) or (i-3) residue'
+    print('CA not found in (i-4) or (i-3) residue')
     raise RuntimeError
   return (ca_3,ca_4)
 
diff --git a/modules/mol/alg/pymod/qsscoring.py b/modules/mol/alg/pymod/qsscoring.py
index 3ed23ae9ea8bbb6bcedc65cdaa132e1c24de8834..ba9c2af9dcde47fb03c8af3f7eccd1ddffe22f8a 100644
--- a/modules/mol/alg/pymod/qsscoring.py
+++ b/modules/mol/alg/pymod/qsscoring.py
@@ -1098,7 +1098,7 @@ class OligoLDDTScorer(object):
       nominator = sum([s * w for s, w in zip(scores, weights)])
       if self.penalize_extra_chains:
         ref_scorers = self._GetRefScorers()
-        denominator = sum(s.total_contacts for s in ref_scorers.values())
+        denominator = sum(s.total_contacts for s in list(ref_scorers.values()))
         denominator += self._GetModelPenalty()
       else:
         denominator = sum(weights)
@@ -1259,7 +1259,7 @@ class OligoLDDTScorer(object):
       for ch_name_mdl in sorted(unmapped_mdl_chains):
         # get penalty for chain
         cur_penalty = None
-        for cm_ref, cm_mdl in self.chem_mapping.iteritems():
+        for cm_ref, cm_mdl in self.chem_mapping.items():
           if ch_name_mdl in cm_mdl:
             # penalize by an average of the chem. mapped ref. chains
             cur_penalty = 0
@@ -1633,8 +1633,8 @@ def _GetChemGroupsMapping(qs_ent_1, qs_ent_2):
   chain_pairs = []
   ca_chains_1 = qs_ent_1.ca_chains
   ca_chains_2 = qs_ent_2.ca_chains
-  for ch_1 in repr_chains_1.keys():
-    for ch_2 in repr_chains_2.keys():
+  for ch_1 in list(repr_chains_1.keys()):
+    for ch_2 in list(repr_chains_2.keys()):
       aln = _AlignAtomSeqs(ca_chains_1[ch_1], ca_chains_2[ch_2])
       if aln:
         chains_seqid = seq.alg.SequenceIdentity(aln)
@@ -1646,24 +1646,24 @@ def _GetChemGroupsMapping(qs_ent_1, qs_ent_2):
   chem_mapping = {}
   for _, c1, c2 in chain_pairs:
     skip = False
-    for a,b in chem_mapping.iteritems():
+    for a,b in chem_mapping.items():
       if repr_chains_1[c1] == a or repr_chains_2[c2] == b:
         skip = True
         break
     if not skip:
       chem_mapping[repr_chains_1[c1]] = repr_chains_2[c2]
   if swapped:
-    chem_mapping = {y: x for x, y in chem_mapping.iteritems()}
+    chem_mapping = {y: x for x, y in chem_mapping.items()}
     qs_ent_1, qs_ent_2 = qs_ent_2, qs_ent_1
 
   # notify chains without partner
-  mapped_1 = set([i for s in chem_mapping.keys() for i in s])
+  mapped_1 = set([i for s in list(chem_mapping.keys()) for i in s])
   chains_1 = set([c.name for c in qs_ent_1.ent.chains])
   if chains_1 - mapped_1:
     LogWarning('Unmapped Chains in %s: %s'
                % (qs_ent_1.GetName(), ','.join(list(chains_1 - mapped_1))))
 
-  mapped_2 = set([i for s in chem_mapping.values() for i in s])
+  mapped_2 = set([i for s in list(chem_mapping.values()) for i in s])
   chains_2 = set([c.name for c in qs_ent_2.ent.chains])
   if chains_2 - mapped_2:
     LogWarning('Unmapped Chains in %s: %s'
@@ -1714,7 +1714,7 @@ def _GetAlignedResidues(qs_ent_1, qs_ent_2, chem_mapping, max_ca_per_chain,
   ca_chains_1 = qs_ent_1.ca_chains
   ca_chains_2 = qs_ent_2.ca_chains
   # go through all mapped chemical groups
-  for group_1, group_2 in chem_mapping.iteritems():
+  for group_1, group_2 in chem_mapping.items():
     # MSA with ClustalW
     seq_list = seq.CreateSequenceList()
     # keep sequence-name (must be unique) to view mapping
@@ -1770,9 +1770,9 @@ def _FindSymmetry(qs_ent_1, qs_ent_2, ent_to_cm_1, ent_to_cm_2, chem_mapping):
 
   # get possible symmetry groups
   symm_subg_1 = _GetSymmetrySubgroups(qs_ent_1, ent_to_cm_1,
-                                      chem_mapping.keys())
+                                      list(chem_mapping.keys()))
   symm_subg_2 = _GetSymmetrySubgroups(qs_ent_2, ent_to_cm_2,
-                                      chem_mapping.values())
+                                      list(chem_mapping.values()))
 
   # check combination of groups
   LogInfo('Selecting Symmetry Groups...')
@@ -1878,7 +1878,7 @@ def _GetChainMapping(ent_1, ent_2, symm_1, symm_2, chem_mapping,
   ref_symm_2 = symm_2[0]
   asu_chem_mapping = _LimitChemMapping(chem_mapping, ref_symm_1, ref_symm_2)
   # for each chemically identical group
-  for g1, g2 in asu_chem_mapping.iteritems():
+  for g1, g2 in asu_chem_mapping.items():
     # try to superpose all possible pairs
     for c1, c2 in itertools.product(g1, g2):
       # get superposition transformation
@@ -1906,7 +1906,7 @@ def _GetChainMapping(ent_1, ent_2, symm_1, symm_2, chem_mapping,
     index_asu_c1 = ref_symm_1.index(intra_asu_c1)
     index_asu_c2 = ref_symm_2.index(intra_asu_c2)
     index_mapp = {ref_symm_1.index(s1): ref_symm_2.index(s2) \
-                  for s1, s2 in intra_asu_mapp.iteritems()}
+                  for s1, s2 in intra_asu_mapp.items()}
     LogInfo('Intra symmetry-group mapping: %s' % str(intra_asu_mapp))
 
     # get INTER symmetry group chain mapping
@@ -1928,8 +1928,8 @@ def _GetChainMapping(ent_1, ent_2, symm_1, symm_2, chem_mapping,
         check += 1
         # need to extract full chain mapping (use indexing)
         mapping = {}
-        for a, b in asu_mapp.iteritems():
-          for id_a, id_b in index_mapp.iteritems():
+        for a, b in asu_mapp.items():
+          for id_a, id_b in index_mapp.items():
             mapping[a[id_a]] = b[id_b]
         chains_rmsd = cached_rmsd.GetMappedRMSD(mapping, res.transformation)
         full_mappings.append((chains_rmsd, mapping))
@@ -2035,16 +2035,16 @@ def _GetDihedralSubgroups(ent, chem_groups, angles, angle_thr):
 
   # get those which are non redundant and covering all chains
   symm_groups = []
-  for clst in same_angles.values():
-    group = clst.keys()
+  for clst in list(same_angles.values()):
+    group = list(clst.keys())
     if _ValidChainGroup(group, ent):
       if len(chem_groups) > 1:
         # if hetero, we want to group toghether different chains only
-        symm_groups.append(zip(*group))
+        symm_groups.append(list(zip(*group)))
       else:
         # if homo, we also want pairs
         symm_groups.append(group)
-        symm_groups.append(zip(*group))
+        symm_groups.append(list(zip(*group)))
   return symm_groups
 
 def _GetCyclicSubgroups(ent, chem_groups, axis, axis_thr):
@@ -2066,8 +2066,8 @@ def _GetCyclicSubgroups(ent, chem_groups, axis, axis_thr):
 
   # use to get grouping
   symm_groups = []
-  for clst in same_axis.values():
-    all_chain = [item for sublist in clst.keys() for item in sublist]
+  for clst in list(same_axis.values()):
+    all_chain = [item for sublist in list(clst.keys()) for item in sublist]
     if len(set(all_chain)) == ent.chain_count:
       # if we have an hetero we can exploit cyclic symmetry for grouping
       if len(chem_groups) > 1:
@@ -2104,14 +2104,14 @@ def _ClusterData(data, thr, metric):
   """
   # special case length 1
   if len(data) == 1:
-    return {0: {data.keys()[0]: data.values()[0]} }
+    return {0: {list(data.keys())[0]: list(data.values())[0]} }
   # do the clustering
-  cluster_indices = fclusterdata(np.asarray(data.values()), thr,
+  cluster_indices = fclusterdata(np.asarray(list(data.values())), thr,
                                  method='complete', criterion='distance',
                                  metric=metric)
   # fclusterdata output is cluster ids -> put into dict of clusters
   res = {}
-  for cluster_idx, data_key in zip(cluster_indices, data.keys()):
+  for cluster_idx, data_key in zip(cluster_indices, list(data.keys())):
     if not cluster_idx in res:
       res[cluster_idx] = {}
     res[cluster_idx][data_key] = data[data_key]
@@ -2215,7 +2215,7 @@ def _LimitChemMapping(chem_mapping, limit_1, limit_2):
   limit_1_set = set(limit_1)
   limit_2_set = set(limit_2)
   asu_chem_mapping = dict()
-  for key, value in chem_mapping.iteritems():
+  for key, value in chem_mapping.items():
     new_key = tuple(set(key) & limit_1_set)
     if new_key:
       asu_chem_mapping[new_key] = tuple(set(value) & limit_2_set)
@@ -2247,7 +2247,7 @@ def _CountSuperpositionsAndMappings(symm_1, symm_2, chem_mapping):
   ref_symm_1 = symm_1[0]
   ref_symm_2 = symm_2[0]
   asu_chem_mapping = _LimitChemMapping(chem_mapping, ref_symm_1, ref_symm_2)
-  for g1, g2 in asu_chem_mapping.iteritems():
+  for g1, g2 in asu_chem_mapping.items():
     chain_superpositions = len(g1) * len(g2)
     c['intra']['superpositions'] += chain_superpositions
     map_per_sup = _PermutationOrCombinations(g1[0], g2[0], asu_chem_mapping)
@@ -2266,12 +2266,12 @@ def _PermutationOrCombinations(sup1, sup2, chem_mapping):
   """Should match len(_ListPossibleMappings(sup1, sup2, chem_mapping))."""
   # remove superposed elements, put smallest complex as key
   chem_map = {}
-  for a,b in chem_mapping.iteritems():
+  for a,b in chem_mapping.items():
     new_a = tuple([x for x in a if x != sup1])
     new_b = tuple([x for x in b if x != sup2])
     chem_map[new_a] = new_b
   mapp_nr = 1.0
-  for a, b in chem_map.iteritems():
+  for a, b in chem_map.items():
     if len(a) == len(b):
       mapp_nr *= factorial(len(a))
     elif len(a) < len(b):
@@ -2306,14 +2306,14 @@ def _ListPossibleMappings(sup1, sup2, chem_mapping):
            less elements) has more elements for any given mapped group.
   """
   # find smallest complex
-  chain1 = [i for s in chem_mapping.keys() for i in s]
-  chain2 = [i for s in chem_mapping.values() for i in s]
+  chain1 = [i for s in list(chem_mapping.keys()) for i in s]
+  chain2 = [i for s in list(chem_mapping.values()) for i in s]
   swap = False
   if len(chain1) > len(chain2):
     swap = True
   # remove superposed elements, put smallest complex as key
   chem_map = {}
-  for a, b in chem_mapping.iteritems():
+  for a, b in chem_mapping.items():
     new_a = tuple([x for x in a if x != sup1])
     new_b = tuple([x for x in b if x != sup2])
     if swap:
@@ -2324,7 +2324,7 @@ def _ListPossibleMappings(sup1, sup2, chem_mapping):
   # equivalent chains
   chem_perm = []
   chem_ref = []
-  for a, b in chem_map.iteritems():
+  for a, b in chem_map.items():
     if len(a) == len(b):
       chem_perm.append(list(itertools.permutations(b)))
       chem_ref.append(a)
@@ -2341,7 +2341,7 @@ def _ListPossibleMappings(sup1, sup2, chem_mapping):
     flat_perm = [i for s in perm for i in s]
     d = {c1: c2 for c1, c2 in zip(flat_ref, flat_perm)}
     if swap:
-      d = {v: k for k, v in d.items()}
+      d = {v: k for k, v in list(d.items())}
     d.update({sup1: sup2})
     mappings.append(d)
   return mappings
@@ -2387,7 +2387,7 @@ def _CheckClosedSymmetry(ent_1, ent_2, symm_1, symm_2, chem_mapping,
   asu_chem_mapping = _LimitChemMapping(chem_mapping, ref_symm_1, ref_symm_2)
   # for each chemically identical group
   rmsd_mappings = []
-  for g1, g2 in asu_chem_mapping.iteritems():
+  for g1, g2 in asu_chem_mapping.items():
     # try to superpose all possible pairs
     # -> note that some chain-chain combinations may work better than others
     #    to superpose the full oligomer (e.g. if some chains are open/closed)
@@ -2436,10 +2436,10 @@ def _GetSuperpositionMapping(ent_1, ent_2, chem_mapping, transformation,
     swap = True
     ent_1, ent_2 = ent_2, ent_1
     transformation = geom.Invert(transformation)
-    chem_pairs = zip(chem_mapping.values(), chem_mapping.keys())
+    chem_pairs = list(zip(list(chem_mapping.values()), list(chem_mapping.keys())))
   else:
     swap = False
-    chem_pairs = chem_mapping.iteritems()
+    chem_pairs = iter(chem_mapping.items())
   # sanity check
   if ent_1.chain_count == 0:
     return None
@@ -2461,7 +2461,7 @@ def _GetSuperpositionMapping(ent_1, ent_2, chem_mapping, transformation,
         ch_atoms[a_2.chain.name].add(a_2.hash_code)
     # get one with most atoms in overlap
     max_num, max_name = max((len(atoms), name)
-                            for name, atoms in ch_atoms.iteritems())
+                            for name, atoms in ch_atoms.items())
     # early abort if overlap fraction not good enough
     ch_2 = ent_2.FindChain(max_name)
     if max_num == 0 or max_num / float(ch_2.handle.atom_count) < sup_fract:
@@ -2480,7 +2480,7 @@ def _GetSuperpositionMapping(ent_1, ent_2, chem_mapping, transformation,
     mapped_chains.add(max_name)
   # unswap if needed and return
   if swap:
-    mapping = {v: k for k, v in mapping.iteritems()}
+    mapping = {v: k for k, v in mapping.items()}
   return mapping
 
 def _GetMappedRMSD(ent_1, ent_2, chain_mapping, transformation):
@@ -2496,7 +2496,7 @@ def _GetMappedRMSD(ent_1, ent_2, chain_mapping, transformation):
   # collect RMSDs and atom counts chain by chain and combine afterwards
   rmsds = []
   atoms = []
-  for c1, c2 in chain_mapping.iteritems():
+  for c1, c2 in chain_mapping.items():
     # get views and atom counts
     chain_1 = ent_1.Select('cname="%s"' % c1)
     chain_2 = ent_2.Select('cname="%s"' % c2)
@@ -2566,7 +2566,7 @@ class _CachedRMSD:
     # collect RMSDs and atom counts chain by chain and combine afterwards
     rmsds = []
     atoms = []
-    for c1, c2 in chain_mapping.iteritems():
+    for c1, c2 in chain_mapping.items():
       # cached?
       if (c1, c2) in self._pair_rmsd:
         atom_count, rmsd = self._pair_rmsd[(c1, c2)]
diff --git a/modules/mol/alg/pymod/structure_analysis.py b/modules/mol/alg/pymod/structure_analysis.py
index 75ae223e1203e6116bccc487341d00e7d52d80a0..2103b14a71aa4ebd0be528df66d3fe21d6782313 100644
--- a/modules/mol/alg/pymod/structure_analysis.py
+++ b/modules/mol/alg/pymod/structure_analysis.py
@@ -30,11 +30,11 @@ def GetDistanceBetwCenterOfMass(sele1,sele2):
   :return: :class:`float`
   """
   if not sele1.IsValid() and sele2.IsValid():
-    print 'invalid view'
+    print('invalid view')
     return
   eh=sele1.GetHandle()
   if not eh==sele2.GetHandle():
-    print 'The two views must be from the same entity'
+    print('The two views must be from the same entity')
     return
   f=GetFrameFromEntity(eh)
   return f.GetDistanceBetwCenterOfMass(sele1,sele2)
@@ -52,11 +52,11 @@ def GetMinDistanceBetweenViews(sele1,sele2):
   :return: :class:`float`
   """
   if not sele1.IsValid() and sele2.IsValid():
-    print 'invalid view'
+    print('invalid view')
     return
   eh=sele1.GetHandle()
   if not eh==sele2.GetHandle():
-    print 'The two views must be from the same entity'
+    print('The two views must be from the same entity')
     return
   f=GetFrameFromEntity(eh)
   return f.GetMinDistance(sele1,sele2)
@@ -74,11 +74,11 @@ def GetMinDistBetwCenterOfMassAndView(sele1,sele2):
   :return: distance (\ :class:`float`\ )
   """
   if not sele1.IsValid() and sele2.IsValid():
-    print 'invalid view'
+    print('invalid view')
     return
   eh=sele1.GetHandle()
   if not eh==sele2.GetHandle():
-    print 'The two views must be from the same entity'
+    print('The two views must be from the same entity')
     return
   f=GetFrameFromEntity(eh)
   return f.GetMinDistBetwCenterOfMassAndView(sele1,sele2)
@@ -95,7 +95,7 @@ def GetAlphaHelixContent(sele1):
   :return: :class:`float`
   """
   if not sele1.IsValid():
-    print 'invalid view'
+    print('invalid view')
     return
   eh=sele1.GetHandle()
   f=GetFrameFromEntity(eh)
@@ -112,7 +112,7 @@ def CalculateBestFitLine(sele1):
   :return: :class:`~ost.geom.Line3`
   """
   if not sele1.IsValid():
-    print 'invalid view'
+    print('invalid view')
     return
   eh=sele1.GetHandle()
   f=GetFrameFromEntity(eh)
@@ -128,7 +128,7 @@ def CalculateBestFitPlane(sele1):
   :return: :class:`~ost.geom.Plane`
   """
   if not sele1.IsValid():
-    print 'invalid view'
+    print('invalid view')
     return
   eh=sele1.GetHandle()
   f=GetFrameFromEntity(eh)
@@ -146,7 +146,7 @@ def CalculateHelixAxis(sele1):
   :return: :class:`~ost.geom.Line3`
   """
   if not sele1.IsValid():
-    print 'invalid view'
+    print('invalid view')
     return
   eh=sele1.GetHandle()
   f=GetFrameFromEntity(eh)
@@ -172,10 +172,10 @@ def CalculateDistanceDifferenceMatrix(sele1,sele2):
     LogError("Function needs numpy, but I could not import it.")
     raise
   if not sele1.IsValid() and sele2.IsValid():
-    print 'invalid view'
+    print('invalid view')
     return
   if not sele1.GetAtomCount()==sele2.GetAtomCount():
-    print 'The two views must have the same number of atoms'
+    print('The two views must have the same number of atoms')
     return
   n_atoms=sele1.GetAtomCount()
   M=npy.zeros([n_atoms,n_atoms])
diff --git a/modules/mol/alg/pymod/superpose.py b/modules/mol/alg/pymod/superpose.py
index 546d71c4611c01a5dc64bb07f27dfbe062bb3392..515a4970363980ec3a9c2bad7ed8756831262af6 100644
--- a/modules/mol/alg/pymod/superpose.py
+++ b/modules/mol/alg/pymod/superpose.py
@@ -110,13 +110,13 @@ def MatchResidueByNum(ent_a, ent_b, atoms='all'):
       ## check residues & copy to views
       try:
         while True:
-          r_a=residues_a.next()
-          r_b=residues_b.next()
+          r_a=next(residues_a)
+          r_b=next(residues_b)
           while r_a.number!=r_b.number:
             while r_a.number<r_b.number:
-              r_a=residues_a.next()
+              r_a=next(residues_a)
             while r_b.number<r_a.number:
-              r_b=residues_b.next()
+              r_b=next(residues_b)
           assert r_a.number==r_b.number
           result_a,result_b=_fetch_atoms(r_a, r_b, result_a, result_b, atmset)
       except StopIteration:
@@ -125,7 +125,7 @@ def MatchResidueByNum(ent_a, ent_b, atoms='all'):
       ## iterate one list of residues, search in other list
       try:
         while True:
-          r_a=residues_a.next()
+          r_a=next(residues_a)
           r_b=chain_b.FindResidue(r_a.number)
           if r_b.IsValid():
             result_a,result_b=_fetch_atoms(r_a, r_b, result_a, result_b, atmset)
diff --git a/modules/mol/alg/pymod/trajectory_analysis.py b/modules/mol/alg/pymod/trajectory_analysis.py
index 2d71b5a4d533c8a14a066176311c2da99e5b84d5..9ff4b341ef55ebde0d3a8c966df2292793a3fa95 100644
--- a/modules/mol/alg/pymod/trajectory_analysis.py
+++ b/modules/mol/alg/pymod/trajectory_analysis.py
@@ -199,7 +199,7 @@ def DistRMSDFromTraj(t,sele,ref_sele,radius=7.0,average=False,seq_sep=4,first=0,
   :return: a numpy vecor dist_rmsd(N\ :subscript:`frames`).  
   """
   if not sele.GetAtomCount()==ref_sele.GetAtomCount():
-    print 'Not same number of atoms in the two views'
+    print('Not same number of atoms in the two views')
     return
   try:
     import numpy as npy
diff --git a/modules/mol/alg/pymod/views.py b/modules/mol/alg/pymod/views.py
index f4a20307724c6b814183ad242637a9852f714ebb..8889538246b310b0af20e8bcf49fe826addb22b8 100644
--- a/modules/mol/alg/pymod/views.py
+++ b/modules/mol/alg/pymod/views.py
@@ -38,12 +38,12 @@ def PairResiduesByNum(view_a, view_b,
   result_b=_EmptyView(view_b)
   try:
     while True:
-      r1=residues_a.next()
-      r2=residues_b.next()
+      r1=next(residues_a)
+      r2=next(residues_b)
       while r1.number<r2.number:
-        r1=residues_a.next()
+        r1=next(residues_a)
       while r2.number<r1.number:
-        r2=residues_b.next()
+        r2=next(residues_b)
       assert r1.number==r2.number
       result_a.AddResidue(r1, view_add_flags)
       result_b.AddResidue(r2, view_add_flags)
diff --git a/modules/mol/alg/tests/test_accessibility.py b/modules/mol/alg/tests/test_accessibility.py
index c3d857fd52f43dc5c30c58ebab47b763fa28da45..8a951529f41b71db43d73b6098997ccc4f699e27 100644
--- a/modules/mol/alg/tests/test_accessibility.py
+++ b/modules/mol/alg/tests/test_accessibility.py
@@ -106,7 +106,7 @@ class TestAccessibility(unittest.TestCase):
       acc_naccess = AccessibilitiesRaw(ent_three, use_naccess=True)
       self.assertTrue(Compare(acc_classic, acc_naccess))
     except:
-      print "Could not find NACCESS, could not compare Accessiblity function..."
+      print("Could not find NACCESS, could not compare Accessiblity function...")
 
 
   def testAccDSSP(self):
@@ -117,7 +117,7 @@ class TestAccessibility(unittest.TestCase):
       dssp_path = settings.Locate(['dsspcmbi', 'dssp', 'mkdssp'],
                                   env_name='DSSP_EXECUTABLE')
     except:
-      print "Could not find DSSP, could not compare Accessibility function..."
+      print("Could not find DSSP, could not compare Accessibility function...")
       return
 
     # we assume oligo mode to be working as it is tested in 
diff --git a/modules/mol/alg/tests/test_convenient_superpose.py b/modules/mol/alg/tests/test_convenient_superpose.py
index 30f06242931c9197c9334fc326a278e7de3b505a..8e01b4937d8fb6c45a0193ba103790e84e107a47 100644
--- a/modules/mol/alg/tests/test_convenient_superpose.py
+++ b/modules/mol/alg/tests/test_convenient_superpose.py
@@ -9,9 +9,9 @@ class TestConvenientSuperpose(unittest.TestCase):
     pass
   
   def assertEqualAtomOrder(self, view1, view2):
-    self.assertEquals(len(view1.atoms),len(view2.atoms))
+    self.assertEqual(len(view1.atoms),len(view2.atoms))
     for a1, a2 in zip(view1.atoms, view2.atoms):
-      self.assertEquals(a1.element, a2.element)
+      self.assertEqual(a1.element, a2.element)
       self.assertTrue(geom.Equal(a1.pos, a2.pos))
 
   def testAssertion(self):
diff --git a/modules/mol/alg/tests/test_nonstandard.py b/modules/mol/alg/tests/test_nonstandard.py
index c81c92479a4150c76aa89ff92409a4dc7656e25f..cbe8257a70576b5eb225ca3ba29293f95e362075 100644
--- a/modules/mol/alg/tests/test_nonstandard.py
+++ b/modules/mol/alg/tests/test_nonstandard.py
@@ -121,4 +121,4 @@ if __name__ == "__main__":
   if testutils.SetDefaultCompoundLib():
     testutils.RunTests()
   else:
-    print 'No compound library available. Ignoring test_nonstandard.py tests.'
+    print('No compound library available. Ignoring test_nonstandard.py tests.')
diff --git a/modules/mol/alg/tests/test_pdbize.py b/modules/mol/alg/tests/test_pdbize.py
index abf2bc892f2618aa85f37b1ffd79c93d106b4d5d..0048f9b3683c99be13f13380f1c94e4c04d7431a 100644
--- a/modules/mol/alg/tests/test_pdbize.py
+++ b/modules/mol/alg/tests/test_pdbize.py
@@ -18,13 +18,13 @@ class TestPDBize(unittest.TestCase):
     seqs = seq.CreateSequenceList()
     pdbizer.Add(m.Select(''), transformations, seqs)
     pdbized = pdbizer.Finish()
-    self.assertEquals([c.name for c in pdbized.chains], ["-"])
+    self.assertEqual([c.name for c in pdbized.chains], ["-"])
     residues = pdbized.residues
     for i in range(26):
-      self.assertEquals(residues[i].number.num, 1)
-      self.assertEquals(residues[i].number.ins_code, chr(ord('A')+i))
-    self.assertEquals(residues[26].number.num, 2)
-    self.assertEquals(residues[26].number.ins_code, 'A')
+      self.assertEqual(residues[i].number.num, 1)
+      self.assertEqual(residues[i].number.ins_code, chr(ord('A')+i))
+    self.assertEqual(residues[26].number.num, 2)
+    self.assertEqual(residues[26].number.ins_code, 'A')
   def test_starts_from_last_water_rnum(self):
     m = mol.CreateEntity()
     e = m.EditXCS(mol.BUFFERED_EDIT)
@@ -38,9 +38,9 @@ class TestPDBize(unittest.TestCase):
     pdbizer.Add(m.Select(''), transformations,seqs)
     pdbizer.Add(m.Select(''), transformations,seqs)
     pdbized = pdbizer.Finish()
-    self.assertEquals([c.name for c in pdbized.chains], ["-"])
+    self.assertEqual([c.name for c in pdbized.chains], ["-"])
     residues = pdbized.residues
-    self.assertEquals([r.number for r in residues],
+    self.assertEqual([r.number for r in residues],
                       [mol.ResNum(1, 'A'), mol.ResNum(1, 'B')])
 
 
diff --git a/modules/mol/alg/tests/test_qsscoring.py b/modules/mol/alg/tests/test_qsscoring.py
index c241eea49dea794a7f620014561a482019b3507a..770c95f6ed3b4c75458ca822a2f6b52d4f7ec84d 100644
--- a/modules/mol/alg/tests/test_qsscoring.py
+++ b/modules/mol/alg/tests/test_qsscoring.py
@@ -5,8 +5,8 @@ from ost import io, mol, settings
 try:
   from ost.mol.alg.qsscoring import *
 except ImportError:
-  print "Failed to import qsscoring. Happens when numpy or scipy missing. " \
-        "Ignoring test_qsscoring.py tests."
+  print("Failed to import qsscoring. Happens when numpy or scipy missing. " \
+        "Ignoring test_qsscoring.py tests.")
   sys.exit(0)
 
 from ost.mol.alg import lDDTSettings
@@ -605,7 +605,7 @@ class TestQSscore(unittest.TestCase):
     cn2 = set([c for cg in qs_ent_2.chem_groups for c in cg])
     cm_names_1 = list()
     cm_names_2 = list()
-    for cg1, cg2 in qs_scorer.chem_mapping.iteritems():
+    for cg1, cg2 in qs_scorer.chem_mapping.items():
       ch_ref = qs_scorer.ent_to_cm_1.FindChain(cg1[0])
       self.assertEqual(ch_ref.residue_count, ch_ref.atom_count)
       self.assertGreaterEqual(ch_ref.residue_count, 5)
@@ -646,12 +646,12 @@ class TestQSscore(unittest.TestCase):
                      cm_names_2)
     # check chain_mapping
     # (all chains of ent with less chains mapped, each only once, chem_map)
-    chm_names_1 = qs_scorer.chain_mapping.keys()
-    chm_names_2 = qs_scorer.chain_mapping.values()
+    chm_names_1 = list(qs_scorer.chain_mapping.keys())
+    chm_names_2 = list(qs_scorer.chain_mapping.values())
     self.assertEqual(len(chm_names_1), min(len(cm_names_1), len(cm_names_2)))
     self.assertEqual(len(set(chm_names_1)), len(chm_names_1))
     self.assertEqual(len(set(chm_names_2)), len(chm_names_2))
-    for cg1, cg2 in qs_scorer.chem_mapping.iteritems():
+    for cg1, cg2 in qs_scorer.chem_mapping.items():
       for ch_name in cg1:
         if ch_name in qs_scorer.chain_mapping:
           self.assertTrue(qs_scorer.chain_mapping[ch_name] in cg2)
@@ -678,10 +678,10 @@ if __name__ == "__main__":
   try:
     settings.Locate(('clustalw', 'clustalw2'))
   except:
-    print "Could not find ClustalW. Ignoring test_qsscoring.py tests."
+    print("Could not find ClustalW. Ignoring test_qsscoring.py tests.")
     sys.exit(0)
   from ost import testutils
   if testutils.SetDefaultCompoundLib():
     testutils.RunTests()
   else:
-    print 'No compound library available. Ignoring test_qsscoring.py tests.'
+    print('No compound library available. Ignoring test_qsscoring.py tests.')
diff --git a/modules/mol/alg/tests/test_sec_struct.py b/modules/mol/alg/tests/test_sec_struct.py
index 207f8600c8f14589577fd1fabddd6ee0ebf72598..c6b6a928e20e33db23622681f70bd6c64821f54c 100644
--- a/modules/mol/alg/tests/test_sec_struct.py
+++ b/modules/mol/alg/tests/test_sec_struct.py
@@ -14,7 +14,7 @@ class TestSecStruct(unittest.TestCase):
       dssp_path = settings.Locate(['dsspcmbi', 'dssp', 'mkdssp'],
                                   env_name='DSSP_EXECUTABLE')
     except:
-      print "Could not find DSSP, could not compare sec struct assignment..."
+      print("Could not find DSSP, could not compare sec struct assignment...")
       return
 
     dssp_ent = io.LoadPDB(os.path.join("testfiles", "1a0s.pdb"))
diff --git a/modules/mol/base/tests/test_numpy.py b/modules/mol/base/tests/test_numpy.py
index 27061a234cc96d72ceaeec28ffad6ab18ca44d0a..fb69b9afe1a5fd8bf0a3e9d02b3a4006c7fd6a12 100644
--- a/modules/mol/base/tests/test_numpy.py
+++ b/modules/mol/base/tests/test_numpy.py
@@ -11,7 +11,7 @@ if ost.WITH_NUMPY:
   has_numpy=True
   try:
     import numpy
-  except ImportError, e:
+  except ImportError as e:
     has_numpy=False
 else:
   has_numpy=False
diff --git a/modules/mol/mm/pymod/__init__.py b/modules/mol/mm/pymod/__init__.py
index a4a7c650e1fe7c9bbb0643bdce4a8d4be6e8e6bd..f10fd7e6b69b7e5130e165de2063830c4f0c4f61 100644
--- a/modules/mol/mm/pymod/__init__.py
+++ b/modules/mol/mm/pymod/__init__.py
@@ -19,7 +19,7 @@
 
 import os.path
 from _ost_mol_mm import *
-import antechamber
+from . import antechamber
 import ost
 
 def LoadAMBERForcefield():
diff --git a/modules/mol/mm/pymod/antechamber.py b/modules/mol/mm/pymod/antechamber.py
index 6fc48ce3f38effb6e1a92683e1ec0e31d318c326..979dd4d992ee29ccdc7a18ba83f5647f38485e26 100644
--- a/modules/mol/mm/pymod/antechamber.py
+++ b/modules/mol/mm/pymod/antechamber.py
@@ -226,7 +226,7 @@ def _ParseAmberForceField(filename):
       if not keyword in keywords: continue
       # loop until empty line found
       ff_dict[keyword] = []
-      line = in_file.next()
+      line = next(in_file)
       while len(line.strip()) > 0:
         # check for warnings
         if 'ATTN' in line:
@@ -286,7 +286,7 @@ def _ParseAmberForceField(filename):
           epsilon = float(s[1])
           ff_dict[keyword].append([atype, Rvdw, epsilon])
         # next...
-        line = in_file.next()
+        line = next(in_file)
   return ff_dict
 ###############################################################################
 
diff --git a/modules/seq/alg/pymod/__init__.py b/modules/seq/alg/pymod/__init__.py
index 44fa50acf5584119845f501ed28ac3b580468cec..8d9b9c806e1c15adcadf4574ee4a60c49b17defc 100644
--- a/modules/seq/alg/pymod/__init__.py
+++ b/modules/seq/alg/pymod/__init__.py
@@ -188,7 +188,7 @@ def PredictContacts(ali):
   import math
   from ost import seq
   if not type(ali)==type(seq.AlignmentHandle()):
-    print "Parameter should be an AlignmentHandle"
+    print("Parameter should be an AlignmentHandle")
     return
   mi=CalculateMutualInformation(ali)
   CoEvoSc=CalculateContactSubstitutionScore(ali)
diff --git a/modules/seq/alg/tests/test_aligntoseqres.py b/modules/seq/alg/tests/test_aligntoseqres.py
index 2aa1bb4fed038ef28671f0feee2c46e878e1a207..5664ff8378a30667bf543d2463b2a6c29a51b385 100644
--- a/modules/seq/alg/tests/test_aligntoseqres.py
+++ b/modules/seq/alg/tests/test_aligntoseqres.py
@@ -71,4 +71,4 @@ if __name__ == "__main__":
   if testutils.SetDefaultCompoundLib():
     testutils.RunTests()
   else:
-    print 'No compound library available. Ignoring test_aligntoseqres.py tests.'
+    print('No compound library available. Ignoring test_aligntoseqres.py tests.')
diff --git a/modules/seq/alg/tests/test_renumber.py b/modules/seq/alg/tests/test_renumber.py
index 0309f5a1b9166f1a2e4a2febd947832c06487218..f587d588fec5b0104a9c7760894cf6a749d7758a 100644
--- a/modules/seq/alg/tests/test_renumber.py
+++ b/modules/seq/alg/tests/test_renumber.py
@@ -167,6 +167,6 @@ if __name__ == "__main__":
   # test renumbering
   # test if clustalw package is available on system, otherwise ignore tests
   if not clustalw_path:
-    print "Could not find clustalw executable: ignoring some renumber unit tests"
+    print("Could not find clustalw executable: ignoring some renumber unit tests")
   from ost import testutils
   testutils.RunTests()
diff --git a/scripts/bump-version.py b/scripts/bump-version.py
index 053b08bfd7863accecff7e3302d7b43a5f78e651..ddf414cd60a1114fc1d00462c05f55e7fa621523 100755
--- a/scripts/bump-version.py
+++ b/scripts/bump-version.py
@@ -2,9 +2,9 @@
 import sys
 
 if len(sys.argv) < 2:
-  print "USAGE: python scripts/bump-version.py OST_VERSION"
-  print "-> OST_VERSION is MAJOR.MINOR.PATCH (e.g. 1.9.1)"
-  print "-> assumption is that a git tag OST_VERSION will exist"
+  print("USAGE: python scripts/bump-version.py OST_VERSION")
+  print("-> OST_VERSION is MAJOR.MINOR.PATCH (e.g. 1.9.1)")
+  print("-> assumption is that a git tag OST_VERSION will exist")
   sys.exit(1)
 
 # split up version number
diff --git a/scripts/code_coverage.py b/scripts/code_coverage.py
index 6df65830ef09f91c88df5ff64d117ef58c4a2c05..1cb87bc3ab51ae3bf7133b94ceac760b35623ea5 100644
--- a/scripts/code_coverage.py
+++ b/scripts/code_coverage.py
@@ -42,7 +42,7 @@ def ParseOptions():
 
 def CheckLCOV():
   if not Which('lcov'):
-    print 'please install lcov to run this script'
+    print('please install lcov to run this script')
     sys.exit(-1)
 
 CheckLCOV()
@@ -50,9 +50,9 @@ CheckLCOV()
 opts=ParseOptions()
 
 def Print(message):
-  print '~~~~~~'
-  print '|  *  |', message
-  print '~~~~~~'  
+  print('~~~~~~')
+  print('|  *  |', message)
+  print('~~~~~~')  
   
 def RemoveFiles(directory, extension):
   glob_pattern='*.%s' % extension
@@ -113,7 +113,7 @@ def RunTests(modules):
     if os.path.exists(test_dir):
       test_binary=os.path.join(os.path.join(test_dir, 'tests'))
       if os.path.exists(test_binary) and os.access(test_binary, os.X_OK):
-        print 'running tests for module', module        
+        print('running tests for module', module)        
         Cleanup(modules)
         cmd='cd "%s"; ./tests' % test_dir
         os.system(cmd)