Commits

Ali Afshar committed 44c9e6b

Better output using py.io

Comments (0)

Files changed (5)

     """Attempt to call an undefined reference
     """
 
+class CircularDependency(RuntimeError):
+    """A circular dependency"""
+
 from blinker import signal
 
 from bu.preproc import preproc_action
-from bu.errors import TargetNotFound
+from bu.errors import TargetNotFound, CircularDependency
+
+action_started = signal('action-started')
+action_completed = signal('action-completed')
+target_started = signal('target-started')
+target_completed = signal('target-completed')
+build_started = signal('build-started')
+build_completed = signal('build-completed')
 
 
 explicit_executors = {}
         if target is None:
             raise ValueError('Target must not be None')
         self.queue = Queue()
-        self.action_started = signal('queue-action-started')
-        self.action_completed = signal('queue-action-completed')
         self.add_target(target)
 
     def add_target(self, target):
     def __call__(self):
         results = []
         for node in self:
-            self.action_started.send(self, action=node.action)
+            action_started.send(self, action=node.action)
             result = node()
-            self.action_completed.send(self, action=node.action, result=result)
+            action_completed.send(self, action=node.action, result=result)
             results.append(result)
         return results
 
         self.enqueued = set()
         self.nodes = set()
         self.queue = Queue()
-        self.action_started = signal('action-started')
-        self.action_completed = signal('action-completed')
-        self.target_started = signal('target-started')
-        self.target_completed = signal('target-completed')
+        self.results = []
         self.generate_graph(target)
 
-    def generate_graph(self, target):
+    def generate_graph(self, target, parents=None):
         self.nodes.add(target)
+        if parents is None:
+            parents = set()
+        if target in parents:
+            raise CircularDependency('Target %r is already in the dependency '
+                                     'tree, causing a circular dependency' %
+                                     target)
+        parents.add(target)
         for child in target.deps(self.script):
-            self.generate_graph(child)
+            self.generate_graph(child, parents=parents.copy())
 
     @property
     def available(self):
                 node.deps(self.script) <= self.completed):
                 yield node
 
-    def execute(self, workers=14):
+    def execute(self, workers=4):
+        build_started.send(self, script=self.script, target=self.target)
         self.update_queue()
         for i in range(workers):
             t = Thread(target=self._work)
             self.queue.put(item)
 
     def join(self):
+        """Wait until execution has completed.
+
+        You only need to call this if you are not running some mainloopish
+        thing.
+        """
         while len(self.nodes) > len(self.completed):
             time.sleep(0.1)
+        build_completed.send(self, script=self.script, target=self.target,
+                             results=self.results)
         time.sleep(0.1)
 
     def _on_task_completed(self, task, results):
         self.completed.add(task)
-        self.target_completed.send(self, target=task, results=results)
+        self.results.append(results)
+        target_completed.send(self, target=task, results=results)
         self.update_queue()
 
-    def _on_action_started(self, sender, action):
-        self.action_started.send(self, action=action)
-
-    def _on_action_completed(self, sender, action, result):
-        self.action_completed.send(self, action=action, result=result)
-
     def __iter__(self):
         return iter(self.nodes)
 
         while failures < 5 and len(self.nodes) > len(self.completed):
             try:
                 node = self.queue.get_nowait()
-                self.target_started.send(self, target=node)
+                target_started.send(self, target=node)
                 queue = ExecutionQueue(node)
-                queue.action_started.connect(self._on_action_started)
-                queue.action_completed.connect(self._on_action_completed)
                 results = queue()
                 self._on_task_completed(node, results)
             except Empty:
 too deps=roo:
     echo 1
 """)
-    def callback(sender, target):
-        print 'starting target', target
-    def callback2(sender, target, results):
-        print 'finished target', results
     s = ScriptExecutionGraph(c, 'noo')
-    s.target_started.connect(callback)
-    s.target_completed.connect(callback2)
+    from bu.output import ConsoleOutput
+    c = ConsoleOutput()
     s.execute()
+    s.join()
 
 
 
 from bu.parsing import Parser
 from bu.execute import ScriptExecutionGraph
-from bu.output import info, log_action, log_reply, error
+from bu.output import info, error, ConsoleOutput
 from bu.errors import BuError
 
 
     :param script: The script instance to execute
     :param target: The target to execute
     """
-    print_version()
-    info('bu', '[%s] @%s build starts' % (script.path, target))
     executor = ScriptExecutionGraph(script, target)
