# HG changeset patch # User Drew Perttula # Date 2019-05-27 06:20:38 # Node ID f001d689b3e2a955e686d61589c512abd5c7ddf3 # Parent 5ebdb55e2ee6038dca3a6f234133b58f4ba44f4b more py3 and typing fixes Ignore-this: 3180bd966cac69de56b86ef6a308cad4 diff --git a/bcf2000.py b/bcf2000.py --- a/bcf2000.py +++ b/bcf2000.py @@ -1,9 +1,10 @@ #!/usr/bin/python -from __future__ import division + import math import twisted.internet.fdesc from twisted.internet import reactor from twisted.internet.task import LoopingCall +from typing import Dict class BCF2000(object): @@ -35,7 +36,7 @@ class BCF2000(object): once as C0D0. It should be autodetected""" self.devPath = dev self.dev = None - self.lastValue = {} # control name : value + self.lastValue: Dict[str, int] = {} # control name : value self.reopen() self.packet = "" loop = LoopingCall(self.poll) @@ -47,7 +48,7 @@ class BCF2000(object): except (IOError, AttributeError): return if len(bytes) == 0: - print "midi stall, reopen slider device" + print("midi stall, reopen slider device") self.reopen() return self.packet += bytes @@ -67,7 +68,7 @@ class BCF2000(object): self.lastValue[name] = value self.valueIn(name, value) else: - print "unknown control %s to %s" % (which, value) + print("unknown control %s to %s" % (which, value)) def reopen(self): if self.dev is not None: @@ -83,7 +84,7 @@ class BCF2000(object): def valueIn(self, name, value): """override this with your handler for when events come in from the hardware""" - print "slider %s to %s" % (name, value) + print("slider %s to %s" % (name, value)) if name == 'slider1': for x in range(2,8+1): v2 = int(64 + 64 * math.sin(x / 3 + value / 10)) @@ -101,7 +102,7 @@ class BCF2000(object): if self.lastValue.get(name) == value: return self.lastValue[name] = value - which = [k for k,v in self.control.items() if v == name] + which = [k for k,v in list(self.control.items()) if v == name] assert len(which) == 1, "unknown control name %r" % name if name.startswith('button-'): value = value * 127 diff --git a/bin/keyboardcomposer b/bin/keyboardcomposer --- a/bin/keyboardcomposer +++ b/bin/keyboardcomposer @@ -9,6 +9,7 @@ from twisted.internet import reactor, tk from twisted.web import resource from rdflib import URIRef, Literal import tkinter.tix as tk +from typing import Dict, Tuple, List from light9.Fadable import Fadable from light9.subclient import SubClient @@ -84,7 +85,7 @@ class SubmasterBox(tk.Frame): self.sub = sub self.session = session self.col, self.row = col, row - bg = self.graph.value(sub, L9.color, default='#000000') + bg = self.graph.value(sub, L9['color'], default='#000000') rgb = webcolors.hex_to_rgb(bg) hsv = colorsys.rgb_to_hsv(*[x / 255 for x in rgb]) darkBg = webcolors.rgb_to_hex( @@ -186,9 +187,9 @@ class KeyboardComposer(tk.Frame, SubClie self.graph = graph self.session = session - self.subbox = {} # sub uri : SubmasterBox - self.slider_table = {} # coords : SubmasterBox - self.rows = [] # this holds Tk Frames for each row + self.subbox: Dict[URIRef, SubmasterBox] = {} # sub uri : SubmasterBox + self.slider_table: Dict[Tuple[int, int], SubmasterBox] = {} # coords : SubmasterBox + self.rows: List[tk.Frame] = [] # this holds Tk Frames for each row self.current_row = 0 # should come from session graph @@ -270,7 +271,7 @@ class KeyboardComposer(tk.Frame, SubClie log.info("withgroups %s", withgroups) - self.effectEval = {} + self.effectEval: Dict[URIRef, light9.effect.effecteval.EffectEval] = {} imp.reload(light9.effect.effecteval) simpleOutputs = SimpleOutputs(self.graph) for group, order, sortLabel, effect in withgroups: diff --git a/bin/rdfdb b/bin/rdfdb --- a/bin/rdfdb +++ b/bin/rdfdb @@ -1,21 +1,23 @@ #!bin/python import run_local # noqa import os +from rdflib import URIRef from light9 import networking, showconfig import rdfdb.service rdfdb.service.main( dirUriMap={ - os.environ['LIGHT9_SHOW'].rstrip('/') + '/': showconfig.showUri() + '/' + os.environ['LIGHT9_SHOW'].encode('ascii').rstrip(b'/') + b'/': + URIRef(showconfig.showUri() + '/') }, prefixes={ - 'show': showconfig.showUri() + '/', - '': 'http://light9.bigasterisk.com/', - 'rdf': 'http://www.w3.org/1999/02/22-rdf-syntax-ns#', - 'rdfs': 'http://www.w3.org/2000/01/rdf-schema#', - 'xsd': 'http://www.w3.org/2001/XMLSchema#', - 'effect': 'http://light9.bigasterisk.com/effect/', - 'dev': 'http://light9.bigasterisk.com/device/', + 'show': URIRef(showconfig.showUri() + '/'), + '': URIRef('http://light9.bigasterisk.com/'), + 'rdf': URIRef('http://www.w3.org/1999/02/22-rdf-syntax-ns#'), + 'rdfs': URIRef('http://www.w3.org/2000/01/rdf-schema#'), + 'xsd': URIRef('http://www.w3.org/2001/XMLSchema#'), + 'effect': URIRef('http://light9.bigasterisk.com/effect/'), + 'dev': URIRef('http://light9.bigasterisk.com/device/'), }, port=networking.rdfdb.port, ) diff --git a/light9/collector/collector_client.py b/light9/collector/collector_client.py --- a/light9/collector/collector_client.py +++ b/light9/collector/collector_client.py @@ -11,7 +11,6 @@ log = logging.getLogger('coll_client') class TwistedZmqClient(object): - def __init__(self, service): zf = ZmqFactory() e = ZmqEndpoint('connect', 'tcp://%s:%s' % (service.host, service.port)) diff --git a/light9/effect/effecteval.py b/light9/effect/effecteval.py --- a/light9/effect/effecteval.py +++ b/light9/effect/effecteval.py @@ -1,4 +1,4 @@ -from rdflib import Literal +from rdflib import Literal, URIRef from light9.namespaces import L9, DEV from webcolors import rgb_to_hex, hex_to_rgb from colorsys import hsv_to_rgb @@ -7,6 +7,7 @@ from noise import pnoise1 import logging from light9.effect.settings import DeviceSettings from light9.effect.scale import scale +from typing import Dict, Tuple, Any import random random.seed(0) print("reload effecteval") @@ -76,7 +77,7 @@ class EffectEval(object): return DeviceSettings(self.graph, []), {'zero': True} report = {} - out = {} # (dev, attr): value + out: Dict[Tuple[URIRef, URIRef], Any] = {} # (dev, attr): value out.update( self.simpleOutputs.values( diff --git a/light9/effect/sequencer.py b/light9/effect/sequencer.py --- a/light9/effect/sequencer.py +++ b/light9/effect/sequencer.py @@ -11,12 +11,14 @@ from twisted.python.filepath import File import cyclone.sse import logging, bisect, time import traceback +from typing import Any, Callable, Dict, List, Tuple from light9.namespaces import L9, RDF from light9.vidref.musictime import MusicTime from light9.effect import effecteval from light9.effect.settings import DeviceSettings from light9.effect.simple_outputs import SimpleOutputs +from rdfdb.syncedgraph import SyncedGraph from greplin import scales import imp @@ -38,7 +40,7 @@ class Note(object): self.uri = uri self.effectEval = effectevalModule.EffectEval( graph, g.value(uri, L9['effectClass']), simpleOutputs) - self.baseEffectSettings = {} # {effectAttr: value} + self.baseEffectSettings: Dict[URIRef, Any] = {} # {effectAttr: value} for s in g.objects(uri, L9['setting']): settingValues = dict(g.predicate_objects(s)) ea = settingValues[L9['effectAttr']] @@ -48,13 +50,13 @@ class Note(object): return float(g.value(s, p).toPython()) originTime = floatVal(uri, L9['originTime']) - self.points = [] + self.points: List[Tuple[float, float]] = [] for curve in g.objects(uri, L9['curve']): self.points.extend( self.getCurvePoints(curve, L9['strength'], originTime)) self.points.sort() - def getCurvePoints(self, curve, attr, originTime): + def getCurvePoints(self, curve, attr, originTime) -> List[Tuple[float, float]]: points = [] po = list(self.graph.predicate_objects(curve)) if dict(po).get(L9['attr'], None) != attr: @@ -129,16 +131,17 @@ class CodeWatcher(object): class Sequencer(object): - def __init__(self, graph, sendToCollector, fps=40): + def __init__(self, graph: SyncedGraph, sendToCollector: Callable[[DeviceSettings], None], + fps=40): self.graph = graph self.fps = fps self.sendToCollector = sendToCollector self.music = MusicTime(period=.2, pollCurvecalc=False) - self.recentUpdateTimes = [] - self.lastStatLog = 0 + self.recentUpdateTimes: List[float] = [] + self.lastStatLog = 0.0 self._compileGraphCall = None - self.notes = {} # song: [notes] + self.notes: Dict[URIRef, List[Note]] = {} # song: [notes] self.simpleOutputs = SimpleOutputs(self.graph) self.graph.addHandler(self.compileGraph) self.updateLoop() @@ -166,7 +169,7 @@ class Sequencer(object): Note(self.graph, note, effecteval, self.simpleOutputs)) log.info(' compile %s took %.2f ms', song, 1000 * (time.time() - t1)) - def updateLoop(self): + def updateLoop(self) -> None: # print "updateLoop" now = time.time() self.recentUpdateTimes = self.recentUpdateTimes[-40:] + [now] @@ -232,7 +235,7 @@ class Updates(cyclone.sse.SSEHandler): def __init__(self, application, request, **kwargs): cyclone.sse.SSEHandler.__init__(self, application, request, **kwargs) - self.state = {} + self.state: Dict = {} dispatcher.connect(self.updateState, 'state') self.numConnected = 0 diff --git a/light9/effect/settings.py b/light9/effect/settings.py --- a/light9/effect/settings.py +++ b/light9/effect/settings.py @@ -10,7 +10,7 @@ from light9.namespaces import RDF, L9 import logging log = logging.getLogger('settings') from light9.collector.device import resolve - +from typing import Sequence, Dict, Union, List def parseHex(h): if h[0] != '#': raise ValueError(h) @@ -21,9 +21,10 @@ def parseHexNorm(h): return [x / 255 for x in parseHex(h)] -def toHex(rgbFloat): - return '#%02x%02x%02x' % tuple( - max(0, min(255, int(v * 255))) for v in rgbFloat) +def toHex(rgbFloat: Sequence[float]) -> str: + assert len(rgbFloat) == 3 + scaled = (max(0, min(255, int(v * 255))) for v in rgbFloat) + return '#%02x%02x%02x' % tuple(scaled) # type: ignore def getVal(graph, subj): @@ -42,7 +43,7 @@ class _Settings(object): def __init__(self, graph, settingsList): self.graph = graph # for looking up all possible attrs - self._compiled = {} # dev: { attr: val }; val is number or colorhex + self._compiled: Dict[URIRef, Dict[URIRef, Union[float, str]]] = {} # dev: { attr: val }; val is number or colorhex for row in settingsList: self._compiled.setdefault(row[0], {})[row[1]] = row[2] # self._compiled may not be final yet- see _fromCompiled @@ -68,7 +69,7 @@ class _Settings(object): @classmethod def fromVector(cls, graph, vector, deviceAttrFilter=None): - compiled = {} + compiled: Dict[URIRef, Dict[URIRef, Union[float, str]]] = {} i = 0 for (d, a) in cls(graph, [])._vectorKeys(deviceAttrFilter): if a == L9['color']: @@ -186,7 +187,7 @@ class _Settings(object): return list(self._compiled.keys()) def toVector(self, deviceAttrFilter=None): - out = [] + out: List[float] = [] for dev, attr in self._vectorKeys(deviceAttrFilter): v = self.getValue(dev, attr) if attr == L9['color']: diff --git a/light9/effect/simple_outputs.py b/light9/effect/simple_outputs.py --- a/light9/effect/simple_outputs.py +++ b/light9/effect/simple_outputs.py @@ -1,7 +1,8 @@ import traceback from light9.namespaces import L9, RDF from light9.effect.scale import scale - +from typing import Dict, List, Tuple, Any +from rdflib import URIRef class SimpleOutputs(object): @@ -9,7 +10,7 @@ class SimpleOutputs(object): self.graph = graph # effect : [(dev, attr, value, isScaled)] - self.effectOutputs = {} + self.effectOutputs: Dict[URIRef, List[Tuple[URIRef, URIRef, Any, bool]]] = {} self.graph.addHandler(self.updateEffectsFromGraph) diff --git a/light9/namespaces.py b/light9/namespaces.py --- a/light9/namespaces.py +++ b/light9/namespaces.py @@ -1,4 +1,5 @@ from rdflib import Namespace, RDF, RDFS # noqa +from typing import Dict # Namespace was showing up in profiles @@ -6,7 +7,7 @@ class FastNs(object): def __init__(self, base): self.ns = Namespace(base) - self.cache = {} + self.cache: Dict[str, Namespace] = {} def __getitem__(self, term): if term not in self.cache: diff --git a/light9/networking.py b/light9/networking.py --- a/light9/networking.py +++ b/light9/networking.py @@ -1,5 +1,4 @@ from urllib.parse import urlparse -from urllib.parse import splitport from .showconfig import getGraph, showUri from .namespaces import L9 @@ -20,15 +19,11 @@ class ServiceAddress(object): @property def port(self): - _, netloc, _, _, _, _ = urlparse(self._url()) - host, port = splitport(netloc) - return int(port) + return urlparse(self._url()).port @property def host(self): - _, netloc, _, _, _, _ = urlparse(self._url()) - host, port = splitport(netloc) - return host + return urlparse(self._url()).hostname @property def url(self): diff --git a/light9/prof.py b/light9/prof.py --- a/light9/prof.py +++ b/light9/prof.py @@ -1,4 +1,5 @@ import sys, traceback, time, logging +from typing import Any, Dict log = logging.getLogger() @@ -30,7 +31,7 @@ def watchPoint(filename, lineno, event=" Switch to 'line' to match lines inside functions. Execution speed will be much slower.""" - seenTraces = {} # trace contents : count + seenTraces: Dict[Any, int] = {} # trace contents : count def trace(frame, ev, arg): if ev == event: diff --git a/light9/showconfig.py b/light9/showconfig.py --- a/light9/showconfig.py +++ b/light9/showconfig.py @@ -2,14 +2,15 @@ import logging, warnings from twisted.python.filepath import FilePath from os import path, getenv from rdflib import Graph -from rdflib import URIRef +from rdflib import URIRef, Literal from .namespaces import L9 +from typing import List, cast log = logging.getLogger('showconfig') _config = None # graph -def getGraph(): +def getGraph() -> Graph: warnings.warn( "code that's using showconfig.getGraph should be " "converted to use the sync graph", @@ -27,26 +28,26 @@ def getGraph(): return _config -def root(): +def root() -> bytes: r = getenv("LIGHT9_SHOW") if r is None: raise OSError( "LIGHT9_SHOW env variable has not been set to the show root") - return r + return r.encode('ascii') _showUri = None -def showUri(): +def showUri() -> URIRef: """Return the show URI associated with $LIGHT9_SHOW.""" global _showUri if _showUri is None: - _showUri = URIRef(open(path.join(root(), 'URI')).read().strip()) + _showUri = URIRef(open(path.join(root(), b'URI')).read().strip()) return _showUri -def songOnDisk(song): +def songOnDisk(song: URIRef) -> bytes: """given a song URI, where's the on-disk file that mpd would read?""" graph = getGraph() root = graph.value(showUri(), L9['musicRoot']) @@ -57,20 +58,22 @@ def songOnDisk(song): if not name: raise ValueError("Song %r has no :songFilename" % song) - return path.abspath(path.join(root, name)) + return path.abspath(path.join( + cast(Literal, root).toPython(), + cast(Literal, name).toPython())) -def songFilenameFromURI(uri): +def songFilenameFromURI(uri: URIRef) -> bytes: """ 'http://light9.bigasterisk.com/show/dance2007/song8' -> 'song8' everything that uses this should be deprecated for real URIs everywhere""" assert isinstance(uri, URIRef) - return uri.split('/')[-1] + return str(uri).split('/')[-1].encode('ascii') -def getSongsFromShow(graph, show): +def getSongsFromShow(graph: Graph, show: URIRef) -> List[URIRef]: playList = graph.value(show, L9['playList']) if not playList: raise ValueError("%r has no l9:playList" % show) @@ -82,12 +85,12 @@ def getSongsFromShow(graph, show): def curvesDir(): - return path.join(root(), "curves") + return path.join(root(), b"curves") def subFile(subname): - return path.join(root(), "subs", subname) + return path.join(root(), b"subs", subname) def subsDir(): - return path.join(root(), 'subs') + return path.join(root(), b'subs') diff --git a/light9/tkdnd.py b/light9/tkdnd.py --- a/light9/tkdnd.py +++ b/light9/tkdnd.py @@ -1,6 +1,6 @@ from glob import glob from os.path import join, basename - +from typing import Dict, Any class TkdndEvent(object): """ @@ -45,7 +45,7 @@ class Hover(object): def __init__(self, widget, style): self.widget, self.style = widget, style - self.oldStyle = {} + self.oldStyle: Dict[Any, Any] = {} def set(self, ev): for k, v in list(self.style.items()): diff --git a/light9/uihelpers.py b/light9/uihelpers.py --- a/light9/uihelpers.py +++ b/light9/uihelpers.py @@ -6,6 +6,7 @@ from rdflib import Literal from tkinter.tix import Button, Toplevel, Tk, IntVar, Entry, DoubleVar import tkinter from light9.namespaces import L9 +from typing import Dict log = logging.getLogger("toplevel") @@ -44,7 +45,7 @@ def toplevelat(name, existingtoplevel=No lastSaved = [None] setOnce = [False] - graphSetTime = [0] + graphSetTime = [0.0] def setPosFromGraphOnce(): """ @@ -53,7 +54,7 @@ def toplevelat(name, existingtoplevel=No """ if setOnce[0]: return - geo = graph.value(session, L9.windowGeometry) + geo = graph.value(session, L9['windowGeometry']) log.debug("setPosFromGraphOnce %s", geo) setOnce[0] = True @@ -76,7 +77,7 @@ def toplevelat(name, existingtoplevel=No return lastSaved[0] = geo log.debug("saving position %s", geo) - graph.patchObject(session, session, L9.windowGeometry, Literal(geo)) + graph.patchObject(session, session, L9['windowGeometry'], Literal(geo)) if graph is not None and session is not None: graph.addHandler(setPosFromGraphOnce) @@ -142,7 +143,7 @@ def colorlabel(label): low = (80, 80, 180) high = (255, 55, 0o50) out = [int(l + lev * (h - l)) for h, l in zip(high, low)] - col = "#%02X%02X%02X" % tuple(out) + col = "#%02X%02X%02X" % tuple(out) # type: ignore label.config(bg=col) @@ -150,7 +151,7 @@ def colorlabel(label): def colorfade(low, high, percent): '''not foolproof. make sure 0 < percent < 1''' out = [int(l + percent * (h - l)) for h, l in zip(high, low)] - col = "#%02X%02X%02X" % tuple(out) + col = "#%02X%02X%02X" % tuple(out) # type: ignore return col @@ -216,8 +217,8 @@ class FancyDoubleVar(DoubleVar): def __init__(self, master=None): DoubleVar.__init__(self, master) - self.callbacklist = {} # cbname : mode - self.namedtraces = {} # name : cbname + self.callbacklist: Dict[str, str] = {} # cbname : mode + self.namedtraces: Dict[str, str] = {} # name : cbname def trace_variable(self, mode, callback): """Define a trace callback for the variable. diff --git a/light9/vidref/musictime.py b/light9/vidref/musictime.py --- a/light9/vidref/musictime.py +++ b/light9/vidref/musictime.py @@ -2,6 +2,7 @@ import time, json, logging from light9 import networking from twisted.internet import reactor from cyclone.httpclient import fetch +from typing import Dict log = logging.getLogger() @@ -28,7 +29,7 @@ class MusicTime(object): self.hoverPeriod = .05 self.onChange = onChange - self.position = {} + self.position: Dict[str, float] = {} # driven by our pollCurvecalcTime and also by Gui.incomingTime self.lastHoverTime = None # None means "no recent value" self.pollMusicTime() @@ -125,6 +126,6 @@ class MusicTime(object): fetch( method=b'POST', url=networking.musicPlayer.path('time'), - body=json.dumps({"t": t}), + postdata=json.dumps({"t": t}).encode('utf8'), headers={b"content-type": [b"application/json"]}, ) diff --git a/light9/web/websocket.js b/light9/web/websocket.js --- a/light9/web/websocket.js +++ b/light9/web/websocket.js @@ -1,5 +1,6 @@ /* - url is now relative to the window location + url is now relative to the window location. Note that nginx may drop + the connection after 60sec of inactivity. */ function reconnectingWebSocket(url, onMessage) { var pong = 0; diff --git a/requirements.txt b/requirements.txt --- a/requirements.txt +++ b/requirements.txt @@ -31,6 +31,7 @@ ipdb==0.10.2 ipython==5.3.0 mypy==0.701 flake8 +typing_extensions git+http://github.com/drewp/scales.git@448d59fb491b7631877528e7695a93553bfaaa93#egg=scales git+http://github.com/11craft/louie.git@f18bb71010c114eca9c6b88c96453340e3b39454#egg=louie diff --git a/tasks.py b/tasks.py --- a/tasks.py +++ b/tasks.py @@ -30,18 +30,21 @@ def pkg_sources(): @task def mypy(ctx): + print('\n\n') def run(sources): ss = ' '.join(sources) - ctx.run(f'MYPYPATH=stubs env/bin/mypy --check-untyped-defs {ss}', + ctx.run(f'MYPYPATH=stubs:/my/proj/rdfdb env/bin/mypy --check-untyped-defs {ss}', pty=True, warn=True) sources = ' '.join(bin_sources + pkg_sources()) ctx.run(f'env/bin/flake8 --ignore=E115,E123,E124,E126,E225,E231,E261,E262,E265,E301,E302,E303,E305,E306,E401,E402,E501,E701,E731,W291,W293,W391,W504 {sources}', warn=True) sources = ' '.join(pkg_sources()) - for src in bin_sources: - print(f"mypy {src}") - run([src])# + pkg_sources()) + run(['bin/rdfdb']) + run(['bin/keyboardcomposer']) + #for src in bin_sources: + # print(f"mypy {src}") + # run([src])# + pkg_sources()) @task def reformat(ctx): ctx.run("env/bin/yapf --verbose --parallel --in-place --style google light9/**/*.py `file --no-pad bin/* | grep 'Python script' | perl -lpe 's/:.*//'`")