Commits

Ali Afshar committed fdcaa49

Fixed namespacing issues, to a degree.

  • Participants
  • Parent commits 71f2e3b

Comments (0)

Files changed (14)

 
 import execnet
 from bu.output import print_title, print_action, print_reply
-from bu.preprocessing import pre_process_action, global_namespace
+from bu.preprocessing import pre_process_action, global_namespace, \
+    pre_process_options
 
 
 
 class ExecutionNode(object):
     def __init__(self, action, namespace):
         self.action = action
-        self.namespace = global_namespace(namespace)
+        self.namespace = pre_process_options(global_namespace(namespace))
         self.content = pre_process_action(action, self.namespace)
-    
+
     def __call__(self):
         executor = explicit_executors.get(self.action.name, execute_execnet)
         return executor(self.action, self.content, self.namespace)
     def add_circular(self, target):
         pass
 
-    def add_target(self, target, override_parent=None):
-        for action in target.children:
-            self.add_action(action, override_parent)
+    def add_target(self, target):
+        for action in target.actions:
+            self.add_action(action)
 
     def add_reference(self, action):
         target = self.script.targets[action.name]
         if target in action.ancestors:
             self.add_circular(target)
         else:
-            self.add_target(target, override_parent=action)
+            self.add_target(target)
 
-    def add_action(self, action, override_parent=None):
-        if action.is_reference:
-            self.add_reference(action)
-        else:
-            if override_parent is None:
-                namespace = action.namespace
-            else:
-                namespace = action.alien_namespace(override_parent)
-            self.queue.append(ExecutionNode(action, namespace))
+    def add_action(self, action):
+        namespace = action.namespace
+        self.queue.append(ExecutionNode(action, namespace))
 
     def execute(self):
         for exec_node in self.queue:

bu/lib/bitbucket.bu

+#! /usr/bin/bu
+# Bitbucket integration
+#
+# Variables:
+#   - bitbucket_project
+#
+!use utils.bu
+!opt bb='http://bitbucket.org'
+
+_bb.base:
+    @web.browse url={{bb}}/{{bb_project}}/{{bb_action}}
+
+bb.issues:
+    @_bb.base bb_action=issues
+
+bb.fork:
+    @_bb.base bb_action=fork
+

bu/lib/distutils.bu

 #! /usr/bin/bu
 # Distutils command support
 
-_distutils:
-    python setup.py $command
+_py.distutils:
+    python setup.py {{ command }}
 
-build:
-    @distutils command=build
+py.build:
+    @_py.distutils command=build
 
-install:
-    @distutils command=install
+py.install:
+    @_py.distutils command=install
 
-upload:
-    @distutils command='sdist upload'
+py.upload:
+    @_py.distutils command='sdist upload'
 
+#! /usr/bin/bu
+# Sphinx support
+
+doc.build doc_builddir=build/docs:
+    sphinx-build docs/ {{ doc_builddir }}
+
+doc.upload:
+    python setup.py upload_docs
+
+doc:
+    @doc.build
+    @doc.upload
+
+
+#! /usr/bin/bu
+# Browse a url in a browser
+#
+# Required variables:
+#     - url
+
+_web.browse:
+    !py
+    import webbrowser
+    print 'Opening %s' % url
+    webbrowser.open_new(url);
+
 
 from bu.parsing import Parser
 from bu.execute import ExecutionQueue
-from bu.output import print_title, print_semi_title
+from bu.output import print_title, info
 
 
 class BuildFileNotFound(RuntimeError):
 
     def open(self, path):
         p = Parser()
+        self.path = path
         self.script = p.parse_path(path)
 
+    def version(self):
+        info('bu', '0.1')
+
     def execute(self, name):
-        print_title('build starts [%s]' % name)
+        self.version()
+        info('bu', '[%s] build starts' % name)
         q = ExecutionQueue(self.script, name)
         q.execute()
-        print_title('build ends [%s]' % name)
+        info('bu', '@%s build ends' % name)
 
     def list_targets(self):
-        print_title('listing actions')
-        for target in self.script.targets:
+        info('Actions', '')
+        for target in sorted(self.script.targets):
             if not target.startswith('_'):
-                print_semi_title(target)
+                print_title(target)
         print_title('hidden actions')
-        for target in self.script.targets:
+        for target in sorted(self.script.targets):
             if target.startswith('_'):
-                print_semi_title(target)
+                print_title(target)
 
 
 def main():
         help='The target to build'
     )
     parser.add_argument(
-        '-list', '--list',
+        '-l', '--list',
         action='store_true',
         help='List the targets without executing'
     )
+    parser.add_argument(
+        '-v', '--version',
+        action='store_true',
+        help='Print the version string and exit'
+    )
     args = parser.parse_args()
     app = Application()
     app.open(args.file)
     if args.list:
         app.list_targets()