-    def _log_action(sender, action):
-        log_action(action)
-    executor.action_started.connect(_log_action)
-    def _log_reply(sender, action, result):
-        log_reply(*result)
-    executor.action_completed.connect(_log_reply)
+    output = ConsoleOutput()
     executor.execute(4)
     executor.join()
-    info('bu', '@%s build ends' % target)
 
 
 def print_targets(script):
 """
 
 import logging
+from py.io import TerminalWriter
 
 from bu.preproc import preproc_action
+from bu.execute import (build_started, build_completed, action_started,
+    action_completed, target_started, target_completed)
+
+
+
 
 
 logging.basicConfig(level=logging.DEBUG,
     format='%(name)s: %(message)s')
 
 
+
+class Output(object):
+
+    def connect(self):
+        build_started.connect(self.on_build_started)
+        build_completed.connect(self.on_build_completed)
+        target_started.connect(self.on_target_started)
+        target_completed.connect(self.on_target_completed)
+        action_started.connect(self.on_action_started)
+        action_completed.connect(self.on_action_completed)
+
+    def on_action_started(self, sender, action):
+        pass
+
+    def on_action_completed(self, sender, action, result):
+        pass
+
+    def on_target_started(self, sender, target):
+        pass
+
+    def on_target_completed(self, sender, target, results):
+        pass
+
+    def on_build_started(self, sender, script, target):
+        pass
+
+    def on_build_completed(self, sender, script, target):
+        pass
+
+
+class TextOutput(Output):
+
+    def h1(self, text):
+        raise NotImplementedError
+
+    def h2(self, text):
+        raise NotImplementedError
+
+    def h3(self, text):
+        raise NotImplementedError
+
+    def line(self, text):
+        raise NotImplementedError
+
+    def sep(self, sepchar):
+        raise NotImplementedError
+
+    def lines(self, lines):
+        for i, line in enumerate(lines):
+            self.line(' '.join([str(i + 1).rjust(8), line]))
+
+    def on_action_started(self, sender, action):
+        self.h3('%s %s' % (action.name, _flatten_options(action.options)))
+        namespace, content = preproc_action(action)
+        self.lines(content.splitlines())
+
+    def on_action_completed(self, sender, action, result):
+        data, code = result
+        self.code(code)
+        self.lines(data.splitlines())
+
+    def on_target_started(self, sender, target):
+        self.h2('%s [' % target.name)
+
+    def on_target_completed(self, sender, target, results):
+        self.h2('] %s' % target.name)
+        self.line('')
+
+    def on_build_started(self, sender, script, target):
+        self.h1('[bu 0.1] Starts (%s:%s)' % (script.path, target.name))
+        self.line('')
+
+    def on_build_completed(self, sender, script, target, results):
+        self.h1('[bu 0.1] Ends (%s:%s)' % (script.path, target.name))
+
+
+class ConsoleOutput(TextOutput):
+
+    def __init__(self):
+        self.connect()
+        self.term = TerminalWriter()
+
+    def line(self, text):
+        self.term.line(text)
+
+    def h1(self, text):
+        self.term.sep('=', text, White=True, black=True, bold=True)
+
+    def h2(self, text):
+        self.term.line(text, cyan=True, light=False)
+
+    def h3(self, text):
+        self.term.line(('!%s' % text), bold=True)
+
+    def code(self, code):
+        if code == 0:
+            kw = {'green':True}
+        else:
+            kw = {'red': True}
+        self.term.line(('> %s' % code), bold=True, **kw)
+
+
+
 def info(prefix, content):
     log = logging.getLogger(prefix)
     log.info(content)
     log.error(str(e))
 
 
-def log_lines(lines, level=info):
-    for i, line in enumerate(lines):
-        level(str(i + 1).rjust(8), line)
 
 
 def _flatten_options(options):
     return '{%s}' % ' '.join('%s=%s' % i for i in options.items())
 
 
-def log_action(action):
-    info(action.name, _flatten_options(action.options))
-    namespace, content = preproc_action(action)
-    log_lines(content.splitlines())
-
-
-def log_reply(data, code):
-    info('>>', '%s' % code)
-    lines = data.strip().splitlines()
-    log_lines(lines)
-
 test PYTHONPATH={{cwd}}:
     py.test
 
+mypy:
+    !py
+    print banana
 
+