changeset 1859:f066d6e874db

2to3 with these fixers: all idioms set_literal Ignore-this: cbd28518218c2f0ddce8c4f92d3b8b33
author drewp@bigasterisk.com
date Wed, 22 May 2019 00:08:22 +0000
parents 7772cc48e016
children 5bcb950024af
files bin/ascoltami2 bin/bumppad bin/captureDevice bin/clientdemo bin/collector bin/collector_loadtest.py bin/curvecalc bin/dmxserver bin/effecteval bin/effectsequencer bin/homepageConfig bin/inputdemo bin/inputquneo bin/keyboardcomposer bin/lightsim bin/listsongs bin/mpd_timing_test bin/musictime bin/paintserver bin/picamserve bin/run_local.py bin/staticclient bin/subcomposer bin/tkdnd_minimal_drop.py bin/tracker bin/wavecurve bin/webcontrol light9/Effects.py light9/Fadable.py light9/FlyingFader.py light9/Patch.py light9/Submaster.py light9/TLUtility.py light9/ascoltami/player.py light9/chase.py light9/clientsession.py light9/collector/collector.py light9/collector/collector_client.py light9/collector/device.py light9/collector/output.py light9/curvecalc/client.py light9/curvecalc/curve.py light9/curvecalc/curveview.py light9/curvecalc/output.py light9/curvecalc/subterm.py light9/curvecalc/subtermview.py light9/curvecalc/zoomcontrol.py light9/dmxchanedit.py light9/dmxclient.py light9/editchoice.py light9/effect/edit.py light9/effect/effecteval.py light9/effect/scale.py light9/effect/sequencer.py light9/effect/settings.py light9/effect/simple_outputs.py light9/effecteval/effect.py light9/effecteval/effectloop.py light9/effecteval/test_effect.py light9/io/__init__.py light9/io/udmx.py light9/networking.py light9/paint/solve.py light9/paint/solve_test.py light9/prof.py light9/showconfig.py light9/tkdnd.py light9/uihelpers.py light9/updatefreq.py light9/vidref/main.py light9/vidref/qt_test.py light9/vidref/remotepivideo.py light9/vidref/replay.py light9/vidref/videorecorder.py light9/wavelength.py light9/wavepoints.py
diffstat 76 files changed, 300 insertions(+), 302 deletions(-) [+]
line wrap: on
line diff
--- a/bin/ascoltami2	Tue May 21 23:56:12 2019 +0000
+++ b/bin/ascoltami2	Wed May 22 00:08:22 2019 +0000
@@ -1,7 +1,7 @@
 #!bin/python
 from run_local import log
 from twisted.internet import reactor
-import web, thread, sys, optparse, logging
+import web, _thread, sys, optparse, logging
 from rdflib import URIRef
 sys.path.append(".")
 sys.path.append('/usr/lib/python2.7/dist-packages')  # For gi
--- a/bin/bumppad	Tue May 21 23:56:12 2019 +0000
+++ b/bin/bumppad	Wed May 22 00:08:22 2019 +0000
@@ -1,7 +1,7 @@
 #!bin/python
-from __future__ import division, nested_scopes
+
 import sys, time, math
-import Tkinter as tk
+import tkinter as tk
 
 import run_local
 import light9.dmxclient as dmxclient
@@ -55,7 +55,7 @@
         self.master.after_idle(self.output)
 
     def output(self):
-        dmx = sub_maxes(*[s * l for s, l in self.levs.items()]).get_dmx_list()
+        dmx = sub_maxes(*[s * l for s, l in list(self.levs.items())]).get_dmx_list()
         dmxclient.outputlevels(dmx, clientid="bumppad")
 
 
--- a/bin/captureDevice	Tue May 21 23:56:12 2019 +0000
+++ b/bin/captureDevice	Wed May 22 00:08:22 2019 +0000
@@ -1,5 +1,5 @@
 #!bin/python
-from __future__ import division
+
 from rdflib import URIRef
 from twisted.internet import reactor
 from twisted.internet.defer import inlineCallbacks, Deferred
--- a/bin/clientdemo	Tue May 21 23:56:12 2019 +0000
+++ b/bin/clientdemo	Wed May 22 00:08:22 2019 +0000
@@ -25,7 +25,7 @@
 
     def updateDemoValue():
         v = list(g.objects(L9['demo'], L9['is']))
-        print "demo value is %r" % v
+        print("demo value is %r" % v)
 
     g.addHandler(updateDemoValue)
 
--- a/bin/collector	Tue May 21 23:56:12 2019 +0000
+++ b/bin/collector	Wed May 22 00:08:22 2019 +0000
@@ -7,7 +7,7 @@
 Input can be over http or zmq.
 """
 
-from __future__ import division
+
 from run_local import log
 
 from rdflib import URIRef, Literal
@@ -34,7 +34,7 @@
     body = json.loads(msg)
     settings = []
     for device, attr, value in body['settings']:
-        if isinstance(value, basestring) and value.startswith('http'):
+        if isinstance(value, str) and value.startswith('http'):
             value = URIRef(value)
         else:
             value = Literal(value)
@@ -114,7 +114,7 @@
 
     def makeMsg(self, dev, attrs, outputMap):
         attrRows = []
-        for attr, val in attrs.items():
+        for attr, val in list(attrs.items()):
             output, index = outputMap[(dev, attr)]
             attrRows.append({
                 'attr': attr.rsplit('/')[-1],
--- a/bin/collector_loadtest.py	Tue May 21 23:56:12 2019 +0000
+++ b/bin/collector_loadtest.py	Wed May 22 00:08:22 2019 +0000
@@ -10,7 +10,7 @@
 
 
 def loadTest():
-    print "scheduling loadtest"
+    print("scheduling loadtest")
     n = 2500
     times = [None] * n
     session = "loadtest%s" % time.time()
@@ -41,7 +41,7 @@
         offset += .002
 
     def done():
-        print "loadtest done"
+        print("loadtest done")
         with open('/tmp/times', 'w') as f:
             f.write(''.join('%s\n' % t for t in times))
         reactor.stop()
--- a/bin/curvecalc	Tue May 21 23:56:12 2019 +0000
+++ b/bin/curvecalc	Wed May 22 00:08:22 2019 +0000
@@ -8,9 +8,10 @@
 todo: curveview should preserve more objects, for speed maybe
 
 """
-from __future__ import division
+
 
 import sys
+import imp
 sys.path.append('/usr/lib/python2.7/dist-packages')  # For gtk
 from twisted.internet import gtk3reactor
 gtk3reactor.install()
@@ -22,7 +23,7 @@
 from gi.repository import GObject
 from gi.repository import Gdk
 
-from urlparse import parse_qsl
+from urllib.parse import parse_qsl
 import louie as dispatcher
 from rdflib import URIRef, Literal, RDF, RDFS
 import logging
@@ -171,7 +172,7 @@
 
         def recv(widget, context, x, y, selection, targetType, time):
             subUri = URIRef(selection.data.strip())
-            print "into curves", subUri
+            print("into curves", subUri)
             with self.graph.currentState(tripleFilter=(subUri, RDFS.label,
                                                        None)) as current:
                 subName = current.label(subUri)
@@ -190,7 +191,7 @@
                     pass
             curveView = self.curvesetView.row(subName).curveView
             t = self.lastSeenInputTime  # curveView.current_time() # new curve hasn't heard the time yet. this has gotten too messy- everyone just needs to be able to reach the time source
-            print "time", t
+            print("time", t)
             curveView.add_points([(t - .5, 0), (t, 1)])
 
         w.connect("drag-data-received", recv)
@@ -412,7 +413,7 @@
             ('input time', lambda t: "%.2fs" % t),
             ('output levels', lambda levels: textwrap.fill(
                 "; ".join([
-                    "%s:%.2f" % (n, v) for n, v in levels.items()[:2] if v > 0
+                    "%s:%.2f" % (n, v) for n, v in list(levels.items())[:2] if v > 0
                 ]), 70)),
             ('update period', lambda t: "%.1fms" % (t * 1000)),
             ('update status', lambda x: str(x)),
@@ -445,14 +446,14 @@
 
         if (not hasattr(self, 'curvesetView') or
                 self.curvesetView._mtimes != mtimes):
-            print "reload curveview.py"
+            print("reload curveview.py")
             curvesVBox = wtree.get_object("curves")
             zoomControlBox = wtree.get_object("zoomControlBox")
             [curvesVBox.remove(c) for c in curvesVBox.get_children()]
             [zoomControlBox.remove(c) for c in zoomControlBox.get_children()]
             try:
                 linecache.clearcache()
-                reload(curveview)
+                imp.reload(curveview)
 
                 # old ones are not getting deleted right
                 if hasattr(self, 'curvesetView'):
@@ -469,7 +470,7 @@
                 # ok. You'll just get some wasted redraws.
                 self.curvesetView.goLive()
             except Exception:
-                print "reload failed:"
+                print("reload failed:")
                 traceback.print_exc()
         if self.opts.reload:
             reactor.callLater(1, self.refreshCurveView)