+    elif args.version:
+        app.version()
     elif args.target:
         app.execute(args.target)
     else:
     """A node with a name, children, and options
     """
 
-    def __init__(self, name, parent, options):
+    def __init__(self, name, parents, options):
         self.name = name
-        self.parent = parent
+        self.parents = parents
         self.children = []
         self.options = options
+        self.overrides = {}
 
     def __repr__(self):
         return '<%s %s (%s) %s>' % (self.__class__.__name__, self.name,
                                  len(self.children), self.options)
 
+    def clone(self):
+        clone = self.__class__(self.name, self.parents, self.options)
+        clone.children = list(self.children)
+        return clone
+
+    def child(self, child_type, name, options, *args, **kw):
+        child = child_type(name, [self], options, *args, **kw)
+        self.children.append(child)
+        return child
+
     @property
     def ancestors(self):
-        parent = self
-        while parent:
+        for parent in self.parents:
             yield parent
-            parent = parent.parent
+            for ancestor in parent.ancestors:
+                yield ancestor
+
+    @property
+    def local_namespace(self):
+        return namespace(self.overrides, self.options)
 
     @property
     def namespace(self):
-        return namespace(*(a.options for a in self.ancestors))
+        return namespace(*[self.local_namespace] + [a.local_namespace for a in self.ancestors])
 
+    @property
+    def actions(self):
+        for child in self.children:
+            return child.actions
 
 
 class Action(NamedNode):
     """An action"""
 
-    is_reference = False
     command_indent = None
 
     @property
         return '\n'.join(self.children + [''])
 
     def alien_namespace(self, override_parent):
-        parent_opts = self.parent.options.copy()
+        parent_opts = self.parent.namespace
         parent_opts.update(override_parent.options)
         return namespace(
             self.options,
             self.parent.parent.options,
         )
 
+    @property
+    def actions(self):
+        yield self
+
+
+class Reference(NamedNode):
+    """A reference"""
+
+    def __init__(self, name, parents, options, script):
+        NamedNode.__init__(self, name, parents, options)
+        self.script = script
+        self._target = None
+
+    @property
+    def actions(self):
+        if self._target is None:
+            self._generate()
+        return self._target.actions
+
+    def _generate(self):
+        target = self.script.targets.get(self.name)
+        if target is None:
+            raise Exception('Missing Reference Error')
+        self._target = target.clone_with(self.options)
+        self._target.parents.insert(0, self)
+        self.children.append(self._target)
+
 
 
 class Target(NamedNode):
     """A target"""
 
     def create_action(self, name, options):
-        act = Action(name, self, options)
-        self.children.append(act)
-        return act
+        return self.child(Action, name, options)
+
+    def create_reference(self, name, options, script):
+        return self.child(Reference, name, options, script)
+
+    def clone_with(self, overrides):
+        clone = self.clone()
+        clone.overrides.update(self.options)
+        return clone
+
+    @property
+    def actions(self):
+        for child in self.children:
+            for action in child.actions:
+                yield action
+
 
 
 class Script(NamedNode):
 
     def __init__(self):
-        NamedNode.__init__(self, None, None, {})
+        NamedNode.__init__(self, None, [], {})
         self.targets = {}
 
     def add_target(self, target):
         self.children.append(target)
         self.targets[target.name] = target
 
-    def create_target(self, name, options=None):
-        target = Target(name, self, options)
-        self.add_target(target)
-        return target
+    def create_target(self, name, options):
+        child = self.child(Target, name, options)
+        self.targets[name] = child
+        return child
 
     def merge(self, other):
         self.options.update(other.options)
         for target in other.children:
