diff --git a/configure.ac b/configure.ac
index 49cafb3..c0d49f4 100644
--- a/configure.ac
+++ b/configure.ac
@@ -37,7 +37,7 @@ AC_PATH_PROG([XSLTPROC], [xsltproc])
 GLIB_GSETTINGS
 
 # Get a value to substitute into gnome-shell.in
-AM_PATH_PYTHON([2.5])
+AM_PATH_PYTHON([3])
 AC_SUBST(PYTHON)
 
 # We need at least this, since gst_plugin_register_static() was added
diff --git a/src/gnome-shell-extension-tool.in b/src/gnome-shell-extension-tool.in
index e00d5cd..d10d433 100644
--- a/src/gnome-shell-extension-tool.in
+++ b/src/gnome-shell-extension-tool.in
@@ -14,7 +14,7 @@ except ImportError:
     try:
         import simplejson as json
     except ImportError:
-        print 'The Python simplejson module is required'
+        print('The Python simplejson module is required')
         sys.exit(1)
 
 from gi.repository import Gio
@@ -88,36 +88,36 @@ function disable() {
 }
 
 def create_extension():
-    print
-    print '''Name should be a very short (ideally descriptive) string.
+    print()
+    print('''Name should be a very short (ideally descriptive) string.
 Examples are: "Click To Focus",  "Adblock", "Shell Window Shrinker".
-'''
-    name = raw_input('Name: ').strip()
-    print
-    print '''Description is a single-sentence explanation of what your extension does.
+''')
+    name = input('Name: ').strip()
+    print()
+    print('''Description is a single-sentence explanation of what your extension does.
 Examples are: "Make windows visible on click", "Block advertisement popups"
               "Animate windows shrinking on minimize"
-'''
-    description = raw_input('Description: ').strip()
+''')
+    description = input('Description: ').strip()
     underifier = re.compile('[^A-Za-z]')
     sample_uuid = underifier.sub('_', name)
     # TODO use evolution data server
     hostname = socket.gethostname()
     sample_uuid = sample_uuid + '@' + hostname
 
-    print
-    print '''Uuid is a globally-unique identifier for your extension.
+    print()
+    print('''Uuid is a globally-unique identifier for your extension.
 This should be in the format of an email address (foo.bar@extensions.example.com), but
 need not be an actual email address, though it's a good idea to base the uuid on your
 email address.  For example, if your email address is janedoe@example.com, you might
-use an extension title clicktofocus@janedoe.example.com.'''
-    uuid = raw_input('Uuid [%s]: ' % (sample_uuid, )).strip()
+use an extension title clicktofocus@janedoe.example.com.''')
+    uuid = input('Uuid [%s]: ' % (sample_uuid, )).strip()
     if uuid == '':
         uuid = sample_uuid
 
     extension_path = os.path.join(os.path.expanduser('~/.local'), 'share', 'gnome-shell', 'extensions', uuid)
     if os.path.exists(extension_path):
