diff --git a/.travis.yml b/.travis.yml
index a604df641f8a7689cf4b4ef7e4004fb03c674e23..0d555b54df85c8fe42c1a3de0772d34002860cdc 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -132,7 +132,7 @@ install:
     if [[ "$WITH_PYTHON" == "true" ]]; then
       curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py
       python$PY get-pip.py
-      pip --version
+      pip$PY --version
     fi
   - if [[ ( "$WITH_PYTHON" == "true" ) && ( "$TRAVIS_OS_NAME" == "osx" ) ]]; then pip$PY install numpy; fi
   - |
diff --git a/ci/codecov/build.Dockerfile b/ci/codecov/build.Dockerfile
index 1cb095e3e1c544bfc44c04ac0f8e6ef8582397da..23f9e621b0d2966ce67efdab1652152ffa348728 100644
--- a/ci/codecov/build.Dockerfile
+++ b/ci/codecov/build.Dockerfile
@@ -11,12 +11,12 @@ ENV MPICH_VERSION ${MPICH_VERSION}
 # Install basic tools
 RUN apt-get update -qq && apt-get install -qq -y --no-install-recommends \
     build-essential lcov \
-    python \
+    python3 \
     git tar wget curl && \
     rm -rf /var/lib/apt/lists/*
 
 # Install cmake
-RUN wget -qO- "https://cmake.org/files/v3.17/cmake-3.17.0-Linux-x86_64.tar.gz" | tar --strip-components=1 -xz -C /usr/local
+RUN wget -qO- "https://github.com/Kitware/CMake/releases/download/v3.12.4/cmake-3.12.4-Linux-x86_64.tar.gz" | tar --strip-components=1 -xz -C /usr/local
 
 # Install MPICH ABI compatible with Cray's lib on Piz Daint
 RUN wget -q https://www.mpich.org/static/downloads/${MPICH_VERSION}/mpich-${MPICH_VERSION}.tar.gz && \
@@ -27,7 +27,7 @@ RUN wget -q https://www.mpich.org/static/downloads/${MPICH_VERSION}/mpich-${MPIC
     rm -rf mpich-${MPICH_VERSION}.tar.gz mpich-${MPICH_VERSION}
 
 # Install bundle tooling for creating small Docker images
-RUN wget -q https://github.com/haampie/libtree/releases/download/v1.1.3/libtree_x86_64.tar.gz && \
+RUN wget -q https://github.com/haampie/libtree/releases/download/v1.2.0/libtree_x86_64.tar.gz && \
     tar -xzf libtree_x86_64.tar.gz && \
     rm libtree_x86_64.tar.gz && \
     ln -s /root/libtree/libtree /usr/local/bin/libtree
\ No newline at end of file
diff --git a/ci/release/build.Dockerfile b/ci/release/build.Dockerfile
index 67ba8be8aa21edc61f52bdbd9659f2675714be1e..4c17a820e44a2e5fc5d9d66d0fd0f6fe2e8b8888 100644
--- a/ci/release/build.Dockerfile
+++ b/ci/release/build.Dockerfile
@@ -11,12 +11,12 @@ ENV MPICH_VERSION ${MPICH_VERSION}
 # Install basic tools
 RUN apt-get update -qq && apt-get install -qq -y --no-install-recommends \
     build-essential \
-    python \
+    python3 \
     git tar wget curl && \
     rm -rf /var/lib/apt/lists/*
 
 # Install cmake
-RUN wget -qO- "https://cmake.org/files/v3.17/cmake-3.17.0-Linux-x86_64.tar.gz" | tar --strip-components=1 -xz -C /usr/local
+RUN wget -qO- "https://github.com/Kitware/CMake/releases/download/v3.12.4/cmake-3.12.4-Linux-x86_64.tar.gz" | tar --strip-components=1 -xz -C /usr/local
 
 # Install MPICH ABI compatible with Cray's lib on Piz Daint
 RUN wget -q https://www.mpich.org/static/downloads/${MPICH_VERSION}/mpich-${MPICH_VERSION}.tar.gz && \
@@ -27,7 +27,7 @@ RUN wget -q https://www.mpich.org/static/downloads/${MPICH_VERSION}/mpich-${MPIC
     rm -rf mpich-${MPICH_VERSION}.tar.gz mpich-${MPICH_VERSION}
 
 # Install bundle tooling for creating small Docker images
-RUN wget -q https://github.com/haampie/libtree/releases/download/v1.1.3/libtree_x86_64.tar.gz && \
+RUN wget -q https://github.com/haampie/libtree/releases/download/v1.2.0/libtree_x86_64.tar.gz && \
     tar -xzf libtree_x86_64.tar.gz && \
     rm libtree_x86_64.tar.gz && \
-    ln -s /root/libtree/libtree /usr/local/bin/libtree
\ No newline at end of file
+    ln -s /root/libtree/libtree /usr/local/bin/libtree
diff --git a/example/lfp/plot-lfp.py b/example/lfp/plot-lfp.py
index 14ddde3eacd33da10de67a8879498c2d21eb6001..bbbeff05be3109880c5859f073407fe5cfe01ba8 100755
--- a/example/lfp/plot-lfp.py
+++ b/example/lfp/plot-lfp.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 
 import argparse
 import matplotlib.pyplot as plt
diff --git a/scripts/compare_spike_trains.py b/scripts/compare_spike_trains.py
index 597ce1b9100b94763026bd60eaddc910a2abb2c9..c2bbb4088d18b7f13689a359de3bf4efb18a1501 100644
--- a/scripts/compare_spike_trains.py
+++ b/scripts/compare_spike_trains.py
@@ -1,10 +1,11 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 import os
 import sys
 import collections
+from itertools import zip_longest
 
 def usage(default_delta):
-    print ("""
+    print("""
     compare two input spike time files on equality with a delta of max_delta
     order of the spikes is not important. GID should always be equal
     Display the first 50 differences encountered.
@@ -36,9 +37,9 @@ def parse_file(path):
             gid = int(split_items[0].strip())
             time = float(split_items[1].strip())
         except:
-            print "Could not parse a line in the file!!!! \n"
-            print " line: " , line_idx, ": ", stripped_line
-            print path
+            print("Could not parse a line in the file!!!! \n")
+            print(" line: " , line_idx, ": ", stripped_line)
+            print(path)
             
             exit(1) #failure
 
@@ -59,20 +60,20 @@ def compare(path1, data1, path2, data2, delta):
     """
     combined_data = collections.defaultdict(lambda : [[],[]])
     
-    for gid, spike_data in data1.items():
+    for gid, spike_data in list(data1.items()):
         combined_data[gid][0].extend(spike_data)
 
 
-    for gid, spike_data in data2.items():
+    for gid, spike_data in list(data2.items()):
         combined_data[gid][1].extend(spike_data)
 
     different_spikes = []
-    for gid, (data_1, data_2)in combined_data.items():
+    for gid, (data_1, data_2)in list(combined_data.items()):
         gid_list1 = data_1
         gid_list2 = data_2
 
         if len(gid_list1) != len(gid_list2):
-            for idx, (time1, time2) in enumerate(map(None, gid_list1, gid_list2)):
+            for idx, (time1, time2) in enumerate(zip_longest(gid_list1, gid_list2)):
                 # We have to loop all spikes, check here if we have missing spikes 
                 # and treat those different
                 if time1 == None or time2 == None:
@@ -93,18 +94,18 @@ def compare(path1, data1, path2, data2, delta):
                 different_spikes.append((gid, time1, time2))
 
     if len(different_spikes) != 0:
-        print "Found difference in spike times, displaying first 50 \n"
-        print "key == (line_nr, spike_time, content line parsed)\n"
-        print "difference #, gid :  target output !=  simulation output"
+        print("Found difference in spike times, displaying first 50 \n")
+        print("key == (line_nr, spike_time, content line parsed)\n")
+        print("difference #, gid :  target output !=  simulation output")
 
         for idx, (gid, time1, time2) in enumerate(different_spikes):
             if idx == 50:
                 break
 
             dif_str = "difference #{0}, {3}: {1} !=  {2}".format(idx, time1, time2, gid)
-            print dif_str
+            print(dif_str)
 
-        print "\n\n"
+        print("\n\n")
 
 
         # Also output to file (could be done in previous loop, but seperation
diff --git a/scripts/print_backtrace b/scripts/print_backtrace
index bca8a5bc6fb4dee1bf8e336ab23f5c2807105f84..ecba7600ef2cb5ce245dbdb39692d7a3d7c964d0 100755
--- a/scripts/print_backtrace
+++ b/scripts/print_backtrace
@@ -1,4 +1,4 @@
-#!/usr/bin/env python2
+#!/usr/bin/env python3
 #coding: utf-8
 
 import argparse
@@ -54,7 +54,7 @@ def parse_backtrace(source):
             tokens = line.split()
             trace.append({'location':tokens[0], 'function':tokens[1]})
     else:
-        print "error: unable to open back trace file ", source
+        print("error: unable to open back trace file ", source)
 
     return trace
 
@@ -75,7 +75,7 @@ args = parse_clargs()
 # check that a valid executable was provided
 executable = args.executable
 if not os.path.isfile(executable):
-    print "error:", executable, "is not a valid executable"
+    print("error:", executable, "is not a valid executable")
 else:
     for frame in parse_backtrace(args.input):
         location = get_function_name(frame['location'], executable)
@@ -84,6 +84,6 @@ else:
         fname = c.yellow + location['filename'] + c.end
         line = c.cyan + location['line'] + c.end
         if args.brief:
-            print  fname + ':' + line
+            print(fname + ':' + line)
         else:
-            print  fname + ':' + line, '\n ', name
+            print(fname + ':' + line, '\n ', name)
diff --git a/scripts/tsplot b/scripts/tsplot
index 3207dcb9590977227a0e5007759ac0a7aae74968..c64e7b18160bf78f90b25099e91fde6a042a2a0d 100755
--- a/scripts/tsplot
+++ b/scripts/tsplot
@@ -1,8 +1,6 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 #coding: utf-8
 
-from __future__ import print_function
-
 import argparse
 import json
 import numpy as np
@@ -84,7 +82,7 @@ def parse_clargs():
     return P.parse_args(argsbis)
 
 def isstring(s):
-    return isinstance(s,str) or isinstance(s,unicode)
+    return isinstance(s,str)
 
 def take(n, s):
     return islice((i for i in s), 0, n)
@@ -225,7 +223,7 @@ def read_json_timeseries(j, axis='time', select=[]):
            return ""
 
     i = 1
-    for key in jdata.keys():
+    for key in list(jdata.keys()):
         if key==axis: continue
 
         meta = j.copy()
@@ -278,7 +276,7 @@ class PlotData:
             return labels
 
         n = len(labels)
-        keyset = reduce(lambda k, s: k.union(s.meta.keys()), self.series, set())
+        keyset = reduce(lambda k, s: k.union(list(s.meta.keys())), self.series, set())
         keyi = iter(keyset)
         try:
             while len(set(labels)) != n:
@@ -293,7 +291,7 @@ class PlotData:
                 for i in range(n):
                     prefix = '' if k=='name' else k+'='
                     if vs[i] is not None:
-                        labels[i] += u', '+k+u'='+unicode(formatter(vs[i]))
+                        labels[i] += ', '+k+'='+str(formatter(vs[i]))
 
         except StopIteration:
             pass
@@ -310,7 +308,7 @@ def gather_ts_plots(tss, groupby):
     for ts in tss:
         key = tuple([ts.meta.get(g) for g in groupby])
         if key is () or None in key or key not in group_lookup:
-            pretty_key=', '.join([unicode(k)+u'='+unicode(v) for k,v in zip(groupby, key) if v is not None])
+            pretty_key=', '.join([str(k)+'='+str(v) for k,v in zip(groupby, key) if v is not None])
             pd = PlotData(pretty_key)
             pd.series = [ts]
             plot_groups.append(pd)
diff --git a/validation/ref/neuron/ball_and_3stick.py b/validation/ref/neuron/ball_and_3stick.py
index 473fbda9e33aa113fd27fde638fdf2da466a6e30..a14ae07392bab81140eecd46096bdb242c1733db 100644
--- a/validation/ref/neuron/ball_and_3stick.py
+++ b/validation/ref/neuron/ball_and_3stick.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 #coding: utf-8
 
 import json
diff --git a/validation/ref/neuron/ball_and_squiggle.py b/validation/ref/neuron/ball_and_squiggle.py
index 9fcc5b49c5affdb5600437a500f50d8c0f20fb6d..b69bc2ece2726070bd551a4c8537bd130a833367 100644
--- a/validation/ref/neuron/ball_and_squiggle.py
+++ b/validation/ref/neuron/ball_and_squiggle.py
@@ -1,15 +1,10 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 #coding: utf-8
 
 import json
 import math
 import nrn_validation as V
 
-try:
-    from builtins import range
-except ImportError:
-    from __builtin__ import range
-
 V.override_defaults_from_args()
 
 # dendrite geometry: 100 µm long, varying diameter.
diff --git a/validation/ref/neuron/ball_and_stick.py b/validation/ref/neuron/ball_and_stick.py
index 52378e64e9f3163aa30332acfc50b3cee66e6faf..93cc097eb07c05b13e00108e68534bcccb5762ac 100644
--- a/validation/ref/neuron/ball_and_stick.py
+++ b/validation/ref/neuron/ball_and_stick.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 #coding: utf-8
 
 import json
diff --git a/validation/ref/neuron/ball_and_taper.py b/validation/ref/neuron/ball_and_taper.py
index 13961c0e62b57bc1bc9c8ba901e8f1b7e11c60d6..7c351021220dd8609a533cc1bdd863daeba2aba7 100644
--- a/validation/ref/neuron/ball_and_taper.py
+++ b/validation/ref/neuron/ball_and_taper.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 #coding: utf-8
 
 import json
diff --git a/validation/ref/neuron/nrn_validation.py b/validation/ref/neuron/nrn_validation.py
index a924937474c3c7988a87569746da21fa35d01dea..5370a077b072ec9ee766abddc468f0a8c10bda90 100644
--- a/validation/ref/neuron/nrn_validation.py
+++ b/validation/ref/neuron/nrn_validation.py
@@ -1,7 +1,6 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 #coding: utf-8
 
-import json
 import sys
 import os
 import re
@@ -9,11 +8,6 @@ import numpy as np
 import neuron
 from neuron import h
 
-try:
-    from builtins import range
-except ImportError:
-    from __builtin__ import range
-
 # This is super annoying: without neuron.gui, need
 # to explicit load 'standard' hoc routines like 'run',
 # but this is chatty on stdout, which means we get
@@ -55,7 +49,7 @@ default_model_parameters = {
 
 def override_defaults_from_args(args=sys.argv):
     global default_model_parameters
-    keys = default_model_parameters.keys()
+    keys = list(default_model_parameters.keys())
     r = re.compile('('+'|'.join(keys)+')=(.*)')
     for m in [r.match(a) for a in args]:
         if m:
@@ -77,7 +71,7 @@ class VModel:
         self.netcons = []
 
     def set_ncomp(self, n):
-        for s in self.sections.values():
+        for s in list(self.sections.values()):
             s.nseg = int(n)
 
     def add_iclamp(self, t0, dt, i, to=None, pos=1):
diff --git a/validation/ref/neuron/simple_exp2_synapse.py b/validation/ref/neuron/simple_exp2_synapse.py
index c2314e7619cde5fcfbef583437237546ee3ff043..bbaaa0688cf603d6ba8d7ec27b520ff6b508cdf2 100644
--- a/validation/ref/neuron/simple_exp2_synapse.py
+++ b/validation/ref/neuron/simple_exp2_synapse.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 #coding: utf-8
 
 import json
diff --git a/validation/ref/neuron/simple_exp_synapse.py b/validation/ref/neuron/simple_exp_synapse.py
index 587317faaa785491caa640ae5069f9202bacb968..e83a7051dc14a2a0df484fa592a91a4271049af5 100644
--- a/validation/ref/neuron/simple_exp_synapse.py
+++ b/validation/ref/neuron/simple_exp_synapse.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 #coding: utf-8
 
 import json
diff --git a/validation/ref/neuron/soma.py b/validation/ref/neuron/soma.py
index 393d898cf20fcac084125e89392182b87dc424f3..372f420732ab294f3cfebacbc1ffae3042a78b5d 100644
--- a/validation/ref/neuron/soma.py
+++ b/validation/ref/neuron/soma.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 #coding: utf-8
 
 import json