--- a/bin/dmxserver	Tue May 21 23:56:12 2019 +0000
+++ b/bin/dmxserver	Wed May 22 00:08:22 2019 +0000
@@ -24,7 +24,7 @@
   if parport fails, run in dummy mode (and make an option for that too)
 """
 
-from __future__ import division
+
 from twisted.internet import reactor
 from twisted.web import xmlrpc, server
 import sys, time, os
@@ -68,7 +68,7 @@
             self.port,
             txosc. async .DatagramServerProtocol(self.receiver),
             interface='0.0.0.0')
-        print "Listening OSC on udp port %s" % (self.port)
+        print("Listening OSC on udp port %s" % (self.port))
 
     def pixel_handler(self, message, address):
         # this is already 1-based though I don't know why
@@ -97,7 +97,7 @@
         # desired seconds between sendlevels() calls
         self.calldelay = 1 / options.updates_per_sec
 
-        print "starting parport connection"
+        print("starting parport connection")
         self.parportdmx = UsbDMX(dimmers=90, port=options.dmx_device)
         if os.environ.get('DMXDUMMY', 0):
             self.parportdmx.godummy()
@@ -122,12 +122,12 @@
         reactor.callLater(1, self.purgeclients)
 
         now = time.time()
-        cids = self.lastseen.keys()
+        cids = list(self.lastseen.keys())
         for cid in cids:
             lastseen = self.lastseen[cid]
             if lastseen < now - purge_age:
-                print("forgetting client %s (no activity for %s sec)" %
-                      (cid, purge_age))
+                print(("forgetting client %s (no activity for %s sec)" %
+                      (cid, purge_age)))
                 try:
                     del self.clientlevels[cid]
                 except KeyError:
@@ -175,7 +175,7 @@
         self.combinedlevels = []
         for chan in range(0, self.parportdmx.dimmers):
             x = 0
-            for clientlist in self.clientlevels.values():
+            for clientlist in list(self.clientlevels.values()):
                 if len(clientlist) > chan:
                     # clamp client levels to 0..1
                     cl = max(0, min(1, clientlist[chan]))
@@ -184,8 +184,8 @@
 
     def printlevels(self):
         """write all the levels to stdout"""
-        print "Levels:", "".join(
-            ["% 2d " % (x * 100) for x in self.combinedlevels])
+        print("Levels:", "".join(
+            ["% 2d " % (x * 100) for x in self.combinedlevels]))
 
     def printstats(self):
         """print the clock, freq, etc, with a \r at the end"""
@@ -194,7 +194,7 @@
             time.strftime("%H:%M:%S"),
             str(self.updatefreq),
         ))
-        for cid, freq in self.clientfreq.items():
+        for cid, freq in list(self.clientfreq.items()):
             sys.stdout.write("[%s %s] " % (cid, str(freq)))
         sys.stdout.write("\r")
         sys.stdout.flush()
@@ -237,7 +237,7 @@
 
     def trackClientFreq(self, cid):
         if cid not in self.lastseen:
-            print "hello new client %s" % cid
+            print("hello new client %s" % cid)
             self.clientfreq[cid] = Updatefreq()
         self.lastseen[cid] = time.time()
         self.clientfreq[cid].update()
@@ -264,13 +264,13 @@
                   help="dummy mode, same as DMXDUMMY=1 env variable")
 (options, songfiles) = parser.parse_args()
 
-print options
+print(options)
 
 if options.dummy:
     os.environ['DMXDUMMY'] = "1"
 
 port = networking.dmxServer.port
-print "starting xmlrpc server on port %s" % port
+print("starting xmlrpc server on port %s" % port)
 xmlrpcServe = XMLRPCServe(options)
 reactor.listenTCP(port, server.Site(xmlrpcServe))
 
--- a/bin/effecteval	Tue May 21 23:56:12 2019 +0000
+++ b/bin/effecteval	Wed May 22 00:08:22 2019 +0000
@@ -1,6 +1,6 @@
 #!bin/python
 
-from __future__ import division
+
 from run_local import log
 from twisted.internet import reactor
 from twisted.internet.defer import inlineCallbacks, returnValue
@@ -173,7 +173,7 @@
             return
         with self.settings.graph.currentState(tripleFilter=(None, L9['effect'],
                                                             effect)) as g:
-            song = g.subjects(L9['effect'], effect).next()
+            song = next(g.subjects(L9['effect'], effect))
 
         replaceObjects(self.settings.graph, song, effect, L9['code'], codeLines)
 
--- a/bin/effectsequencer	Tue May 21 23:56:12 2019 +0000
+++ b/bin/effectsequencer	Wed May 22 00:08:22 2019 +0000
@@ -2,7 +2,7 @@
 """
 plays back effect notes from the timeline
 """
-from __future__ import division
+
 from run_local import log
 from twisted.internet import reactor
 from light9.greplin_cyclone import StatsForCyclone
--- a/bin/homepageConfig	Tue May 21 23:56:12 2019 +0000
+++ b/bin/homepageConfig	Wed May 22 00:08:22 2019 +0000
@@ -3,8 +3,8 @@
 from rdflib import RDF, URIRef
 from light9 import networking, showconfig
 from light9.namespaces import L9
-from urlparse import urlparse
-from urllib import splitport
+from urllib.parse import urlparse
+from urllib.parse import splitport
 
 from rdfdb.syncedgraph import SyncedGraph
 from twisted.internet import reactor
@@ -15,11 +15,11 @@
 webServer = graph.value(netHome, L9['webServer'])
 if not webServer:
     raise ValueError('no %r :webServer' % netHome)
-print "listen %s;" % splitport(urlparse(webServer).netloc)[1]
+print("listen %s;" % splitport(urlparse(webServer).netloc)[1])
 
 
 def location(path, server):
-    print """
+    print("""
     location /%(path)s/ {
 
       # for websocket
@@ -31,7 +31,7 @@
       proxy_pass %(server)s;
       proxy_buffering off;
       rewrite /[^/]+/(.*) /$1 break;
-    }""" % vars()
+    }""" % vars())
 
 
 for role, server in sorted(graph.predicate_objects(netHome)):
@@ -44,10 +44,10 @@
     location(path, server)
 
 showPath = showconfig.showUri().split('/', 3)[-1]
-print """
+print("""
     location /%(path)s {
       root %(root)s;
     }""" % {
     'path': showPath,
     'root': showconfig.root()[:-len(showPath)]
-}
+})
--- a/bin/inputdemo	Tue May 21 23:56:12 2019 +0000
+++ b/bin/inputdemo	Wed May 22 00:08:22 2019 +0000
@@ -5,7 +5,7 @@
 gtk3reactor.install()
 from twisted.internet import reactor
 from rdflib import URIRef
-import optparse, logging, urllib, time
+import optparse, logging, urllib.request, urllib.parse, urllib.error, time
 from gi.repository import Gtk
 from run_local import log
 from light9 import showconfig, networking
@@ -34,7 +34,7 @@
         self.curve = args[0] if args else URIRef(
             'http://light9.bigasterisk.com/show/dance2014/song1/curve/c-1401259747.675542'
         )
-        print "sending points on curve %s" % self.curve
+        print("sending points on curve %s" % self.curve)
 
         reactor.run()
 
@@ -60,7 +60,7 @@
 
         @d.addCallback
         def done(result):
-            print "posted in %.1f ms" % (1000 * (time.time() - t1))
+            print("posted in %.1f ms" % (1000 * (time.time() - t1)))
 
 
 App()
--- a/bin/inputquneo	Tue May 21 23:56:12 2019 +0000
+++ b/bin/inputquneo	Wed May 22 00:08:22 2019 +0000
@@ -2,9 +2,9 @@
 """
 read Quneo midi events, write to curvecalc and maybe to effects
 """
-from __future__ import division
+
 from run_local import log
-import logging, urllib
+import logging, urllib.request, urllib.parse, urllib.error
 import cyclone.web, cyclone.httpclient
 from rdflib import URIRef
 from twisted.internet import reactor, task
@@ -63,7 +63,7 @@
         for ev in self.inp.read(999):
             (status, d1, d2, _), _ = ev
             if status in [NOTEON, NOTEOFF]:
-                print status, d1, d2
+                print(status, d1, d2)
 
             if status == NOTEON:
                 if not self.noteIsOn.get(d1):
@@ -77,7 +77,7 @@
                                 'Content-Type':
                                 ['application/x-www-form-urlencoded']
                             },
-                            postdata=urllib.urlencode([('drop', e)]),
+                            postdata=urllib.parse.urlencode([('drop', e)]),
                         )
                     except KeyError:
                         pass
@@ -90,7 +90,7 @@
                 for group in [(23, 24, 25), (6, 18)]:
                     if d1 in group:
                         if not self.noteIsOn.get(group):
-                            print "start zero"
+                            print("start zero")
 
                             for d in group:
                                 sendLiveInputPoint(curves[d], 0)
--- a/bin/keyboardcomposer	Tue May 21 23:56:12 2019 +0000
+++ b/bin/keyboardcomposer	Wed May 22 00:08:22 2019 +0000
@@ -1,6 +1,6 @@
 #!bin/python
 
-from __future__ import division, nested_scopes
+
 from run_local import log
 import cgi, time, logging
 from optparse import OptionParser
@@ -9,7 +9,7 @@
 from twisted.internet import reactor, tksupport
 from twisted.web import resource
 from rdflib import URIRef, Literal
-import Tix as tk
+import tkinter.tix as tk
 
 from light9.Fadable import Fadable
 from light9.subclient import SubClient
@@ -26,6 +26,7 @@
 from light9.effect.simple_outputs import SimpleOutputs
 
 from bcf2000 import BCF2000
+import imp
 
 nudge_keys = {'up': list('qwertyui'), 'down': list('asdfghjk')}
 
@@ -250,7 +251,7 @@
         for r in self.rows:
             r.destroy()
         self.rows = []
-        for b in self.subbox.values():
+        for b in list(self.subbox.values()):
             b.cleanup()
         self.subbox.clear()
         self.slider_table.clear()
@@ -271,7 +272,7 @@
         log.info("withgroups %s", withgroups)
 
         self.effectEval = {}
-        reload(light9.effect.effecteval)
+        imp.reload(light9.effect.effecteval)
         simpleOutputs = SimpleOutputs(self.graph)
         for group, order, sortLabel, effect in withgroups:
             if col == 0 or group != last_group:
@@ -338,7 +339,7 @@
         self.keyhints = keyhintrow
 
     def setup_key_nudgers(self, tkobject):
-        for d, keys in nudge_keys.items():
+        for d, keys in list(nudge_keys.items()):
             for key in keys:
                 # lowercase makes full=0
                 keysym = "<KeyPress-%s>" % key
@@ -501,7 +502,7 @@
 
     def get_levels(self):
         return dict([
-            (uri, box.slider_var.get()) for uri, box in self.subbox.items()
+            (uri, box.slider_var.get()) for uri, box in list(self.subbox.items())
         ])
 
     def get_output_settings(self, _graph=None):
@@ -540,7 +541,7 @@
         self.sub_name.delete(0, tk.END)
 
     def alltozero(self):
-        for uri, subbox in self.subbox.items():
+        for uri, subbox in list(self.subbox.items()):
             if subbox.scale.scale_var.get() != 0:
                 subbox.scale.fade(value=0.0, length=0)
 
--- a/bin/lightsim	Tue May 21 23:56:12 2019 +0000
+++ b/bin/lightsim	Wed May 22 00:08:22 2019 +0000
@@ -1,6 +1,6 @@
 #!bin/python
 
-from __future__ import division
+
 import run_local
 import sys, logging
 
--- a/bin/listsongs	Tue May 21 23:56:12 2019 +0000
+++ b/bin/listsongs	Wed May 22 00:08:22 2019 +0000
@@ -21,7 +21,7 @@
 def printSongs(result):
     with graph.currentState() as current:
         for song in current.subjects(RDF.type, L9['Song']):
-            print song
+            print(song)
     reactor.stop()
 
 