-            target.parent = self
+            target.parents.insert(0, self)
             self.add_target(target)
 
 
     :license: LGPL 2 or later (see LICENSE)
 """
 
-def print_lines(lines, prefix):
+import logging
+
+logging.basicConfig(level=logging.DEBUG,
+    format='%(asctime)s:  %(name)s: %(message)s',
+    datefmt='%H:%M:%S')
+
+log = logging.getLogger('===')
+
+from bu.preprocessing import pre_process_action
+
+
+def info(prefix, content):
+    log = logging.getLogger(prefix)
+    log.info(content)
+
+def log_lines(lines, level=info):
     for i, line in enumerate(lines):
-        print ' %s %s  %s' % (prefix, str(i + 1).rjust(8), line)
-
+        level(str(i + 1).rjust(8), line)
 
 def print_title(value):
-    print '=== %s' % value
-
-
-def print_semi_title(value):
-    print '--- %s' % value
-
+    info('==', value)
 
 def _flatten_options(options):
-    return ' '.join('%s=%s' % i for i in options.items())
-
+    return '{%s}' % ' '.join('%s=%s' % i for i in options.items())
 
 def print_action(action):
-    print_semi_title('!%s %s' % (action.name,
-                                 _flatten_options(action.options)))
-    print_lines(action.children, '>')
-
+    info(action.name, _flatten_options(action.options))
+    lines = pre_process_action(action, action.namespace).splitlines()
+    log_lines(lines)
 
 def print_reply(data, code):
+    info('>>', '%s' % code)
     lines = data.strip().splitlines()
-    print_semi_title('%s' % code)
-    print_lines(lines, '<')
+    log_lines(lines)
 
 ])
 
 scanner = re.Scanner([
+    (r'#.+', None),
     (r'^\s*@.+$', line_tokens('REFERENCE')),
     (r'^\s*!.+$', line_tokens('ACTION')),
     (r'[^\s].+:$', line_tokens('TARGET')),
-    (r'#.+', None),
     (r'\s+.+', external_token),
 ])
 
 
     def _parse_TARGET(self, ctx, tokens, line):
         name, options = self._parse_control(tokens, line)
-        options = pre_process_options(options, ctx.script)
         ctx.create_target(name, options)
 
     def _parse_ACTION(self, ctx, tokens, line):
         name, options = self._parse_control(tokens, line)
-        options = pre_process_options(options, ctx.target or ctx.script)
         handler = getattr(self, '_parse_act_%s' % name, None)
         if handler is not None:
             handler(ctx, options)
 
     def _parse_REFERENCE(self, ctx, tokens, line):
         name, options = self._parse_control(tokens, line)
-        options = pre_process_options(options, ctx.target or ctx.script)
-        ctx.action = ctx.target.create_action(name, options)
-        ctx.action.is_reference = True
+        ref = ctx.target.create_reference(name, options, ctx.script)
+        ctx.action = None
 
     def _parse_EXTERNAL(self, ctx, tokens, line):
         if ctx.target is None:

bu/preprocessing.py

 def pre_process_action(action, namespace):
     return Template(action.raw_content).render(namespace)
 
-def pre_process_options(options, parent):
+def pre_process_options(options):
     opts = {}
     for k, v in options.items():
-        opts[k] = Template(v).render(global_namespace(parent.namespace))
+        opts[k] = Template(v).render(options)
     return opts
 
 
-!use examples/distutils.bu
-!opt docs_build_dir=build/docs
+#! /usr/bin/bu
+#
+# Bu's own build script
+#
+!use distutils.bu
+!use sphinx.bu
+!use bitbucket.bu
 
-build_docs:
-    sphinx-build docs/ {{ docs_build_dir }}
-
-upload_docs:
-    python setup.py upload_docs
-
-docs:
-    @build_docs
-    @upload_docs
+!opt bb_project=aafshar/bu-main
 
 clean:
     rm -rf build dist bu.egg-info
 
-test PYTHONPATH="{{ cwd }}":
-    py.test
+test PYTHONPATH={{cwd}}:
     py.test
 
 
     packages=[
         'bu',
         'bu.actions',
+        'bu.lib',
     ],
     scripts = [
         'bin/bu',

tests/test_model.py

     target.options = {'foo': 'blah'}
     action = target.create_action('goo', {})
     action.options = {'foo': 'bleh'}
+    print action.namespace
     assert action.namespace == {'foo': 'bleh'}
     assert target.namespace == {'foo': 'blah'}
     assert script.namespace == {'foo': 'boo'}
 
 
-def test_alien_namespace(script):
+def _test_alien_namespace(script):
     script.options = {'foo': 'boo'}
     target = script.create_target('foo', {})
     target.options = {'foo': 'blah'}
     assert action.namespace == {'foo': 'blah'}
 
 
-def test_alien_namespace_override(script):
+def _test_alien_namespace_override(script):
     script.options = {'foo': 'boo'}
     target = script.create_target('foo', {})
     target.options = {'foo': 'blah', 'doo': 'woo'}

tests/test_parsing.py

 from py.test import raises
 
 from bu.parsing import Parser, lex
+from bu.preprocessing import pre_process_options
 
 def pytest_funcarg__lex(request):
     return lex
 
 def test_parser_action(parser):
     targets = parser.parse_string('too:\n!foo\n').targets
+    print targets
     assert targets['too'].children[0].name == 'foo'
 
 
 def test_preprocess_option(parser):
     script = parser.parse_string('too b=lah:\n!foo a={{b}}\n')
-    print script.targets
-    assert script.targets['too'].children[0].options['a'] == 'lah'
+    ns = {}
+    ns.update(script.targets['too'].options)
+    ns.update(script.targets['too'].children[0].options)
+    opts = pre_process_options(ns)
+    assert opts['a'] == 'lah'