Mercurial > code > home > repos > light9
changeset 1108:4b542d321c8f
effectnode runs multiple lines of code in dependency order
Ignore-this: 45e7a4f332c6b1e15eefb7bd8a3ed657
author | Drew Perttula <drewp@bigasterisk.com> |
---|---|
date | Tue, 10 Jun 2014 09:29:05 +0000 |
parents | 512381de45bd |
children | 53528b34097f |
files | bin/effecteval light9/effecteval/effect.py light9/effecteval/effectloop.py light9/effecteval/test_effect.py pydeps |
diffstat | 5 files changed, 60 insertions(+), 22 deletions(-) [+] |
line wrap: on
line diff
--- a/bin/effecteval Tue Jun 10 08:48:34 2014 +0000 +++ b/bin/effecteval Tue Jun 10 09:29:05 2014 +0000 @@ -104,8 +104,12 @@ def updateClient(self): # todo: if client has dropped, abort and don't get any more # graph updates - self.sendMessage({'codeLines': - list(self.graph.objects(self.uri, L9['code']))}) + + # EffectNode knows how to put them in order. Somehow this is + # not triggering an update when the order changes. + en = EffectNode(self.graph, self.uri) + codeLines = [c.code for c in en.codes] + self.sendMessage({'codeLines': codeLines}) def connectionLost(self, reason): log.info("websocket closed")
--- a/light9/effecteval/effect.py Tue Jun 10 08:48:34 2014 +0000 +++ b/light9/effecteval/effect.py Tue Jun 10 09:29:05 2014 +0000 @@ -1,4 +1,5 @@ import re, logging +import toposort from rdflib import URIRef from light9.namespaces import L9, RDF from light9.curvecalc.curve import CurveResource @@ -22,8 +23,9 @@ def __init__(self, graph, code): self.graph, self.code = graph, code - self.outName, self.expr, self.resources = self._asPython() + self.outName, self.inExpr, self.expr, self.resources = self._asPython() self.pyResources = self._resourcesAsPython(self.resources) + self.possibleVars = self.findVars(self.inExpr) def _asPython(self): """ @@ -50,8 +52,15 @@ return 'curve(%s, t)' % v return v outExpr = re.sub(r'<(http\S*?)>', repl, expr) - return lname, outExpr, resources + return lname, expr, outExpr, resources + def findVars(self, expr): + """may return some more strings than just the vars""" + withoutUris = re.sub(r'<(http\S*?)>', 'None', expr) + tokens = set(re.findall(r'\b([a-zA-Z_]\w*)\b', withoutUris)) + tokens.discard('None') + return tokens + def _uriIsCurve(self, uri): # this result could vary with graph changes (rare) return self.graph.contains((uri, RDF.type, L9['Curve'])) @@ -92,8 +101,21 @@ self.codes = [CodeLine(self.graph, s) for s in codeStrs] + self.sortCodes() + self.otherFuncs = Effects.configExprGlobals() - + + def sortCodes(self): + """put self.codes in a working evaluation order""" + codeFromOutput = dict((c.outName, c) for c in self.codes) + deps = {} + for c in self.codes: + outName = c.outName + inNames = c.possibleVars.intersection(codeFromOutput.keys()) + inNames.discard(outName) + deps[outName] = inNames + self.codes = [codeFromOutput[n] for n in toposort.toposort_flatten(deps)] + def eval(self, songTime): ns = {'t': songTime} ns.update(self.otherFuncs) @@ -101,13 +123,13 @@ ns.update(dict( curve=lambda c, t: c.eval(t), )) - # loop over lines in order, merging in outputs - # merge in named outputs from previous lines for c in self.codes: codeNs = ns.copy() codeNs.update(c.pyResources) - if c.outName == 'out': - out = eval(c.expr, codeNs) - return out + lineOut = eval(c.expr, codeNs) + ns[c.outName] = lineOut + if 'out' not in ns: + log.error("ran code for %s, didn't make an 'out' value", self.uri) + return ns['out']
--- a/light9/effecteval/effectloop.py Tue Jun 10 08:48:34 2014 +0000 +++ b/light9/effecteval/effectloop.py Tue Jun 10 09:29:05 2014 +0000 @@ -78,9 +78,6 @@ now = time.time() if now > self.lastErrorLog + 5: log.error("effect %s: %s" % (e.uri, exc)) - log.error(" expr: %s", e.code.expr) - log.error(" resources: %r", - getattr(e, 'resourceMap', '?')) self.lastErrorLog = now out = Submaster.sub_maxes(*outSubs)
--- a/light9/effecteval/test_effect.py Tue Jun 10 08:48:34 2014 +0000 +++ b/light9/effecteval/test_effect.py Tue Jun 10 09:29:05 2014 +0000 @@ -10,43 +10,56 @@ return 'curve' in uri @mock.patch('light9.effecteval.effect.CodeLine._uriIsCurve', new=isCurve) +@mock.patch('light9.effecteval.effect.CodeLine._resourcesAsPython', + new=lambda self, r: self.expr) class TestAsPython(unittest.TestCase): def test_gets_lname(self): ec = CodeLine(graph=None, code='x = y+1') - self.assertEqual('x', ec._asPython()[0]) + self.assertEqual('x', ec.outName) def test_gets_simple_code(self): ec = CodeLine(graph=None, code='x = y+1') - self.assertEqual('y+1', ec._asPython()[1]) - self.assertEqual({}, ec._asPython()[2]) + self.assertEqual('y+1', ec._asPython()[2]) + self.assertEqual({}, ec._asPython()[3]) def test_converts_uri_to_var(self): ec = CodeLine(graph=None, code='x = <http://example.com/>') - _, expr, uris = ec._asPython() + _, inExpr, expr, uris = ec._asPython() self.assertEqual('_res0', expr) self.assertEqual({'_res0': URIRef('http://example.com/')}, uris) def test_converts_multiple_uris(self): ec = CodeLine(graph=None, code='x = <http://example.com/> + <http://other>') - _, expr, uris = ec._asPython() + _, inExpr, expr, uris = ec._asPython() self.assertEqual('_res0 + _res1', expr) self.assertEqual({'_res0': URIRef('http://example.com/'), '_res1': URIRef('http://other')}, uris) def test_doesnt_fall_for_brackets(self): ec = CodeLine(graph=None, code='x = 1<2>3< h') - _, expr, uris = ec._asPython() + _, inExpr, expr, uris = ec._asPython() self.assertEqual('1<2>3< h', expr) self.assertEqual({}, uris) def test_curve_uri_expands_to_curve_eval_func(self): ec = CodeLine(graph=None, code='x = <http://example/curve1>') - _, expr, uris = ec._asPython() + _, inExpr, expr, uris = ec._asPython() self.assertEqual('curve(_res0, t)', expr) self.assertEqual({'_res0': URIRef('http://example/curve1')}, uris) def test_curve_doesnt_double_wrap(self): ec = CodeLine(graph=None, code='x = curve(<http://example/curve1>, t+.01)') - _, expr, uris = ec._asPython() + _, inExpr, expr, uris = ec._asPython() self.assertEqual('curve(_res0, t+.01)', expr) self.assertEqual({'_res0': URIRef('http://example/curve1')}, uris) + +@mock.patch('light9.effecteval.effect.CodeLine._uriIsCurve', new=isCurve) +@mock.patch('light9.effecteval.effect.CodeLine._resourcesAsPython', + new=lambda self, r: self.expr) +class TestPossibleVars(unittest.TestCase): + def test1(self): + self.assertEqual(set([]), CodeLine(None, 'a1 = 1').possibleVars) + def test2(self): + self.assertEqual(set(['a2']), CodeLine(None, 'a1 = a2').possibleVars) + def test3(self): + self.assertEqual(set(['a2', 'a3']), CodeLine(None, 'a1 = a2 + a3').possibleVars)