--- a/bin/mpd_timing_test	Tue May 21 23:56:12 2019 +0000
+++ b/bin/mpd_timing_test	Wed May 22 00:08:22 2019 +0000
@@ -11,10 +11,10 @@
 
 """
 
-import xmlrpclib, time
+import xmlrpc.client, time
 
-s = xmlrpclib.ServerProxy("http://localhost:8040")
+s = xmlrpc.client.ServerProxy("http://localhost:8040")
 start = time.time()
-while 1:
-    print time.time() - start, s.gettime()
+while True:
+    print(time.time() - start, s.gettime())
     time.sleep(.01)
--- a/bin/musictime	Tue May 21 23:56:12 2019 +0000
+++ b/bin/musictime	Wed May 22 00:08:22 2019 +0000
@@ -2,7 +2,7 @@
 import run_local
 import light9.networking
 
-import Tkinter as tk
+import tkinter as tk
 import time
 import restkit, jsonlib
 
@@ -18,8 +18,8 @@
             try:
                 playtime = jsonlib.read(self.player.get("time").body_string(),
                                         use_float=True)['t']
-            except restkit.RequestError, e:
-                print "Server error %s, waiting" % e
+            except restkit.RequestError as e:
+                print("Server error %s, waiting" % e)
                 time.sleep(2)
         return playtime
 
@@ -42,7 +42,7 @@
 
         def print_time(evt, *args):
             self.timevar.set(self.get_music_time())
-            print self.timevar.get(), evt.keysym
+            print(self.timevar.get(), evt.keysym)
 
         self.timelabel.bind('<KeyPress>', print_time)
         self.timelabel.bind('<1>', print_time)
--- a/bin/paintserver	Tue May 21 23:56:12 2019 +0000
+++ b/bin/paintserver	Wed May 22 00:08:22 2019 +0000
@@ -1,6 +1,6 @@
 #!bin/python
 
-from __future__ import division
+
 from run_local import log
 import json
 from twisted.internet import reactor
@@ -15,6 +15,7 @@
 import light9.paint.solve
 from lib.cycloneerr import PrettyErrorHandler
 from light9.namespaces import RDF, L9, DEV
+import imp
 
 
 class Solve(PrettyErrorHandler, cyclone.web.RequestHandler):
@@ -42,7 +43,7 @@
             }))
 
     def reloadSolver(self):
-        reload(light9.paint.solve)
+        imp.reload(light9.paint.solve)
         self.settings.solver = light9.paint.solve.Solver(self.settings.graph)
         self.settings.solver.loadSamples()
 
--- a/bin/picamserve	Tue May 21 23:56:12 2019 +0000
+++ b/bin/picamserve	Wed May 22 00:08:22 2019 +0000
@@ -1,5 +1,5 @@
 #!env_pi/bin/python
-from __future__ import division
+
 from run_local import log
 import sys
 sys.path.append('/usr/lib/python2.7/dist-packages/')
--- a/bin/run_local.py	Tue May 21 23:56:12 2019 +0000
+++ b/bin/run_local.py	Wed May 22 00:08:22 2019 +0000
@@ -59,7 +59,7 @@
 from twisted.python.failure import Failure
 
 try:
-    import Tkinter
+    import tkinter
 except ImportError:
     pass
 else:
@@ -71,7 +71,7 @@
         else:
             Failure(val, exc, tb).printDetailedTraceback()
 
-    Tkinter.Tk.report_callback_exception = rce
+    tkinter.Tk.report_callback_exception = rce
 
 import coloredlogs, logging, time
 try:
@@ -103,7 +103,7 @@
 
 def setTerminalTitle(s):
     if os.environ.get('TERM', '') in ['xterm', 'rxvt', 'rxvt-unicode-256color']:
-        print "\033]0;%s\007" % s  # not escaped/protected correctly
+        print("\033]0;%s\007" % s)  # not escaped/protected correctly
 
 
 if 'listsongs' not in sys.argv[0] and 'homepageConfig' not in sys.argv[0]:
--- a/bin/staticclient	Tue May 21 23:56:12 2019 +0000
+++ b/bin/staticclient	Wed May 22 00:08:22 2019 +0000
@@ -2,10 +2,10 @@
 """
 push a dmx level forever
 """
-from __future__ import division, nested_scopes
+
 import time, logging
 from optparse import OptionParser
-import logging, urllib
+import logging, urllib.request, urllib.parse, urllib.error
 from twisted.internet import reactor, tksupport, task
 from rdflib import URIRef, RDF, RDFS, Literal
 
--- a/bin/subcomposer	Tue May 21 23:56:12 2019 +0000
+++ b/bin/subcomposer	Wed May 22 00:08:22 2019 +0000
@@ -15,7 +15,7 @@
 
 
 """
-from __future__ import division, nested_scopes
+
 
 from run_local import log
 import time, logging
@@ -23,8 +23,8 @@
 log.setLevel(logging.DEBUG)
 
 from optparse import OptionParser
-import logging, urllib
-import Tkinter as tk
+import logging, urllib.request, urllib.parse, urllib.error
+import tkinter as tk
 import louie as dispatcher
 from twisted.internet import reactor, tksupport, task
 from rdflib import URIRef, RDF, RDFS, Literal
@@ -119,7 +119,7 @@
         """promote our local submaster into a non-local, named one"""
         uri = self.currentSub().uri
         newUri = showconfig.showUri() + ("/sub/%s" %
-                                         urllib.quote(newName, safe=''))
+                                         urllib.parse.quote(newName, safe=''))
         with self.graph.currentState(tripleFilter=(uri, None, None)) as current:
             if (uri, RDF.type, L9['LocalSubmaster']) not in current:
                 raise ValueError("%s is not a local submaster" % uri)
@@ -161,8 +161,8 @@
         graph = self.graph
 
         def ann():
-            print "currently: session=%s currentSub=%r _currentChoice=%r" % (
-                self.session, self.currentSub(), self._currentChoice())
+            print("currently: session=%s currentSub=%r _currentChoice=%r" % (
+                self.session, self.currentSub(), self._currentChoice()))
 
         @graph.addHandler
         def graphChanged():
@@ -245,7 +245,7 @@
 
     def savenewsub(self, subname):
         leveldict = {}
-        for i, lev in zip(range(len(self.levels)), self.levels):
+        for i, lev in zip(list(range(len(self.levels))), self.levels):
             if lev != 0:
                 leveldict[get_channel_name(i + 1)] = lev
 
--- a/bin/tkdnd_minimal_drop.py	Tue May 21 23:56:12 2019 +0000
+++ b/bin/tkdnd_minimal_drop.py	Wed May 22 00:08:22 2019 +0000
@@ -1,6 +1,6 @@
 #!bin/python
 from run_local import log
-import Tkinter as tk
+import tkinter as tk
 from light9.tkdnd import initTkdnd, dropTargetRegister
 from twisted.internet import reactor, tksupport
 
@@ -20,15 +20,15 @@
 
 
 def onDrop(ev):
-    print "onDrop", ev
+    print("onDrop", ev)
 
 
 def enter(ev):
-    print 'enter', ev
+    print('enter', ev)
 
 
 def leave(ev):
-    print 'leave', ev
+    print('leave', ev)
 
 
 dropTargetRegister(label,
@@ -45,7 +45,7 @@
 
 
 def prn():
-    print "cont", root.winfo_containing(201, 151)
+    print("cont", root.winfo_containing(201, 151))
 
 
 b = tk.Button(root, text="coord", command=prn)
--- a/bin/tracker	Tue May 21 23:56:12 2019 +0000
+++ b/bin/tracker	Wed May 22 00:08:22 2019 +0000
@@ -1,5 +1,5 @@
 #!/usr/bin/python
-from __future__ import division, nested_scopes
+
 
 import sys
 sys.path.append("../../editor/pour")
@@ -14,7 +14,7 @@
 
 import dmxclient
 
-import Tkinter as tk
+import tkinter as tk
 
 defaultfont = "arial 8"
 
@@ -80,10 +80,10 @@
             name = f.name()
             intens = f.calc(x, y)
             if intens > 0:
-                print name, intens,
+                print(name, intens, end=' ')
                 active += 1
         if active > 0:
-            print
+            print()
         self.dmxsend(x, y)
 
     def dmxsend(self, x, y):
@@ -135,7 +135,7 @@
         w2c = self.canvas.world2canvas
 
         # rings
-        for intens, ring in self.rings.items():
+        for intens, ring in list(self.rings.items()):
             rad = f.getdistforintensity(intens)
             p1 = w2c(*(f.center() - Pair(rad, rad)))
             p2 = w2c(*(f.center() + Pair(rad, rad)))
@@ -251,7 +251,7 @@
             ev.x, ev.y)))
 
         def save(ev):
-            print "saving"
+            print("saving")
             self.fieldsetfile.save()
 
         master.bind("<Key-s>", save)
@@ -296,7 +296,7 @@
                               fill='white',
                               anchor=anc1 + anc2,
                               tags='cornercoords')
-        [d.setcoords() for d in self.displays.values()]
+        [d.setcoords() for d in list(self.displays.values())]
 
 
 ########################################################################
--- a/bin/wavecurve	Tue May 21 23:56:12 2019 +0000
+++ b/bin/wavecurve	Wed May 22 00:08:22 2019 +0000
@@ -5,12 +5,12 @@
 
 
 def createCurve(inpath, outpath, t):
-    print "reading %s, writing %s" % (inpath, outpath)
+    print("reading %s, writing %s" % (inpath, outpath))
     points = simp(inpath.replace('.ogg', '.wav'), seconds_per_average=t)
 
     f = file(outpath, 'w')
     for time_val in points:
-        print >> f, "%s %s" % time_val
+        print("%s %s" % time_val, file=f)
 
 
 parser = optparse.OptionParser(usage="""%prog inputSong.wav outputCurve
--- a/bin/webcontrol	Tue May 21 23:56:12 2019 +0000
+++ b/bin/webcontrol	Wed May 22 00:08:22 2019 +0000
@@ -6,7 +6,7 @@
 todo:
 disable buttons that don't make sense
 """
-import sys, xmlrpclib, traceback
+import sys, xmlrpc.client, traceback
 from twisted.internet import reactor
 from twisted.python import log
 from twisted.python.util import sibpath
@@ -19,7 +19,7 @@
 sys.path.append(".")
 from light9 import showconfig, networking
 from light9.namespaces import L9
-from urllib import urlencode
+from urllib.parse import urlencode
 
 
 # move to web lib
@@ -31,7 +31,7 @@
 
     @staticmethod
     def playSong(graph, songUri):
-        s = xmlrpclib.ServerProxy(networking.musicPlayer.url)
+        s = xmlrpc.client.ServerProxy(networking.musicPlayer.url)
         songPath = graph.value(URIRef(songUri), L9.showPath)
         if songPath is None:
             raise ValueError("unknown song %s" % songUri)
@@ -39,7 +39,7 @@
 
     @staticmethod
     def stopMusic(graph):
-        s = xmlrpclib.ServerProxy(networking.musicPlayer.url)
+        s = xmlrpc.client.ServerProxy(networking.musicPlayer.url)
         return s.stop()
 
     @staticmethod
@@ -90,14 +90,14 @@
         try:
             func = getattr(Commands, segments[0])
             req = inevow.IRequest(ctx)
-            simpleArgDict = dict((k, v[0]) for k, v in req.args.items())
+            simpleArgDict = dict((k, v[0]) for k, v in list(req.args.items()))
             try:
                 ret = yield robust_apply(func, func, self.graph,
                                          **simpleArgDict)
             except KeyboardInterrupt:
                 raise
-            except Exception, e:
-                print "Error on command %s" % segments[0]
+            except Exception as e:
+                print("Error on command %s" % segments[0])
                 traceback.print_exc()
                 returnValue((url.here.up().add('status',
                                                str(e)).add('error',
--- a/light9/Effects.py	Tue May 21 23:56:12 2019 +0000
+++ b/light9/Effects.py	Wed May 22 00:08:22 2019 +0000
@@ -1,10 +1,10 @@
-from __future__ import division
+
 import random as random_mod
 import math
 import logging, colorsys
 import light9.Submaster as Submaster
-from chase import chase as chase_logic
-import showconfig
+from .chase import chase as chase_logic
+from . import showconfig
 from rdflib import RDF
 from light9 import Patch
 from light9.namespaces import L9
@@ -77,7 +77,7 @@
 
     chase_vals = chase_logic(t, ontime, offset, onval, offval, names, combiner)
     lev = {}
-    for uri, value in chase_vals.items():
+    for uri, value in list(chase_vals.items()):
         try:
             dmx = Patch.dmx_from_uri(uri)
         except KeyError:
@@ -142,7 +142,7 @@
         shortName = chaseUri.rsplit('/')[-1]
         chans = graph.value(chaseUri, L9['channels'])
         ret[shortName] = list(graph.items(chans))
-        print "%r is a chase" % shortName
+        print("%r is a chase" % shortName)
 
     for f in registered:
         ret[f.__name__] = f
--- a/light9/Fadable.py	Tue May 21 23:56:12 2019 +0000
+++ b/light9/Fadable.py	Wed May 22 00:08:22 2019 +0000
@@ -1,5 +1,5 @@
 # taken from SnackMix -- now that's reusable code
-from Tix import *
+from tkinter.tix import *
 import time
 
 
--- a/light9/FlyingFader.py	Tue May 21 23:56:12 2019 +0000
+++ b/light9/FlyingFader.py	Wed May 22 00:08:22 2019 +0000
@@ -1,6 +1,6 @@
-from Tix import *
+from tkinter.tix import *
 from time import time, sleep
-from __future__ import division
+
 
 
 class Mass:
--- a/light9/Patch.py	Tue May 21 23:56:12 2019 +0000
+++ b/light9/Patch.py	Wed May 22 00:08:22 2019 +0000
@@ -11,8 +11,7 @@
 
 def get_all_channels():
     """returns primary names for all channels (sorted)"""
-    prinames = reverse_patch.values()[:]
-    prinames.sort()
+    prinames = sorted(list(reverse_patch.values())[:])
     return prinames
 
 
--- a/light9/Submaster.py	Tue May 21 23:56:12 2019 +0000
+++ b/light9/Submaster.py	Wed May 22 00:08:22 2019 +0000
@@ -1,4 +1,4 @@
-from __future__ import division
+
 import os, logging, time
 from rdflib import Graph, RDF
 from rdflib import RDFS, Literal, BNode
@@ -7,7 +7,7 @@
 from light9 import showconfig
 from light9.Patch import resolve_name, get_dmx_channel, get_channel_uri, reload_data
 from louie import dispatcher
-from rdfdb.patch import Patch
+from .rdfdb.patch import Patch
 log = logging.getLogger('submaster')
 
 class Submaster(object):
@@ -38,7 +38,7 @@
 
     def set_all_levels(self, leveldict):
         self.levels.clear()
-        for k, v in leveldict.items():
+        for k, v in list(leveldict.items()):
             # this may call _editedLevels too many times
             self.set_level(k, v, save=0)
 
@@ -46,7 +46,7 @@
         return self.levels
 
     def no_nonzero(self):
-        return all(v == 0 for v in self.levels.itervalues())
+        return all(v == 0 for v in self.levels.values())
 
     def __mul__(self, scalar):
         return Submaster("%s*%s" % (self.name, scalar),
@@ -62,8 +62,7 @@
         return (self.name, tuple(sorted(self.levels.items())))
 
     def __repr__(self):
-        items = getattr(self, 'levels', {}).items()
-        items.sort()
+        items = sorted(list(getattr(self, 'levels', {}).items()))
         levels = ' '.join(["%s:%.2f" % item for item in items])
         return "<'%s': [%s]>" % (getattr(self, 'name', 'no name yet'), levels)
 
@@ -80,7 +79,7 @@
         leveldict = self.get_levels() # gets levels of sub contents
 
         levels = []
-        for k, v in leveldict.items():
+        for k, v in list(leveldict.items()):
             if v == 0:
                 continue
             try:
@@ -114,9 +113,9 @@
         NOTE: You should only crossfade between normalized submasters."""
         otherlevels = othersub.get_levels()
         keys_set = {}
-        for k in self.levels.keys() + otherlevels.keys():
+        for k in list(self.levels.keys()) + list(otherlevels.keys()):
             keys_set[k] = 1
-        all_keys = keys_set.keys()
+        all_keys = list(keys_set.keys())
 
         xfaded_sub = Submaster("xfade", {})
         for k in all_keys:
@@ -241,7 +240,7 @@
         graph = Graph()
         subUri = L9['sub/%s' % self.name]
         graph.add((subUri, RDFS.label, Literal(self.name)))
-        for chan in self.levels.keys():
+        for chan in list(self.levels.keys()):
             try:
                 chanUri = get_channel_uri(chan)
             except KeyError:
@@ -279,7 +278,7 @@
     object.  You can give it a better name than the computed one that it
     will get or make it permanent if you'd like it to be saved to disk.
     Serves 8."""
-    scaledsubs = [sub * level for sub, level in subdict.items()]
+    scaledsubs = [sub * level for sub, level in list(subdict.items())]
     maxes = sub_maxes(*scaledsubs)
     if name:
         maxes.name = name
@@ -314,8 +313,7 @@
 
     def get_all_subs(self):
         "All Submaster objects"
-        l = self.submasters.items()
-        l.sort()
+        l = sorted(list(self.submasters.items()))
         l = [x[1] for x in l]
         songs = []
         notsongs = []
--- a/light9/TLUtility.py	Tue May 21 23:56:12 2019 +0000
+++ b/light9/TLUtility.py	Wed May 22 00:08:22 2019 +0000
@@ -1,7 +1,7 @@
 """Collected utility functions, many are taken from Drew's utils.py in
 Cuisine CVS and Hiss's Utility.py."""
 
-from __future__ import generators
+
 import sys
 
 __author__ = "David McClosky <dmcc@bigasterisk.com>, " + \
@@ -32,7 +32,7 @@
         try:
             setattr(callerself,a,callerlocals[a])
         except KeyError:
-            raise KeyError, "Function has no argument '%s'" % a
+            raise KeyError("Function has no argument '%s'" % a)
 
 def enumerate(*collections):
     """Generates an indexed series:  (0,coll[0]), (1,coll[1]) ...
@@ -42,19 +42,19 @@
     """
     i = 0
     iters = [iter(collection) for collection in collections]
-    while 1:
-        yield [i,] + [iterator.next() for iterator in iters]
+    while True:
+        yield [i,] + [next(iterator) for iterator in iters]
         i += 1
 
 def dumpobj(o):
     """Prints all the object's non-callable attributes"""
-    print repr(o)
+    print(repr(o))
     for a in [x for x in dir(o) if not callable(getattr(o, x))]:
         try:
-            print "  %20s: %s " % (a, getattr(o, a))
+            print("  %20s: %s " % (a, getattr(o, a)))
         except:
             pass
-    print ""
+    print("")
 
 def dict_filter_update(d, **newitems):
     """Adds a set of new keys and values to dictionary 'd' if the values are
@@ -65,7 +65,7 @@
     >>> some_dict
     {'c': 1, 's': 'hello'}
     """
-    for k, v in newitems.items():
+    for k, v in list(newitems.items()):
         if v: d[k] = v
 
 def try_get_logger(channel):
@@ -109,7 +109,7 @@
             import warnings
             warnings.warn(msg)
         if self.raise_exceptions:
-            raise AttributeError, msg
+            raise AttributeError(msg)
         return lambda *args, **kw: self.bogus_function()
     def bogus_function(self):
         pass
@@ -144,16 +144,16 @@
     TODO: print out default keywords (maybe)
           indent for recursive call like the lisp version (possible use of 
               generators?)"""
-    name = func.func_name
+    name = func.__name__
     def tracer(*args, **kw):
         s = '|>> %s called' % name
         if args:
             s += ' args: %r' % list(args)
         if kw:
             s += ' kw: %r' % kw
-        print s
+        print(s)
         ret = func(*args, **kw)
-        print '<<| %s returned %s' % (name, ret)
+        print('<<| %s returned %s' % (name, ret))
         return ret
     return tracer
 
@@ -165,13 +165,13 @@
     """
     newdict = {}
     for d in dicts:
-        for k,v in d.items():
+        for k,v in list(d.items()):
             newdict[k] = max(v, newdict.get(k, 0))
     return newdict
 
 def dict_scale(d,scl):
     """scales all values in dict and returns a new dict"""
-    return dict([(k,v*scl) for k,v in d.items()])
+    return dict([(k,v*scl) for k,v in list(d.items())])
     
 def dict_subset(d, dkeys, default=0):
     """Subset of dictionary d: only the keys in dkeys.  If you plan on omitting
--- a/light9/ascoltami/player.py	Tue May 21 23:56:12 2019 +0000
+++ b/light9/ascoltami/player.py	Wed May 22 00:08:22 2019 +0000
@@ -2,7 +2,7 @@
 """
 alternate to the mpd music player, for ascoltami
 """
-from __future__ import division
+
 import time, logging, traceback
 from gi.repository import GObject, Gst
 from twisted.internet import reactor, task
@@ -55,14 +55,14 @@
         bus.add_signal_watch()
 
         def onEos(*args):
-            print "onEos", args
+            print("onEos", args)
             if self.onEOS is not None:
                 self.onEOS(self.getSong())
 
         bus.connect('message::eos', onEos)
 
         def onStreamStatus(bus, message):
-            print "streamstatus", bus, message
+            print("streamstatus", bus, message)
             (statusType, _elem) = message.parse_stream_status()
             if statusType == Gst.StreamStatusType.ENTER:
                 self.setupAutostop()
--- a/light9/chase.py	Tue May 21 23:56:12 2019 +0000
+++ b/light9/chase.py	Wed May 22 00:08:22 2019 +0000
@@ -1,4 +1,4 @@
-from __future__ import division
+
 
 
 def chase(t,
@@ -43,6 +43,5 @@
                        ontime=0.1,
                        offset=0.2,
                        names=('a', 'b', 'c', 'd'))
-        output = output.items()
-        output.sort()
-        print "%.2f\t%s" % (x, ' '.join([str(x) for x in output]))
+        output = sorted(list(output.items()))
+        print("%.2f\t%s" % (x, ' '.join([str(x) for x in output])))
--- a/light9/clientsession.py	Tue May 21 23:56:12 2019 +0000
+++ b/light9/clientsession.py	Wed May 22 00:08:22 2019 +0000
@@ -3,7 +3,7 @@
 multiple instances of that client separate
 """
 from rdflib import URIRef
-from urllib import quote
+from urllib.parse import quote
 from light9 import showconfig
 
 
--- a/light9/collector/collector.py	Tue May 21 23:56:12 2019 +0000
+++ b/light9/collector/collector.py	Wed May 22 00:08:22 2019 +0000
@@ -1,4 +1,4 @@
-from __future__ import division
+
 import time
 import logging
 from rdflib import Literal
@@ -88,7 +88,7 @@
     def _forgetStaleClients(self, now):
         # type: (float) -> None
         staleClientSessions = []
-        for c, (t, _) in self.lastRequest.iteritems():
+        for c, (t, _) in self.lastRequest.items():
             if t < now - self.clientTimeoutSec:
                 staleClientSessions.append(c)
         for c in staleClientSessions:
@@ -118,7 +118,7 @@
     def _merge(self, lastRequests):
         deviceAttrs = {}  # device: {deviceAttr: value}
         for _, lastSettings in lastRequests:
-            for (device, deviceAttr), value in lastSettings.iteritems():
+            for (device, deviceAttr), value in lastSettings.items():
                 if (device, deviceAttr) in self.remapOut:
                     start, end = self.remapOut[(device, deviceAttr)]
                     value = Literal(start + float(value) * (end - start))
@@ -135,7 +135,7 @@
                     self.stickyAttrs[(device, deviceAttr)] = value
 
         # e.g. don't let an unspecified rotation go to 0
-        for (d, da), v in self.stickyAttrs.iteritems():
+        for (d, da), v in self.stickyAttrs.items():
             daDict = deviceAttrs.setdefault(d, {})
             if da not in daDict:
                 daDict[da] = v
@@ -163,7 +163,7 @@
         uniqueSettings = self.resolvedSettingsDict(settings)
         self.lastRequest[(client, clientSession)] = (now, uniqueSettings)
 
-        deviceAttrs = self._merge(self.lastRequest.itervalues())
+        deviceAttrs = self._merge(iter(self.lastRequest.values()))
 
         outputAttrs = {}  # device: {outputAttr: value}
         for d in self.allDevices:
@@ -184,8 +184,8 @@
         for out in self.outputs:
             pendingOut[out] = [0] * out.numChannels
 
-        for device, attrs in outputAttrs.iteritems():
-            for outputAttr, value in attrs.iteritems():
+        for device, attrs in outputAttrs.items():
+            for outputAttr, value in attrs.items():
                 self.setAttr(device, outputAttr, value, pendingOut)
 
         dt1 = 1000 * (time.time() - now)
@@ -204,6 +204,6 @@
 
     def flush(self, pendingOut):
         """write any changed outputs"""
-        for out, vals in pendingOut.iteritems():
+        for out, vals in pendingOut.items():
             out.update(vals)
             out.flush()
--- a/light9/collector/collector_client.py	Tue May 21 23:56:12 2019 +0000
+++ b/light9/collector/collector_client.py	Wed May 22 00:08:22 2019 +0000
@@ -1,4 +1,4 @@
-from __future__ import division
+
 from light9 import networking
 from light9.effect.settings import DeviceSettings
 from twisted.internet import defer
--- a/light9/collector/device.py	Tue May 21 23:56:12 2019 +0000
+++ b/light9/collector/device.py	Wed May 22 00:08:22 2019 +0000
@@ -1,4 +1,4 @@
-from __future__ import division
+
 import logging
 import math
 from light9.namespaces import L9, RDF, DEV
--- a/light9/collector/output.py	Tue May 21 23:56:12 2019 +0000
+++ b/light9/collector/output.py	Wed May 22 00:08:22 2019 +0000
@@ -1,4 +1,4 @@
-from __future__ import division
+
 from rdflib import URIRef
 import sys
 import time
@@ -169,7 +169,7 @@
         with Udmx.stats.write.time():
             try:
                 if not buf:
-                    print "skip empty msg"
+                    print("skip empty msg")
                     return True
                 self.dev.SendDMX(buf)
                 return True
@@ -178,7 +178,7 @@
                 if e.errno != 75:
                     msg = 'usb: sending %s bytes to %r; error %r' % (
                         len(buf), self.uri, e)
-                    print msg
+                    print(msg)
                 return False
 
     def countError(self):
--- a/light9/curvecalc/client.py	Tue May 21 23:56:12 2019 +0000
+++ b/light9/curvecalc/client.py	Wed May 22 00:08:22 2019 +0000
@@ -3,7 +3,7 @@
 """
 import cyclone.httpclient
 from light9 import networking
-import urllib
+import urllib.request, urllib.parse, urllib.error
 from run_local import log
 
 
@@ -11,7 +11,7 @@
     f = cyclone.httpclient.fetch(networking.curveCalc.path('liveInputPoint'),
                                  method='POST',
                                  timeout=1,
-                                 postdata=urllib.urlencode({
+                                 postdata=urllib.parse.urlencode({
                                      'curve': curve,
                                      'value': str(value),
                                  }))
--- a/light9/curvecalc/curve.py	Tue May 21 23:56:12 2019 +0000
+++ b/light9/curvecalc/curve.py	Wed May 22 00:08:22 2019 +0000
@@ -1,4 +1,4 @@
-from __future__ import division
+
 import glob, time, logging, ast, os
 from bisect import bisect_left, bisect
 import louie as dispatcher
@@ -55,7 +55,7 @@
     def set_from_string(self, pts):
         self.points[:] = []
         vals = pts.split()
-        pairs = zip(vals[0::2], vals[1::2])
+        pairs = list(zip(vals[0::2], vals[1::2]))
         for x, y in pairs:
             self.points.append((float(x), ast.literal_eval(y)))
         self.points.sort()
@@ -64,7 +64,7 @@
     def points_as_string(self):
 
         def outVal(x):
-            if isinstance(x, basestring):  # markers
+            if isinstance(x, str):  # markers
                 return x
             return "%.4g" % x
 
@@ -74,7 +74,7 @@
     def save(self, filename):
         # this is just around for markers, now
         if filename.endswith('-music') or filename.endswith('_music'):
-            print "not saving music track"
+            print("not saving music track")
             return
         f = file(filename, 'w')
         for p in self.points:
@@ -148,7 +148,7 @@
         leftidx = max(0, bisect(self.points, (x1, None)) - beyond)
         rightidx = min(len(self.points),
                        bisect(self.points, (x2, None)) + beyond)
-        return range(leftidx, rightidx)
+        return list(range(leftidx, rightidx))
 
     def points_between(self, x1, x2):
         """returns (x,y) points"""
@@ -329,7 +329,7 @@
         try:
             self.markers.load("%s.markers" % basename)
         except IOError:
-            print "no marker file found"
+            print("no marker file found")
 
     def save(self):
         """writes a file for each curve with a name
@@ -339,7 +339,7 @@
             showconfig.songFilenameFromURI(self.currentSong))
 
         patches = []
-        for cr in self.curveResources.values():
+        for cr in list(self.curveResources.values()):
             patches.extend(cr.getSavePatches())
 
         self.markers.save("%s.markers" % basename)
--- a/light9/curvecalc/curveview.py	Tue May 21 23:56:12 2019 +0000
+++ b/light9/curvecalc/curveview.py	Wed May 22 00:08:22 2019 +0000
@@ -1,4 +1,4 @@
-from __future__ import division
+
 import math, logging, traceback
 from gi.repository import Gtk
 from gi.repository import Gdk
@@ -10,9 +10,10 @@
 from light9.curvecalc import cursors
 from light9.curvecalc.curve import introPad, postPad
 from lib.goocanvas_compat import Points, polyline_new_line
+import imp
 
 log = logging.getLogger()
-print "curveview.py toplevel"
+print("curveview.py toplevel")
 
 
 def vlen(v):
@@ -48,8 +49,7 @@
         self.curveview.add_point(p)
 
     def release(self, ev):
-        pts = self.pts
-        pts.sort()
+        pts = sorted(self.pts)
         finalPoints = pts[:]
 
         dx = .01
@@ -168,7 +168,7 @@
 
     def onMotion(self, item, target_item, event, param):
         if hasattr(self, 'dragStartTime'):
-            origPts = zip(self.getSelectedIndices(), self.origPoints)
+            origPts = list(zip(self.getSelectedIndices(), self.origPoints))
             left = origPts[0][1][0]
             right = origPts[-1][1][0]
             width = right - left
@@ -461,7 +461,7 @@
         return canvas
 
     def onAny(self, w, event):
-        print "   %s on %s" % (event, w)
+        print("   %s on %s" % (event, w))
 
     def onFocusIn(self, *args):
         dispatcher.send('curve row focus change')
@@ -555,8 +555,8 @@
 
     def print_state(self, msg=""):
         if 0:
-            print "%s: dragging_dots=%s selecting=%s" % (
-                msg, self.dragging_dots, self.selecting)
+            print("%s: dragging_dots=%s selecting=%s" % (
+                msg, self.dragging_dots, self.selecting))
 
     def select_points(self, pts):
         """set selection to the given point values (tuples, not indices)"""
@@ -749,7 +749,7 @@
         if not self.alive():
             return
         if not self.redrawsEnabled:
-            print "no redrawsEnabled, skipping", self
+            print("no redrawsEnabled, skipping", self)
             return
 
         visible_x = (self.world_from_screen(0, 0)[0],
@@ -993,7 +993,7 @@
         if not self.redrawsEnabled:
             return
 
-        for i, d in self.dots.items():
+        for i, d in list(self.dots.items()):
             if i in self.selected_points:
                 d.set_property('fill_color', 'red')
             else:
@@ -1161,7 +1161,7 @@
         controls.add(box)
 
         curve_name_label = Gtk.LinkButton()
-        print "need to truncate this name length somehow"
+        print("need to truncate this name length somehow")
 
         def update_label():
             # todo: abort if we don't still exist...
@@ -1237,11 +1237,11 @@
         self.watchCurveAreaHeight()
 
     def __del__(self):
-        print "del curvesetview", id(self)
+        print("del curvesetview", id(self))
 
     def initZoomControl(self, zoomControlBox):
         import light9.curvecalc.zoomcontrol
-        reload(light9.curvecalc.zoomcontrol)
+        imp.reload(light9.curvecalc.zoomcontrol)
         zoomControl = light9.curvecalc.zoomcontrol.ZoomControl()
         zoomControlBox.add(zoomControl.widget)
         zoomControl.widget.show_all()
--- a/light9/curvecalc/output.py	Tue May 21 23:56:12 2019 +0000
+++ b/light9/curvecalc/output.py	Wed May 22 00:08:22 2019 +0000
@@ -29,7 +29,7 @@
 
     def updateerr(self, e):
 
-        print e.getTraceback()
+        print(e.getTraceback())
         dispatcher.send("update status", val=e.getErrorMessage())
         if self.later and not self.later.cancelled and not self.later.called:
             self.later.cancel()
--- a/light9/curvecalc/subterm.py	Tue May 21 23:56:12 2019 +0000
+++ b/light9/curvecalc/subterm.py	Wed May 22 00:08:22 2019 +0000
@@ -91,7 +91,7 @@
                     subUri = current.value(self.uri, L9['sub'])
                     sub = self.submasters.get_sub_by_uri(subUri)
                     return sub * subexpr_eval
-            except Exception, e:
+            except Exception as e:
                 dispatcher.send("expr_error", sender=self.uri, exc=repr(e))
                 return Submaster.Submaster(name='Error: %s' % str(e), levels={})
 
@@ -133,7 +133,7 @@
 
         try:
             self.lasteval = eval(expr, glo)
-        except Exception, e:
+        except Exception as e:
             dispatcher.send("expr_error", sender=self.uri, exc=e)
             return Submaster.Submaster("zero", {})
         else:
--- a/light9/curvecalc/subtermview.py	Tue May 21 23:56:12 2019 +0000
+++ b/light9/curvecalc/subtermview.py	Wed May 22 00:08:22 2019 +0000
@@ -42,8 +42,7 @@
         curveNames = self.curveset.curveNamesInOrder()
         currentExpr = self.entryBuffer.get_text()
 
-        usedCurves = [n for n in curveNames if n in currentExpr]
-        usedCurves.sort()
+        usedCurves = sorted([n for n in curveNames if n in currentExpr])
 
         dispatcher.send("set_featured_curves", curveNames=usedCurves)
 
@@ -52,7 +51,7 @@
 
     def set_expression_from_graph(self):
         e = str(self.graph.value(self.ownerSubterm, L9['expression']))
-        print "from graph, set to %r" % e
+        print("from graph, set to %r" % e)
 
         if e != self.entryBuffer.get_text():
             self.entryBuffer.set_text(e, len(e))
--- a/light9/curvecalc/zoomcontrol.py	Tue May 21 23:56:12 2019 +0000
+++ b/light9/curvecalc/zoomcontrol.py	Wed May 22 00:08:22 2019 +0000
@@ -1,4 +1,4 @@
-from __future__ import division
+
 from gi.repository import Gtk
 from gi.repository import GObject
 from gi.repository import GooCanvas
--- a/light9/dmxchanedit.py	Tue May 21 23:56:12 2019 +0000
+++ b/light9/dmxchanedit.py	Wed May 22 00:08:22 2019 +0000
@@ -16,8 +16,8 @@
 - we have to stop packing these into the names. Names should be like 'b33' or 'blue3' or just '44'. maybe 'blacklight'.
 
 """
-from __future__ import nested_scopes, division
-import Tkinter as tk
+
+import tkinter as tk
 from rdflib import RDF, Literal
 import math, logging
 from decimal import Decimal
@@ -227,7 +227,7 @@
                 continue
             if isinstance(lev, Decimal):
                 lev = float(lev)
-            assert isinstance(lev, (int, long, float)), repr(lev)
+            assert isinstance(lev, (int, float)), repr(lev)
             try:
                 self.levelFromUri[chan].setTo(lev)
                 remaining.remove(chan)
--- a/light9/dmxclient.py	Tue May 21 23:56:12 2019 +0000
+++ b/light9/dmxclient.py	Wed May 22 00:08:22 2019 +0000
@@ -4,7 +4,7 @@
 
 client id is formed from sys.argv[0] and the PID.  """
 
-import xmlrpclib, os, sys, socket, time, logging
+import xmlrpc.client, os, sys, socket, time, logging
 from twisted.internet import defer
 from txzmq import ZmqEndpoint, ZmqFactory, ZmqPushConnection
 import json
@@ -50,17 +50,17 @@
     if _dmx is None:
         url = networking.dmxServer.url
         if not twisted:
-            _dmx = xmlrpclib.Server(url)
+            _dmx = xmlrpc.client.Server(url)
         else:
             _dmx = TwistedZmqClient(networking.dmxServerZmq)
 
     if not twisted:
         try:
             _dmx.outputlevels(clientid, levellist)
-        except socket.error, e:
+        except socket.error as e:
             log.error("dmx server error %s, waiting" % e)
             time.sleep(1)
-        except xmlrpclib.Fault, e:
+        except xmlrpc.client.Fault as e:
             log.error("outputlevels had xml fault: %s" % e)
             time.sleep(1)
     else:
@@ -70,7 +70,7 @@
 
 dummy = os.getenv('DMXDUMMY')
 if dummy:
-    print "dmxclient: DMX is in dummy mode."
+    print("dmxclient: DMX is in dummy mode.")
 
     def outputlevels(*args, **kw):
         pass
--- a/light9/editchoice.py	Tue May 21 23:56:12 2019 +0000
+++ b/light9/editchoice.py	Wed May 22 00:08:22 2019 +0000
@@ -1,4 +1,4 @@
-import Tkinter as tk
+import tkinter as tk
 from rdflib import URIRef
 from light9.tkdnd import dragSourceRegister, dropTargetRegister
 
@@ -110,7 +110,7 @@
 
     def updateLabel(self):
         uri = self.resourceObservable()
-        print "get label", repr(uri)
+        print("get label", repr(uri))
         label = self.graph.label(uri)
         self.subIcon.config(text=label or uri)
 
--- a/light9/effect/edit.py	Tue May 21 23:56:12 2019 +0000
+++ b/light9/effect/edit.py	Wed May 22 00:08:22 2019 +0000
@@ -65,9 +65,9 @@
 
         _maybeAddMusicLine(quads, effect, song, ctx)
 
-    print "adding"
+    print("adding")
     for qq in quads:
-        print qq
+        print(qq)
     returnValue(Patch(addQuads=quads))
 
 
@@ -163,7 +163,7 @@
         (song, L9['effect'], effect, ctx),
         (effect, RDF.type, L9['Effect'], ctx),
     ]
-    print "_newEffect", effect, quads
+    print("_newEffect", effect, quads)
     return effect, quads
 
 
--- a/light9/effect/effecteval.py	Tue May 21 23:56:12 2019 +0000
+++ b/light9/effect/effecteval.py	Wed May 22 00:08:22 2019 +0000
@@ -1,4 +1,4 @@
-from __future__ import division
+
 from rdflib import URIRef, Literal
 from light9.namespaces import L9, RDF, DEV
 from webcolors import rgb_to_hex, hex_to_rgb
@@ -13,7 +13,7 @@
 from light9.effect.scale import scale
 import random
 random.seed(0)
-print "reload effecteval"
+print("reload effecteval")
 
 log = logging.getLogger('effecteval')
 
@@ -96,7 +96,7 @@
             else:
                 out.update(func(effectSettings, strength, songTime, noteTime))
 
-        outList = [(d, a, v) for (d, a), v in out.iteritems()]
+        outList = [(d, a, v) for (d, a), v in out.items()]
         return DeviceSettings(self.graph, outList), report
 
 
@@ -137,7 +137,7 @@
     out = {}
     tint = effectSettings.get(L9['tint'], '#ffffff')
     tintStrength = float(effectSettings.get(L9['tintStrength'], 0))
-    print effectSettings
+    print(effectSettings)
     tr, tg, tb = hex_to_rgb(tint)
     for n in range(1, 5 + 1):
         scl = strength * ((int(songTime * 10) % n) < 1)
@@ -209,7 +209,7 @@
     col = literalColorHsv(noise(noisePos), 1, scl)
     col = scale(col, effectSettings.get(L9['colorScale']) or '#ffffff')
 
-    print songTime, quantTime, col
+    print(songTime, quantTime, col)
 
     for n in range(1, 5 + 1):
         dev = L9['device/aura%s' % n]
--- a/light9/effect/scale.py	Tue May 21 23:56:12 2019 +0000
+++ b/light9/effect/scale.py	Wed May 22 00:08:22 2019 +0000
@@ -1,4 +1,4 @@
-from __future__ import division
+
 from rdflib import Literal
 from decimal import Decimal
 from webcolors import rgb_to_hex, hex_to_rgb
@@ -11,14 +11,14 @@
     if isinstance(value, Decimal):
         value = float(value)
 
-    if isinstance(value, basestring):
+    if isinstance(value, str):
         if value[0] == '#':
             if strength == '#ffffff':
                 return value
             r, g, b = hex_to_rgb(value)
             if isinstance(strength, Literal):
                 strength = strength.toPython()
-            if isinstance(strength, basestring):
+            if isinstance(strength, str):
                 sr, sg, sb = [v / 255 for v in hex_to_rgb(strength)]
             else:
                 sr = sg = sb = strength
--- a/light9/effect/sequencer.py	Tue May 21 23:56:12 2019 +0000
+++ b/light9/effect/sequencer.py	Wed May 22 00:08:22 2019 +0000
@@ -2,7 +2,7 @@
 copies from effectloop.py, which this should replace
 '''
 
-from __future__ import division
+
 from louie import dispatcher
 from rdflib import URIRef
 from twisted.internet import reactor
@@ -20,6 +20,7 @@
 from light9.effect.simple_outputs import SimpleOutputs
 
 from greplin import scales
+import imp
 
 log = logging.getLogger('sequencer')
 stats = scales.collection(
@@ -95,7 +96,7 @@
             (str(k), str(v)) for k, v in sorted(effectSettings.items()))
         report['nonZero'] = effectSettings[L9['strength']] > 0
         out, evalReport = self.effectEval.outputFromEffect(
-            effectSettings.items(),
+            list(effectSettings.items()),
             songTime=t,
             # note: not using origin here since it's going away
             noteTime=t - self.points[0][0])
@@ -118,7 +119,7 @@
 
         def go():
             log.info("reload effecteval")
-            reload(effecteval)
+            imp.reload(effecteval)
             self.onChange()
 
         # in case we got an event at the start of the write
--- a/light9/effect/settings.py	Tue May 21 23:56:12 2019 +0000
+++ b/light9/effect/settings.py	Wed May 22 00:08:22 2019 +0000
@@ -1,4 +1,4 @@
-from __future__ import division
+
 """
 Data structure and convertors for a table of (device,attr,value)
 rows. These might be effect attrs ('strength'), device attrs ('rx'),
@@ -16,7 +16,7 @@
 
 def parseHex(h):
     if h[0] != '#': raise ValueError(h)
-    return [int(h[i:i + 2], 16) for i in 1, 3, 5]
+    return [int(h[i:i + 2], 16) for i in (1, 3, 5)]
 
 
 def parseHexNorm(h):
@@ -112,7 +112,7 @@
                     raise TypeError('bad row %r' % (row,))
                 dd = out._compiled.setdefault(row[0], {})
 
-                if isinstance(row[2], basestring):
+                if isinstance(row[2], str):
                     prev = parseHexNorm(dd.get(row[1], '#000000'))
                     newVal = toHex(prev +
                                    weight * numpy.array(parseHexNorm(row[2])))
@@ -128,8 +128,8 @@
         return 0.0
 
     def _delZeros(self):
-        for dev, av in self._compiled.items():
-            for attr, val in av.items():
+        for dev, av in list(self._compiled.items()):
+            for attr, val in list(av.items()):
                 if val == self._zeroForAttr(attr):
                     del av[attr]
             if not av:
@@ -150,15 +150,15 @@
     def __ne__(self, other):
         return not self == other
 
-    def __nonzero__(self):
+    def __bool__(self):
         return bool(self._compiled)
 
     def __repr__(self):
         words = []
 
         def accum():
-            for dev, av in self._compiled.iteritems():
-                for attr, val in sorted(av.iteritems()):
+            for dev, av in self._compiled.items():
+                for attr, val in sorted(av.items()):
                     words.append(
                         '%s.%s=%s' %
                         (dev.rsplit('/')[-1], attr.rsplit('/')[-1], val))
@@ -179,13 +179,13 @@
     def asList(self):
         """old style list of (dev, attr, val) tuples"""
         out = []
-        for dev, av in self._compiled.iteritems():
-            for attr, val in av.iteritems():
+        for dev, av in self._compiled.items():
+            for attr, val in av.items():
                 out.append((dev, attr, val))
         return out
 
     def devices(self):
-        return self._compiled.keys()
+        return list(self._compiled.keys())
 
     def toVector(self, deviceAttrFilter=None):
         out = []
@@ -198,7 +198,7 @@
         return out
 
     def byDevice(self):
-        for dev, av in self._compiled.iteritems():
+        for dev, av in self._compiled.items():
             yield dev, self.__class__._fromCompiled(self.graph, {dev: av})
 
     def ofDevice(self, dev):
--- a/light9/effect/simple_outputs.py	Tue May 21 23:56:12 2019 +0000
+++ b/light9/effect/simple_outputs.py	Wed May 22 00:08:22 2019 +0000
@@ -1,4 +1,4 @@
-from __future__ import division
+
 import traceback
 from light9.namespaces import L9, RDF
 from light9.effect.scale import scale
--- a/light9/effecteval/effect.py	Tue May 21 23:56:12 2019 +0000
+++ b/light9/effecteval/effect.py	Wed May 22 00:08:22 2019 +0000
@@ -81,7 +81,7 @@
         """
         out = {}
         subs = prof.logTime(Submaster.get_global_submasters)(self.graph)
-        for localVar, uri in resources.items():
+        for localVar, uri in list(resources.items()):
 
             for rdfClass in self.graph.objects(uri, RDF.type):
                 if rdfClass == L9['Curve']:
@@ -131,7 +131,7 @@
         deps = {}
         for c in self.codes:
             outName = c.outName
-            inNames = c.possibleVars.intersection(codeFromOutput.keys())
+            inNames = c.possibleVars.intersection(list(codeFromOutput.keys()))
             inNames.discard(outName)
             deps[outName] = inNames
         self.codes = [
--- a/light9/effecteval/effectloop.py	Tue May 21 23:56:12 2019 +0000
+++ b/light9/effecteval/effectloop.py	Wed May 22 00:08:22 2019 +0000
@@ -1,4 +1,4 @@
-from __future__ import division
+
 import time, json, logging, traceback
 import numpy
 import serial
@@ -183,7 +183,7 @@
 
     def logMessage(self, out):
         return ("send dmx: {%s}" % ", ".join(
-            "%r: %.3g" % (str(k), v) for k, v in out.get_levels().items()))
+            "%r: %.3g" % (str(k), v) for k, v in list(out.get_levels().items())))
 
 
 Z = numpy.zeros((50, 3), dtype=numpy.float16)
@@ -306,7 +306,7 @@
 
     def logMessage(self, out):
         return str([(w, p.tolist() if isinstance(p, numpy.ndarray) else p)
-                    for w, p in out.items()])
+                    for w, p in list(out.items())])
 
 
 def makeEffectLoop(graph, stats, outputWhere):
--- a/light9/effecteval/test_effect.py	Tue May 21 23:56:12 2019 +0000
+++ b/light9/effecteval/test_effect.py	Wed May 22 00:08:22 2019 +0000
@@ -3,7 +3,7 @@
 import sys
 sys.path.insert(0, 'bin')  # for run_local
 
-from effect import CodeLine
+from .effect import CodeLine
 from rdflib import URIRef
 
 
@@ -76,8 +76,8 @@
         self.assertEqual(set([]), CodeLine(None, 'a1 = 1').possibleVars)
 
     def test2(self):
-        self.assertEqual(set(['a2']), CodeLine(None, 'a1 = a2').possibleVars)
+        self.assertEqual({'a2'}, CodeLine(None, 'a1 = a2').possibleVars)
 
     def test3(self):
-        self.assertEqual(set(['a2', 'a3']),
+        self.assertEqual({'a2', 'a3'},
                          CodeLine(None, 'a1 = a2 + a3').possibleVars)
--- a/light9/io/__init__.py	Tue May 21 23:56:12 2019 +0000
+++ b/light9/io/__init__.py	Wed May 22 00:08:22 2019 +0000
@@ -1,4 +1,4 @@
-from __future__ import division
+
 import sys
 
 
@@ -11,14 +11,14 @@
 
     def golive(self):
         """call this if you want to promote the dummy object becomes a live object"""
-        print "IO: %s is going live" % self.__name__
+        print("IO: %s is going live" % self.__name__)
         self.dummy = 0
         # you'd override with additional startup stuff here,
         # perhaps even loading a module and saving it to a class
         # attr so the subclass-specific functions can use it
 
     def godummy(self):
-        print "IO: %s is going dummy" % self.__name__
+        print("IO: %s is going dummy" % self.__name__)
         self.dummy = 1
         # you might override this to close ports, etc
 
@@ -44,7 +44,7 @@
 
     def golive(self):
         BaseIO.golive(self)
-        import parport
+        from . import parport
         self.parport = parport
         self.parport.getparport()
 
@@ -71,7 +71,7 @@
     def _dmx(self):
         if self.out is None:
             if self.port == 'udmx':
-                from udmx import Udmx
+                from .udmx import Udmx
                 self.out = Udmx()
                 self.out.write = self.out.SendDMX
             else:
--- a/light9/io/udmx.py	Tue May 21 23:56:12 2019 +0000
+++ b/light9/io/udmx.py	Wed May 22 00:08:22 2019 +0000
@@ -1,4 +1,4 @@
-from __future__ import division
+
 import logging
 import usb.core
 from usb.util import CTRL_TYPE_VENDOR, CTRL_RECIPIENT_DEVICE, CTRL_OUT
@@ -30,7 +30,7 @@
         for dev in usb.core.find(idVendor=0x16c0,
                                  idProduct=0x05dc,
                                  find_all=True):
-            print "udmx device at %r" % dev.bus
+            print("udmx device at %r" % dev.bus)
             if bus is None or bus == dev.bus:
                 self.dev = dev
         if not self.dev:
@@ -59,5 +59,5 @@
             u.SendDMX('\x00' * (chan - 1) + chr(210) + chr(nsin8) + chr(nsin8) +
                       chr(nsin8))
         except usb.core.USBError as e:
-            print "err", time.time(), repr(e)
+            print("err", time.time(), repr(e))
         time.sleep(1 / fps)
--- a/light9/networking.py	Tue May 21 23:56:12 2019 +0000
+++ b/light9/networking.py	Wed May 22 00:08:22 2019 +0000
@@ -1,7 +1,7 @@
-from urlparse import urlparse
-from urllib import splitport
-from showconfig import getGraph, showUri
-from namespaces import L9
+from urllib.parse import urlparse
+from urllib.parse import splitport
+from .showconfig import getGraph, showUri
+from .namespaces import L9
 
 
 class ServiceAddress(object):
--- a/light9/paint/solve.py	Tue May 21 23:56:12 2019 +0000
+++ b/light9/paint/solve.py	Wed May 22 00:08:22 2019 +0000
@@ -1,4 +1,4 @@
-from __future__ import division
+
 from light9.namespaces import RDF, L9, DEV
 from PIL import Image
 import numpy
@@ -152,7 +152,7 @@
         results = []
         dist = ImageDist(img)
         if device is None:
-            items = self.samples.items()
+            items = list(self.samples.items())
         else:
             items = self.samplesForDevice[device]
         for uri, img2 in sorted(items):
@@ -162,9 +162,9 @@
             results.append((dist.distanceTo(img2), uri, img2))
         results.sort()
         topDist, topUri, topImg = results[0]
-        print 'tops2'
+        print('tops2')
         for row in results[:4]:
-            print '%.5f' % row[0], row[1][-20:], self.sampleSettings[row[1]]
+            print('%.5f' % row[0], row[1][-20:], self.sampleSettings[row[1]])
 
         #saveNumpy('/tmp/best_in.png', img)
         #saveNumpy('/tmp/best_out.png', topImg)
@@ -221,8 +221,7 @@
             #saveNumpy('/tmp/sample_%s.png' % sample.split('/')[-1],
             #          f(picSample))
             sampleDist[sample] = dist.distanceTo(picSample)
-        results = [(d, uri) for uri, d in sampleDist.items()]
-        results.sort()
+        results = sorted([(d, uri) for uri, d in list(sampleDist.items())])
 
         sample = results[0][1]
 
@@ -284,7 +283,7 @@
 
     def combineImages(self, layers):
         """make a result image from our self.samples images"""
-        out = (self.fromPath.itervalues().next() * 0).astype(numpy.uint16)
+        out = (next(iter(self.fromPath.values())) * 0).astype(numpy.uint16)
         for layer in layers:
             colorScaled = self.fromPath[layer['path']] * layer['color']
             out += colorScaled.astype(numpy.uint16)
@@ -302,7 +301,7 @@
         for dev, devSettings in settings.byDevice():
             requestedColor = devSettings.getValue(dev, L9['color'])
             candidatePics = []  # (distance, path, picColor)
-            for sample, s in self.sampleSettings.items():
+            for sample, s in list(self.sampleSettings.items()):
                 path = self.path[sample]
                 otherDevSettings = s.ofDevice(dev)
                 if not otherDevSettings:
--- a/light9/paint/solve_test.py	Tue May 21 23:56:12 2019 +0000
+++ b/light9/paint/solve_test.py	Wed May 22 00:08:22 2019 +0000
@@ -1,6 +1,6 @@
 import unittest
 import numpy.testing
-import solve
+from . import solve
 from rdflib import Namespace
 from light9.namespaces import RDF, L9, DEV
 from rdfdb.localsyncedgraph import LocalSyncedGraph
@@ -33,7 +33,7 @@
         })
         self.assertEqual(
             DeviceSettings(self.graph, [
-                (DEV['aura1'], L9['color'], u"#ffffff"),
+                (DEV['aura1'], L9['color'], "#ffffff"),
                 (DEV['aura1'], L9['rx'], 0.5),
                 (DEV['aura1'], L9['ry'], 0.573),
             ]), devAttrs)
@@ -68,7 +68,7 @@
         layers = self.solver.simulationLayers(
             settings=DeviceSettings(self.graph, [(
                 DEV['aura1'], L9['color'],
-                u"#ffffff"), (DEV['aura1'], L9['rx'],
+                "#ffffff"), (DEV['aura1'], L9['rx'],
                               0.5), (DEV['aura1'], L9['ry'], 0.573)]))
         self.assertEqual([{
             'path': CAM_TEST['bg2-d.jpg'],
@@ -79,7 +79,7 @@
         layers = self.solver.simulationLayers(
             settings=DeviceSettings(self.graph, [(
                 DEV['aura1'], L9['color'],
-                u"#304050"), (DEV['aura1'], L9['rx'],
+                "#304050"), (DEV['aura1'], L9['rx'],
                               0.5), (DEV['aura1'], L9['ry'], 0.573)]))
         self.assertEqual([{
             'path': CAM_TEST['bg2-d.jpg'],
@@ -89,10 +89,10 @@
     def testPerfect2Matches(self):
         layers = self.solver.simulationLayers(
             settings=DeviceSettings(self.graph, [
-                (DEV['aura1'], L9['color'], u"#ffffff"),
+                (DEV['aura1'], L9['color'], "#ffffff"),
                 (DEV['aura1'], L9['rx'], 0.5),
                 (DEV['aura1'], L9['ry'], 0.573),
-                (DEV['aura2'], L9['color'], u"#ffffff"),
+                (DEV['aura2'], L9['color'], "#ffffff"),
                 (DEV['aura2'], L9['rx'], 0.7),
                 (DEV['aura2'], L9['ry'], 0.573),
             ]))
--- a/light9/prof.py	Tue May 21 23:56:12 2019 +0000
+++ b/light9/prof.py	Wed May 22 00:08:22 2019 +0000
@@ -37,8 +37,8 @@
             if (frame.f_code.co_filename, frame.f_lineno) == (filename, lineno):
                 stack = ''.join(traceback.format_stack(frame))
                 if stack not in seenTraces:
-                    print "watchPoint hit"
-                    print stack
+                    print("watchPoint hit")
+                    print(stack)
                     seenTraces[stack] = 1
                 else:
                     seenTraces[stack] += 1
--- a/light9/showconfig.py	Tue May 21 23:56:12 2019 +0000
+++ b/light9/showconfig.py	Wed May 22 00:08:22 2019 +0000
@@ -3,7 +3,7 @@
 from os import path, getenv
 from rdflib import Graph
 from rdflib import URIRef
-from namespaces import MUS, L9
+from .namespaces import MUS, L9
 log = logging.getLogger('showconfig')
 
 _config = None # graph
--- a/light9/tkdnd.py	Tue May 21 23:56:12 2019 +0000
+++ b/light9/tkdnd.py	Wed May 22 00:08:22 2019 +0000
@@ -45,7 +45,7 @@
         self.oldStyle = {}
 
     def set(self, ev):
-        for k, v in self.style.items():
+        for k, v in list(self.style.items()):
             self.oldStyle[k] = self.widget.cget(k)
         self.widget.configure(**self.style)
         return ev.action
--- a/light9/uihelpers.py	Tue May 21 23:56:12 2019 +0000
+++ b/light9/uihelpers.py	Wed May 22 00:08:22 2019 +0000
@@ -1,11 +1,11 @@
 """all the tiny tk helper functions"""
 
-from __future__ import nested_scopes
+
 #from Tkinter import Button
 import logging, time
 from rdflib import Literal
-from Tix import Button, Toplevel, Tk, IntVar, Entry, DoubleVar
-import Tkinter
+from tkinter.tix import Button, Toplevel, Tk, IntVar, Entry, DoubleVar
+import tkinter
 from light9.namespaces import L9
 
 log = logging.getLogger("toplevel")
@@ -33,7 +33,7 @@
             f=open(".light9-window-geometry-%s" % name.replace(' ','_'),'w')
             f.write(tl.geometry())
         # else the window never got mapped
-    except Exception, e:
+    except Exception as e:
         # it's ok if there's no saved geometry
         pass
 
@@ -62,7 +62,7 @@
 
     def savePos(ev):
         geo = tl.geometry()
-        if not isinstance(ev.widget, (Tk, Tkinter.Tk)):
+        if not isinstance(ev.widget, (Tk, tkinter.Tk)):
             # I think these are due to internal widget size changes,
             # not the toplevel changing
             return
@@ -103,12 +103,12 @@
 
 # for lambda callbacks
 def printout(t):
-    print 'printout', t
+    print('printout', t)
 
 def printevent(ev):
     for k in dir(ev):
         if not k.startswith('__'):
-            print 'ev', k, getattr(ev,k)
+            print('ev', k, getattr(ev,k))
 
 def eventtoparent(ev,sequence):
     "passes an event to the parent, screws up TixComboBoxes"
@@ -131,7 +131,7 @@
     txt=label['text'] or "0"
     lev=float(txt)/100
     low=(80,80,180)
-    high=(255,55,050)
+    high=(255,55,0o50)
     out = [int(l+lev*(h-l)) for h,l in zip(high,low)]
     col="#%02X%02X%02X" % tuple(out)
     label.config(bg=col)
@@ -218,19 +218,19 @@
         return cbname
     trace=trace_variable
     def disable_traces(self):
-        for cb,mode in self.callbacklist.items():
+        for cb,mode in list(self.callbacklist.items()):
 #            DoubleVar.trace_vdelete(self,v[0],k)
             self._tk.call("trace", "vdelete", self._name, mode,cb)
             # but no master delete!
 
     def recreate_traces(self):
-        for cb,mode in self.callbacklist.items():
+        for cb,mode in list(self.callbacklist.items()):
 #            self.trace_variable(v[0],v[1])
             self._tk.call("trace", "variable", self._name, mode,cb)
 
     def trace_named(self, name, callback):
         if name in self.namedtraces:
-            print "FancyDoubleVar: already had a trace named %s - replacing it" % name
+            print("FancyDoubleVar: already had a trace named %s - replacing it" % name)
             self.delete_named(name)
 
         cbname = self.trace_variable('w',callback) # this will register in self.callbacklist too
@@ -245,9 +245,9 @@
 
             self.trace_vdelete('w',cbname)
 	    #self._tk.call("trace","vdelete",self._name,'w',cbname)
-            print "FancyDoubleVar: successfully deleted trace named %s" % name
+            print("FancyDoubleVar: successfully deleted trace named %s" % name)
         else:
-            print "FancyDoubleVar: attempted to delete named %s which wasn't set to any function" % name
+            print("FancyDoubleVar: attempted to delete named %s which wasn't set to any function" % name)
 
 def get_selection(listbox):
     'Given a listbox, returns first selection as integer'
@@ -259,7 +259,7 @@
     root.tk_focusFollowsMouse()
     iv=IntVar()
     def cb():
-        print "cb!"
+        print("cb!")
     t = Togglebutton(root,text="testbutton",command=cb,variable=iv)
     t.pack()
     Entry(root,textvariable=iv).pack()
--- a/light9/updatefreq.py	Tue May 21 23:56:12 2019 +0000
+++ b/light9/updatefreq.py	Wed May 22 00:08:22 2019 +0000
@@ -1,6 +1,6 @@
 """calculates your updates-per-second"""
 
-from __future__ import division
+
 import time
 
 class Updatefreq:
--- a/light9/vidref/main.py	Tue May 21 23:56:12 2019 +0000
+++ b/light9/vidref/main.py	Wed May 22 00:08:22 2019 +0000
@@ -90,7 +90,7 @@
         self.pipeline.setLiveVideo(widget.get_active())
 
     def on_liveFrameRate_value_changed(self, widget):
-        print widget.get_value()
+        print(widget.get_value())
 
     def onMusicTimeChange(self, position):
         self.ignoreScaleChanges = True
--- a/light9/vidref/qt_test.py	Tue May 21 23:56:12 2019 +0000
+++ b/light9/vidref/qt_test.py	Wed May 22 00:08:22 2019 +0000
@@ -37,33 +37,33 @@
         bus.connect("sync-message::element", self.on_sync_message)
 
     def on_message(self, bus, message):
-        print "msg", bus, message
+        print("msg", bus, message)
         t = message.type
         if t == gst.MESSAGE_EOS:
             self.player.set_state(gst.STATE_NULL)
         elif t == gst.MESSAGE_ERROR:
             err, debug = message.parse_error()
-            print "Error: %s" % err, debug
+            print("Error: %s" % err, debug)
             self.player.set_state(gst.STATE_NULL)
 
     def on_sync_message(self, bus, message):
-        print "syncmsg", bus, message
+        print("syncmsg", bus, message)
         if message.structure is None:
             return
         message_name = message.structure.get_name()
         if message_name == "prepare-xwindow-id":
-            print "pxi"
+            print("pxi")
             win_id = self.windowId
             assert win_id
             imagesink = message.src
             imagesink.set_property("force-aspect-ratio", True)
-            print "set_xwindow_id"
+            print("set_xwindow_id")
             imagesink.set_xwindow_id(win_id)
-            print "dnoe msg"
+            print("dnoe msg")
 
     def startPrev(self):
         self.player.set_state(gst.STATE_PLAYING)
-        print "should be playing"
+        print("should be playing")
 
 
 class MainWin(QtGui.QMainWindow):
@@ -77,4 +77,4 @@
 
     @QtCore.pyqtSlot()
     def startLiveView(self):
-        print "slv"
+        print("slv")
--- a/light9/vidref/remotepivideo.py	Tue May 21 23:56:12 2019 +0000
+++ b/light9/vidref/remotepivideo.py	Wed May 22 00:08:22 2019 +0000
@@ -10,7 +10,7 @@
 from light9 import prof, showconfig
 from light9.namespaces import L9
 from PIL import Image
-from StringIO import StringIO
+from io import StringIO
 log = logging.getLogger('remotepi')
 
 
@@ -80,7 +80,7 @@
         pass
 
     def setLiveVideo(self, on):
-        print "setLiveVideo", on
+        print("setLiveVideo", on)
 
     def onFrame(self, jpg, frameTime):
         # We could pass frameTime here to try to compensate for lag,
--- a/light9/vidref/replay.py	Tue May 21 23:56:12 2019 +0000
+++ b/light9/vidref/replay.py	Wed May 22 00:08:22 2019 +0000
@@ -1,4 +1,4 @@
-from __future__ import division
+
 import os, gtk, shutil, logging, time
 from bisect import bisect_left
 from decimal import Decimal
--- a/light9/vidref/videorecorder.py	Tue May 21 23:56:12 2019 +0000
+++ b/light9/vidref/videorecorder.py	Wed May 22 00:08:22 2019 +0000
@@ -5,7 +5,7 @@
 from PIL import Image
 from threading import Thread
 from twisted.internet import defer
-from Queue import Queue, Empty
+from queue import Queue, Empty
 from light9.vidref.replay import framerate, songDir, takeDir, snapshotDir
 log = logging.getLogger()
 
@@ -56,7 +56,7 @@
             "videocrop left=160 top=180 right=120 bottom=80 ! "
             "queue name=vid" % framerate)
 
-        print cam
+        print(cam)
         self.pipeline = gst.parse_launch(cam)
 
         def makeElem(t, n=None):
--- a/light9/wavelength.py	Tue May 21 23:56:12 2019 +0000
+++ b/light9/wavelength.py	Wed May 22 00:08:22 2019 +0000
@@ -1,6 +1,6 @@
 #!/usr/bin/python
 
-from __future__ import division, nested_scopes
+
 import sys, wave
 
 def wavelength(filename):
@@ -15,4 +15,4 @@
 
 if __name__ == "__main__":
     for songfile in sys.argv[1:]:
-        print songfile, wavelength(songfile)
+        print(songfile, wavelength(songfile))
--- a/light9/wavepoints.py	Tue May 21 23:56:12 2019 +0000
+++ b/light9/wavepoints.py	Wed May 22 00:08:22 2019 +0000
@@ -1,21 +1,21 @@
-from __future__ import division
+
 import wave, audioop
 
 def simp(filename, seconds_per_average=0.001):
     """smaller seconds_per_average means fewer data points"""
     wavefile = wave.open(filename, 'rb')
-    print "# gnuplot data for %s, seconds_per_average=%s" % \
-        (filename, seconds_per_average)
-    print "# %d channels, samplewidth: %d, framerate: %s, frames: %d\n# Compression type: %s (%s)" % wavefile.getparams()
+    print("# gnuplot data for %s, seconds_per_average=%s" % \
+        (filename, seconds_per_average))
+    print("# %d channels, samplewidth: %d, framerate: %s, frames: %d\n# Compression type: %s (%s)" % wavefile.getparams())
 
     framerate = wavefile.getframerate() # frames / second
     frames_to_read = int(framerate * seconds_per_average)
-    print "# frames_to_read=%s" % frames_to_read
+    print("# frames_to_read=%s" % frames_to_read)
 
     time_and_max = []
     values = []
     count = 0
-    while 1:
+    while True:
         fragment = wavefile.readframes(frames_to_read)
         if not fragment:
             break