-        print "Extension path %r already exists" % (extension_path, )
+        print("Extension path %r already exists" % (extension_path, ))
         sys.exit(0)
     os.makedirs(extension_path)
     meta = { 'name': name,
@@ -132,13 +132,13 @@ use an extension title clicktofocus@janedoe.example.com.'''
         f.write(json.write(meta) + '\n')
     f.close()
 
-    for filename, contents in SAMPLE_EXTENSION_FILES.iteritems():
+    for filename, contents in SAMPLE_EXTENSION_FILES.items():
         path = os.path.join(extension_path, filename)
         f = open(path, 'w')
         f.write(contents)
         f.close()
 
-    print "Created extension in %r" % (extension_path, )
+    print("Created extension in %r" % (extension_path, ))
     extensionjs_path = os.path.join(extension_path, 'extension.js')
     subprocess.Popen(['xdg-open', extensionjs_path])
 
@@ -149,19 +149,19 @@ def enable_extension(uuid):
     extensions = settings.get_strv(ENABLED_EXTENSIONS_KEY)
 
     if uuid in extensions:
-        print >> sys.stderr, "%r is already enabled." % (uuid,)
+        print("%r is already enabled." % (uuid,), file=sys.stderr)
         sys.exit(1)
 
     extensions.append(uuid)
     settings.set_strv(ENABLED_EXTENSIONS_KEY, extensions)
-    print >> sys.stderr, "%r is now enabled." % (uuid,)
+    print("%r is now enabled." % (uuid,), file=sys.stderr)
 
 def disable_extension(uuid):
     settings = Gio.Settings(schema='org.gnome.shell')
     extensions = settings.get_strv(ENABLED_EXTENSIONS_KEY)
 
     if uuid not in extensions:
-        print >> sys.stderr, "%r is not enabled or installed." % (uuid,)
+        print("%r is not enabled or installed." % (uuid,), file=sys.stderr)
         sys.exit(1)
 
     # Use a while loop here to remove *all* mentions instances
@@ -170,7 +170,7 @@ def disable_extension(uuid):
         extensions.remove(uuid)
 
     settings.set_strv(ENABLED_EXTENSIONS_KEY, extensions)
-    print >> sys.stderr, "%r is now disabled." % (uuid,)
+    print("%r is now disabled." % (uuid,), file=sys.stderr)
 
 def main():
     parser = optparse.OptionParser()
diff --git a/src/gnome-shell-perf-tool.in b/src/gnome-shell-perf-tool.in
index 55bfdf3..b2a4521 100644
--- a/src/gnome-shell-perf-tool.in
+++ b/src/gnome-shell-perf-tool.in
@@ -14,15 +14,14 @@ import subprocess
 import sys
 import tempfile
 import base64
-from ConfigParser import RawConfigParser
+from configparser import RawConfigParser
 import hashlib
 import hmac
-import httplib
-import urlparse
-import urllib
+from http import client
+from urllib import parse
 
 def show_version(option, opt_str, value, parser):
-    print "GNOME Shell Performance Test @VERSION@"
+    print("GNOME Shell Performance Test @VERSION@")
     sys.exit()
 
 def wait_for_dbus_name(wait_name):
@@ -41,7 +40,7 @@ def wait_for_dbus_name(wait_name):
                                   None)
 
     def on_timeout():
-        print "\nFailed to start %s: timed out" % (wait_name,)
+        print("\nFailed to start %s: timed out" % (wait_name,))
         sys.exit(1)
     GLib.timeout_add_seconds(7, on_timeout)
 
@@ -131,15 +130,15 @@ def upload_performance_report(report_text):
         base_url = config.get('upload', 'url')
         system_name = config.get('upload', 'name')
         secret_key = config.get('upload', 'key')
-    except Exception, e:
-        print "Can't read upload configuration from %s: %s" % (config_file, str(e))
+    except Exception as e:
+        print("Can't read upload configuration from %s: %s" % (config_file, str(e)))
         sys.exit(1)
 
     # Determine host, port and upload URL from provided data, we're
     # a bit extra-careful about normalization since the URL is part
     # of the signature.
 
-    split = urlparse.urlsplit(base_url)
+    split = parse.urlsplit(base_url)
     scheme = split[0].lower()
     netloc = split[1]
     base_path = split[2]
@@ -151,7 +150,7 @@ def upload_performance_report(report_text):
         host, port = m.group(1), None
 
     if scheme != "http":
-        print "'%s' is not a HTTP URL" % base_url
+        print("'%s' is not a HTTP URL" % base_url)
         sys.exit(1)
 
     if port is None:
@@ -166,7 +165,7 @@ def upload_performance_report(report_text):
         normalized_base = "%s://%s:%d%s" % (scheme, host, port, base_path)
 
     upload_url = normalized_base + '/system/%s/upload' % system_name
-    upload_path = urlparse.urlsplit(upload_url)[2] # path portion
+    upload_path = parse.urlsplit(upload_url)[2] # path portion
 
     # Create signature based on upload URL and the report data
 
@@ -174,7 +173,7 @@ def upload_performance_report(report_text):
     h = hmac.new(secret_key, digestmod=hashlib.sha1)
     h.update(signature_data)
     h.update(report_text)
-    signature = urllib.quote(base64.b64encode(h.digest()), "~")
+    signature = parse.quote(base64.b64encode(h.digest()), "~")
 
     headers = {
         'User-Agent': 'gnome-shell-performance-tool/@VERSION@',
@@ -182,15 +181,15 @@ def upload_performance_report(report_text):
         'X-Shell-Signature': 'HMAC-SHA1 ' + signature
     };
 
-    connection = httplib.HTTPConnection(host, port)
+    connection = client.HTTPConnection(host, port)
     connection.request('POST', upload_path, report_text, headers)
     response = connection.getresponse()
 
     if response.status == 200:
-        print "Performance report upload succeeded"
+        print("Performance report upload succeeded")
     else:
-        print "Performance report upload failed with status %d" % response.status
-        print response.read()
+        print("Performance report upload failed with status %d" % response.status)
+        print(response.read())
 
 def gnome_hwtest_log(*args):
     command = ['gnome-hwtest-log', '-t', 'gnome-shell-perf-tool']
@@ -207,7 +206,7 @@ def run_performance_test():
 
     start_perf_helper()
 
-    for i in xrange(0, iters):
+    for i in range(0, iters):
         # We create an empty temporary file that the shell will overwrite
         # with the contents.
         handle, output_file = tempfile.mkstemp(".json", "gnome-shell-perf.")
@@ -306,12 +305,12 @@ def run_performance_test():
         gnome_hwtest_log('--finished')
     else:
         # Write a human readable summary
-        print '------------------------------------------------------------';
+        print('------------------------------------------------------------')
         for metric in sorted(metric_summaries.keys()):
             summary = metric_summaries[metric]
-            print "#", summary['description']
-            print metric, ", ".join((str(x) for x in summary['values']))
-        print '------------------------------------------------------------';
+            print("#", summary['description'])
+            print(metric, ", ".join((str(x) for x in summary['values'])))
+        print('------------------------------------------------------------')
 
     return True
 
diff --git a/tools/check-for-missing.py b/tools/check-for-missing.py
index 7fd8c8c..6168375 100755
--- a/tools/check-for-missing.py
+++ b/tools/check-for-missing.py
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/python3
 #
 # This is a simple script that we use to check for files in git
 # and not in the distribution. It was previously written in shell
@@ -16,10 +16,10 @@ os.chdir(srcdir)
 
 status=0
 for f in subprocess.Popen(["git", "ls-files"], stdout=subprocess.PIPE).stdout:
-    f = f.strip()
+    f = f.decode('utf-8').strip()
     if (not os.path.exists(os.path.join(distdir, f)) and
         not any((fnmatch.fnmatch(f, p) for p in excludes))):
-        print "File missing from distribution:", f
+        print("File missing from distribution:", f)
         status=1
 
 sys.exit(status)
