Added UserInterface and other improved abstractions for command handling
authorW. Trevor King <wking@drexel.edu>
Thu, 31 Dec 2009 19:32:39 +0000 (14:32 -0500)
committerW. Trevor King <wking@drexel.edu>
Thu, 31 Dec 2009 19:32:39 +0000 (14:32 -0500)
24 files changed:
libbe/command/__init__.py
libbe/command/assign.py
libbe/command/base.py
libbe/command/comment.py
libbe/command/commit.py
libbe/command/depend.py
libbe/command/diff.py
libbe/command/due.py
libbe/command/help.py
libbe/command/html.py
libbe/command/import_xml.py
libbe/command/init.py
libbe/command/list.py
libbe/command/merge.py
libbe/command/new.py
libbe/command/remove.py
libbe/command/set.py
libbe/command/severity.py
libbe/command/show.py
libbe/command/status.py
libbe/command/subscribe.py
libbe/command/tag.py
libbe/command/target.py
libbe/ui/command_line.py

index 916b5ce90b4832b58b9a01f579d281ddd5ee815a..8c92e6f948049c2f814a707fe8453a529949e9d0 100644 (file)
@@ -25,6 +25,16 @@ commands = base.commands
 Option = base.Option
 Argument = base.Argument
 Command = base.Command
+InputOutput = base.InputOutput
+StdInputOutput = base.StdInputOutput
+StringInputOutput = base.StringInputOutput
+UnconnectedStorageGetter = base.UnconnectedStorageGetter
+StorageCallbacks = base.StorageCallbacks
+UserInterface = base.UserInterface
 
-__all__ = [UserError, UnknownCommand, get_command, get_command_class,
-           commands, Option, Argument, Command]
+__all__ = [UserError, UnknownCommand,
+           get_command, get_command_class, commands,
+           Option, Argument, Command,
+           InputOutput, StdInputOutput, StringInputOutput,
+           StorageCallbacks, UnconnectedStorageGetter,
+           UserInterface]
index 6cf74bc1660b1a01beb07936818a5d5705c84331..b37b9fdaecefa4aad8912984720bfc2a47e17470 100644 (file)
@@ -29,29 +29,32 @@ class Assign (libbe.command.Command):
     >>> import sys
     >>> import libbe.bugdir
     >>> bd = libbe.bugdir.SimpleBugDir(memory=False)
-    >>> cmd = Assign()
-    >>> cmd._storage = bd.storage
-    >>> cmd._setup_io = lambda i_enc,o_enc : None
-    >>> cmd.stdout = sys.stdout
+    >>> io = libbe.command.StringInputOutput()
+    >>> io.stdout = sys.stdout
+    >>> ui = libbe.command.UserInterface(io=io)
+    >>> ui.storage_callbacks.set_storage(bd.storage)
+    >>> cmd = Assign(ui=ui)
 
     >>> bd.bug_from_uuid('a').assigned is None
     True
-    >>> ret = cmd.run({'user-id':u'Fran\xe7ois'}, ['-', '/a'])
+    >>> ui._user_id = u'Fran\xe7ois'
+    >>> ret = ui.run(cmd, args=['-', '/a'])
     >>> bd.flush_reload()
     >>> bd.bug_from_uuid('a').assigned
     u'Fran\\xe7ois'
 
-    >>> ret = cmd.run(args=['someone', '/a', '/b'])
+    >>> ret = ui.run(cmd, args=['someone', '/a', '/b'])
     >>> bd.flush_reload()
     >>> bd.bug_from_uuid('a').assigned
     'someone'
     >>> bd.bug_from_uuid('b').assigned
     'someone'
 
-    >>> ret = cmd.run(args=['none', '/a'])
+    >>> ret = ui.run(cmd, args=['none', '/a'])
     >>> bd.flush_reload()
     >>> bd.bug_from_uuid('a').assigned is None
     True
+    >>> ui.cleanup()
     >>> bd.cleanup()
     """
     name = 'assign'
index 2318aa7b9ec7247688ba58a35f3b1a7bf1f4805f..357940f120c1b7549be607dd84de1736588c1f2a 100644 (file)
@@ -3,6 +3,7 @@
 import codecs
 import optparse
 import os.path
+import StringIO
 import sys
 
 import libbe
@@ -37,14 +38,19 @@ def get_command(command_name):
         raise UnknownCommand(command_name)
     return cmd
 
-def get_command_class(module, command_name):
+def get_command_class(module=None, command_name=None):
     """Retrieves a command class from a module.
 
     >>> import_xml_mod = get_command('import-xml')
     >>> import_xml = get_command_class(import_xml_mod, 'import-xml')
     >>> repr(import_xml)
     "<class 'libbe.command.import_xml.Import_XML'>"
+    >>> import_xml = get_command_class(command_name='import-xml')
+    >>> repr(import_xml)
+    "<class 'libbe.command.import_xml.Import_XML'>"
     """
+    if module == None:
+        module = get_command(command_name)
     try:
         cname = command_name.capitalize().replace('-', '_')
         cmd = getattr(module, cname)
@@ -167,7 +173,7 @@ class OptionFormatter (optparse.IndentedHelpFormatter):
         return ''.join(ret[:-1])
 
 class Command (object):
-    """One-line command description.
+    """One-line command description here.
 
     >>> c = Command()
     >>> print c.help()
@@ -183,12 +189,8 @@ class Command (object):
 
     name = 'command'
 
-    def __init__(self, input_encoding=None, output_encoding=None,
-                 get_unconnected_storage=None, ui=None):
-        self.input_encoding = input_encoding
-        self.output_encoding = output_encoding
-        self.get_unconnected_storage = get_unconnected_storage
-        self.ui = ui # calling user-interface, e.g. for Help()
+    def __init__(self, ui=None):
+        self.ui = ui # calling user-interface
         self.status = None
         self.result = None
         self.restrict_file_access = True
@@ -203,6 +205,21 @@ class Command (object):
         self.args = []
 
     def run(self, options=None, args=None):
+        self.status = 1 # in case we raise an exception
+        params = self._parse_options_args(options, args)
+        if params['help'] == True:
+            pass
+        else:
+            params.pop('help')
+        if params['complete'] != None:
+            pass
+        else:
+            params.pop('complete')
+
+        self.status = self._run(**params)
+        return self.status
+
+    def _parse_options_args(self, options=None, args=None):
         if options == None:
             options = {}
         if args == None:
@@ -242,33 +259,11 @@ class Command (object):
         if len(args) > len(self.args):  # add some additional repeats
             assert self.args[-1].repeatable == True, self.args[-1].name
             params[self.args[-1].name].extend(args[len(self.args):])
-
-        if params['help'] == True:
-            pass
-        else:
-            params.pop('help')
-        if params['complete'] != None:
-            pass
-        else:
-            params.pop('complete')
-
-        self._setup_io(self.input_encoding, self.output_encoding)
-        self.status = self._run(**params)
-        return self.status
+        return params
 
     def _run(self, **kwargs):
         raise NotImplementedError
 
-    def _setup_io(self, input_encoding=None, output_encoding=None):
-        if input_encoding == None:
-            input_encoding = libbe.util.encoding.get_input_encoding()
-        if output_encoding == None:
-            output_encoding = libbe.util.encoding.get_output_encoding()
-        self.stdin = codecs.getwriter(input_encoding)(sys.stdin)
-        self.stdin.encoding = input_encoding
-        self.stdout = codecs.getwriter(output_encoding)(sys.stdout)
-        self.stdout.encoding = output_encoding
-
     def help(self, *args):
         return '\n\n'.join([self.usage(),
                             self._option_help(),
@@ -340,43 +335,171 @@ class Command (object):
             raise UserError('file access restricted!\n  %s not in %s'
                             % (path, repo))
 
-    def _get_unconnected_storage(self):
-        """Callback for use by commands that need it."""
+    def cleanup(self):
+        pass
+
+class InputOutput (object):
+    def __init__(self, stdin=None, stdout=None):
+        self.stdin = stdin
+        self.stdout = stdout
+
+    def setup_command(self, command):
+        if not hasattr(self.stdin, 'encoding'):
+            self.stdin.encoding = libbe.util.encoding.get_input_encoding()
+        if not hasattr(self.stdout, 'encoding'):
+            self.stdout.encoding = libbe.util.encoding.get_output_encoding()
+        command.stdin = self.stdin
+        command.stdin.encoding = self.stdin.encoding
+        command.stdout = self.stdout
+        command.stdout.encoding = self.stdout.encoding
+
+    def cleanup(self):
+        pass
+
+class StdInputOutput (InputOutput):
+    def __init__(self, input_encoding=None, output_encoding=None):
+        stdin,stdout = self._get_io(input_encoding, output_encoding)
+        InputOutput.__init__(self, stdin, stdout)
+
+    def _get_io(self, input_encoding=None, output_encoding=None):
+        if input_encoding == None:
+            input_encoding = libbe.util.encoding.get_input_encoding()
+        if output_encoding == None:
+            output_encoding = libbe.util.encoding.get_output_encoding()
+        stdin = codecs.getwriter(input_encoding)(sys.stdin)
+        stdin.encoding = input_encoding
+        stdout = codecs.getwriter(output_encoding)(sys.stdout)
+        stdout.encoding = output_encoding
+        return (stdin, stdout)
+
+class StringInputOutput (InputOutput):
+    """
+    >>> s = StringInputOutput()
+    >>> s.set_stdin('hello')
+    >>> s.stdin.read()
+    'hello'
+    >>> s.stdin.read()
+    >>> print >> s.stdout, 'goodbye'
+    >>> s.get_stdout()
+    'goodbye\n'
+    >>> s.get_stdout()
+    ''
+
+    Also works with unicode strings
+
+    >>> s.set_stdin(u'hello')
+    >>> s.stdin.read()
+    u'hello'
+    >>> print >> s.stdout, u'goodbye'
+    >>> s.get_stdout()
+    u'goodbye\n'
+    """
+    def __init__(self):
+        stdin = StringIO.StringIO()
+        stdin.encoding = 'utf-8'
+        stdout = StringIO.StringIO()
+        stdout.encoding = 'utf-8'
+        InputOutput.__init__(self, stdin, stdout)
+
+    def set_stdin(self, stdin_string):
+        self.stdin = StringIO.StringIO(stdin_string)
+
+    def get_stdout(self):
+        ret = self.stdout.getvalue()
+        self.stdout = StringIO.StringIO() # clear stdout for next read
+        self.stdin.encoding = 'utf-8'
+        return ret
+
+class UnconnectedStorageGetter (object):
+    def __init__(self, location):
+        self.location = location
+
+    def __call__(self):
+        return libbe.storage.get_storage(self.location)
+
+class StorageCallbacks (object):
+    def __init__(self, location=None):
+        if location == None:
+            location = '.'
+        self.location = location
+        self._get_unconnected_storage = UnconnectedStorageGetter(location)
+
+    def setup_command(self, command):
+        command._get_unconnected_storage = self.get_unconnected_storage
+        command._get_storage = self.get_storage
+        command._get_bugdir = self.get_bugdir
+
+    def get_unconnected_storage(self):
+        """
+        Callback for use by commands that need it.
+        
+        The returned Storage instance is may actually be connected,
+        but commands that make use of the returned value should only
+        make use of non-connected Storage methods.  This is mainly
+        intended for the init command, which calls Storage.init().
+        """
         if not hasattr(self, '_unconnected_storage'):
-            if self.get_unconnected_storage == None:
+            if self._get_unconnected_storage == None:
                 raise NotImplementedError
-            self._unconnected_storage = self.get_unconnected_storage()
+            self._unconnected_storage = self._get_unconnected_storage()
         return self._unconnected_storage
 
-    def _get_storage(self):
-        """
-        Callback for use by commands that need it.
+    def set_unconnected_storage(self, unconnected_storage):
+        self._unconnected_storage = unconnected_storage
 
-        Note that with the current implementation,
-        _get_unconnected_storage() will not work after this method
-        runs, but that shouldn't be an issue for any command I can
-        think of...
-        """
+    def get_storage(self):
+        """Callback for use by commands that need it."""
         if not hasattr(self, '_storage'):
-            self._storage = self._get_unconnected_storage()
+            self._storage = self.get_unconnected_storage()
             self._storage.connect()
             version = self._storage.storage_version()
             if version != libbe.storage.STORAGE_VERSION:
                 raise libbe.storage.InvalidStorageVersion(version)
         return self._storage
 
-    def _get_bugdir(self):
+    def set_storage(self, storage):
+        self._storage = storage
+
+    def get_bugdir(self):
         """Callback for use by commands that need it."""
         if not hasattr(self, '_bugdir'):
-            self._bugdir = libbe.bugdir.BugDir(self._get_storage(), from_storage=True)
+            self._bugdir = libbe.bugdir.BugDir(self.get_storage(),
+                                               from_storage=True)
         return self._bugdir
 
+    def set_bugdir(self, bugdir):
+        self._bugdir = bugdir
+
+    def cleanup(self):
+        if hasattr(self, '_storage'):
+            self._storage.disconnect()
+
+class UserInterface (object):
+    def __init__(self, io=None, location=None):
+        if io == None:
+            io = StringInputOutput()
+        self.io = io
+        self.storage_callbacks = StorageCallbacks(location)
+        self.restrict_file_access = True
+
+    def help(self):
+        raise NotImplementedError
+
+    def run(self, command, options=None, args=None):
+        command.ui = self
+        self.io.setup_command(command)
+        self.storage_callbacks.setup_command(command)
+        command.restrict_file_access = self.restrict_file_access
+        command._get_user_id = self._get_user_id
+        return command.run(options, args)
+
     def _get_user_id(self):
         """Callback for use by commands that need it."""
         if not hasattr(self, '_user_id'):
-            self._user_id = libbe.ui.util.user.get_user_id(self._get_storage())
+            self._user_id = libbe.ui.util.user.get_user_id(
+                self.storage_callbacks.get_storage())
         return self._user_id
 
     def cleanup(self):
-        if hasattr(self, '_storage'):
-            self._storage.disconnect()
+        self.storage_callbacks.cleanup()
+        self.io.cleanup()
index cb0dcbb3346d819b82aa3165c6204c5d232d1ff5..7b859a83ef850aabf33082e04b8ddc0f21e09929 100644 (file)
@@ -33,13 +33,14 @@ class Comment (libbe.command.Command):
     >>> import time
     >>> import libbe.bugdir
     >>> bd = libbe.bugdir.SimpleBugDir(memory=False)
-    >>> cmd = Comment()
-    >>> cmd._storage = bd.storage
-    >>> cmd._setup_io = lambda i_enc,o_enc : None
-    >>> cmd.stdout = sys.stdout
+    >>> io = libbe.command.StringInputOutput()
+    >>> io.stdout = sys.stdout
+    >>> ui = libbe.command.UserInterface(io=io)
+    >>> ui.storage_callbacks.set_storage(bd.storage)
+    >>> cmd = Comment(ui=ui)
 
-    >>> ret = cmd.run({'user-id':u'Fran\\xe7ois'},
-    ...               ['/a', 'This is a comment about a'])
+    >>> ui._user_id = u'Fran\\xe7ois'
+    >>> ret = ui.run(cmd, args=['/a', 'This is a comment about a'])
     >>> bd.flush_reload()
     >>> bug = bd.bug_from_uuid('a')
     >>> bug.load_comments(load_full=False)
@@ -58,12 +59,13 @@ class Comment (libbe.command.Command):
 
     >>> if 'EDITOR' in os.environ:
     ...     del os.environ['EDITOR']
-    >>> ret = cmd.run({'user-id':u'Frank'}, ['/b'])
+    >>> ui._user_id = u'Frank'
+    >>> ret = ui.run(cmd, args=['/b'])
     Traceback (most recent call last):
     UserError: No comment supplied, and EDITOR not specified.
 
     >>> os.environ['EDITOR'] = "echo 'I like cheese' > "
-    >>> ret = cmd.run({'user-id':u'Frank'}, ['/b'])
+    >>> ret = ui.run(cmd, args=['/b'])
     >>> bd.flush_reload()
     >>> bug = bd.bug_from_uuid('b')
     >>> bug.load_comments(load_full=False)
@@ -71,6 +73,7 @@ class Comment (libbe.command.Command):
     >>> print comment.body
     I like cheese
     <BLANKLINE>
+    >>> ui.cleanup()
     >>> bd.cleanup()
     """
     name = 'comment'
index 7d82e7dc4042b01085893d9161604ea0588bc941..4938c444372c8f234cd36fd02bfe3d4b961e51f4 100644 (file)
@@ -30,15 +30,17 @@ class Commit (libbe.command.Command):
     >>> import sys
     >>> import libbe.bugdir
     >>> bd = libbe.bugdir.SimpleBugDir(memory=False, versioned=True)
-    >>> cmd = Commit()
-    >>> cmd._storage = bd.storage
-    >>> cmd._setup_io = lambda i_enc,o_enc : None
-    >>> cmd.stdout = sys.stdout
+    >>> io = libbe.command.StringInputOutput()
+    >>> io.stdout = sys.stdout
+    >>> ui = libbe.command.UserInterface(io=io)
+    >>> ui.storage_callbacks.set_storage(bd.storage)
+    >>> cmd = Commit(ui=ui)
 
     >>> bd.extra_strings = ['hi there']
     >>> bd.flush_reload()
-    >>> cmd.run({'user-id':'Joe'}, ['Making a commit']) # doctest: +ELLIPSIS
+    >>> ui.run(cmd, {'user-id':'Joe'}, ['Making a commit']) # doctest: +ELLIPSIS
     Committed ...
+    >>> ui.cleanup()
     >>> bd.cleanup()
     """
     name = 'commit'
index 5e476fcd790090c06dbe57b953dfccab959f872a..9068f3ae8303c5b9b27f1b22e40aed4cf6d8f2c2 100644 (file)
@@ -45,35 +45,37 @@ class Depend (libbe.command.Command):
     >>> import sys
     >>> import libbe.bugdir
     >>> bd = libbe.bugdir.SimpleBugDir(memory=False)
-    >>> cmd = Depend()
-    >>> cmd._storage = bd.storage
-    >>> cmd._setup_io = lambda i_enc,o_enc : None
-    >>> cmd.stdout = sys.stdout
+    >>> io = libbe.command.StringInputOutput()
+    >>> io.stdout = sys.stdout
+    >>> ui = libbe.command.UserInterface(io=io)
+    >>> ui.storage_callbacks.set_storage(bd.storage)
+    >>> cmd = Depend(ui=ui)
 
-    >>> ret = cmd.run(args=['/a', '/b'])
+    >>> ret = ui.run(cmd, args=['/a', '/b'])
     a blocked by:
     b
-    >>> ret = cmd.run(args=['/a'])
+    >>> ret = ui.run(cmd, args=['/a'])
     a blocked by:
     b
-    >>> ret = cmd.run({'show-status':True}, ['/a']) # doctest: +NORMALIZE_WHITESPACE
+    >>> ret = ui.run(cmd, {'show-status':True}, ['/a']) # doctest: +NORMALIZE_WHITESPACE
     a blocked by:
     b closed
-    >>> ret = cmd.run(args=['/b', '/a'])
+    >>> ret = ui.run(cmd, args=['/b', '/a'])
     b blocked by:
     a
     b blocks:
     a
-    >>> ret = cmd.run({'show-status':True}, ['/a']) # doctest: +NORMALIZE_WHITESPACE
+    >>> ret = ui.run(cmd, {'show-status':True}, ['/a']) # doctest: +NORMALIZE_WHITESPACE
     a blocked by:
     b closed
     a blocks:
     b closed
-    >>> ret = cmd.run({'repair':True})
-    >>> ret = cmd.run({'remove':True}, ['/b', '/a'])
+    >>> ret = ui.run(cmd, {'repair':True})
+    >>> ret = ui.run(cmd, {'remove':True}, ['/b', '/a'])
     b blocks:
     a
-    >>> ret = cmd.run({'remove':True}, ['/a', '/b'])
+    >>> ret = ui.run(cmd, {'remove':True}, ['/a', '/b'])
+    >>> ui.cleanup()
     >>> bd.cleanup()
     """
     name = 'depend'
index d945f9687140a89e33465c63c63d00c5f49489be..ebfe8fe3e58b85cea7ddaccdc816569d8d8c9800 100644 (file)
@@ -30,27 +30,29 @@ class Diff (libbe.command.Command):
     >>> import sys
     >>> import libbe.bugdir
     >>> bd = libbe.bugdir.SimpleBugDir(memory=False, versioned=True)
+    >>> io = libbe.command.StringInputOutput()
+    >>> io.stdout = sys.stdout
+    >>> ui = libbe.command.UserInterface(io=io)
+    >>> ui.storage_callbacks.set_storage(bd.storage)
     >>> cmd = Diff()
-    >>> cmd._storage = bd.storage
-    >>> cmd._setup_io = lambda i_enc,o_enc : None
-    >>> cmd.stdout = sys.stdout
 
     >>> original = bd.storage.commit('Original status')
     >>> bug = bd.bug_from_uuid('a')
     >>> bug.status = 'closed'
     >>> changed = bd.storage.commit('Closed bug a')
-    >>> ret = cmd.run(args=[original])
+    >>> ret = ui.run(cmd, args=[original])
     Modified bugs:
       abc/a:cm: Bug A
         Changed bug settings:
           status: open -> closed
-    >>> ret = cmd.run({'subscribe':'%(bugdir_id)s:mod', 'uuids':True}, [original])
+    >>> ret = ui.run(cmd, {'subscribe':'%(bugdir_id)s:mod', 'uuids':True}, [original])
     a
     >>> bd.storage.versioned = False
-    >>> ret = cmd.run(args=[original])
+    >>> ret = ui.run(cmd, args=[original])
     Traceback (most recent call last):
       ...
     UserError: This repository is not revision-controlled.
+    >>> ui.cleanup()
     >>> bd.cleanup()
     """ % {'bugdir_id':libbe.diff.BUGDIR_ID}
     name = 'diff'
index f3ad2f104d5067de25d93a5952a49f8e15af8d4e..2eb319434932eb78d925405ea6c86fbb7c12ffa0 100644 (file)
@@ -29,19 +29,21 @@ class Due (libbe.command.Command):
     >>> import sys
     >>> import libbe.bugdir
     >>> bd = libbe.bugdir.SimpleBugDir(memory=False)
-    >>> cmd = Due()
-    >>> cmd._storage = bd.storage
-    >>> cmd._setup_io = lambda i_enc,o_enc : None
-    >>> cmd.stdout = sys.stdout
+    >>> io = libbe.command.StringInputOutput()
+    >>> io.stdout = sys.stdout
+    >>> ui = libbe.command.UserInterface(io=io)
+    >>> ui.storage_callbacks.set_storage(bd.storage)
+    >>> cmd = Due(ui=ui)
 
-    >>> ret = cmd.run(args=['/a'])
+    >>> ret = ui.run(cmd, args=['/a'])
     No due date assigned.
-    >>> ret = cmd.run(args=['/a', 'Thu, 01 Jan 1970 00:00:00 +0000'])
-    >>> ret = cmd.run(args=['/a'])
+    >>> ret = ui.run(cmd, args=['/a', 'Thu, 01 Jan 1970 00:00:00 +0000'])
+    >>> ret = ui.run(cmd, args=['/a'])
     Thu, 01 Jan 1970 00:00:00 +0000
-    >>> ret = cmd.run(args=['/a', 'none'])
-    >>> ret = cmd.run(args=['/a'])
+    >>> ret = ui.run(cmd, args=['/a', 'none'])
+    >>> ret = ui.run(cmd, args=['/a'])
     No due date assigned.
+    >>> ui.cleanup()
     >>> bd.cleanup()
     """
     name = 'due'
index 6e5559884478f61f9031b968ff959c94e9318bd6..d509d31814f6f7806fe8792c8ddaca6b44006dea 100644 (file)
@@ -28,11 +28,12 @@ class Help (libbe.command.Command):
 
     >>> import sys
     >>> import libbe.bugdir
+    >>> io = libbe.command.StringInputOutput()
+    >>> io.stdout = sys.stdout
+    >>> ui = libbe.command.UserInterface(io=io)
     >>> cmd = Help()
-    >>> cmd._setup_io = lambda i_enc,o_enc : None
-    >>> cmd.stdout = sys.stdout
 
-    >>> ret = cmd.run(args=['help'])
+    >>> ret = ui.run(cmd, args=['help'])
     usage: be help [options] [TOPIC]
     <BLANKLINE>
     Options:
@@ -61,8 +62,7 @@ class Help (libbe.command.Command):
         elif params['topic'] in libbe.command.commands():
             module = libbe.command.get_command(params['topic'])
             Class = libbe.command.get_command_class(module,params['topic'])
-            c = Class(get_unconnected_storage=self.get_unconnected_storage,
-                      ui=self.ui)
+            c = Class(ui=self.ui)
             print >> self.stdout, c.help().rstrip('\n')
         elif params['topic'] in TOPICS:
             print >> self.stdout, TOPICS[params['topic']].rstrip('\n')
index 99cd61f07ec4595f4ec79085792cb8d4b06ff562..7fd753acda39b219500460b436631e8f75d9b5b3 100644 (file)
@@ -36,12 +36,13 @@ class HTML (libbe.command.Command):
     >>> import sys
     >>> import libbe.bugdir
     >>> bd = libbe.bugdir.SimpleBugDir(memory=False)
-    >>> cmd = HTML()
-    >>> cmd._storage = bd.storage
-    >>> cmd._setup_io = lambda i_enc,o_enc : None
-    >>> cmd.stdout = sys.stdout
+    >>> io = libbe.command.StringInputOutput()
+    >>> io.stdout = sys.stdout
+    >>> ui = libbe.command.UserInterface(io=io)
+    >>> ui.storage_callbacks.set_storage(bd.storage)
+    >>> cmd = HTML(ui=ui)
 
-    >>> ret = cmd.run({'output':os.path.join(bd.storage.repo, 'html_export')})
+    >>> ret = ui.run(cmd, {'output':os.path.join(bd.storage.repo, 'html_export')})
     >>> os.path.exists(os.path.join(bd.storage.repo, 'html_export'))
     True
     >>> os.path.exists(os.path.join(bd.storage.repo, 'html_export', 'index.html'))
@@ -54,6 +55,7 @@ class HTML (libbe.command.Command):
     True
     >>> os.path.exists(os.path.join(bd.storage.repo, 'html_export', 'bugs', 'b.html'))
     True
+    >>> ui.cleanup()
     >>> bd.cleanup()
     """
     name = 'html'
index 2e96848bd2b81b3a08e10939d066fd7ae0b832f9..be22c82033164a46653e2aa4a4711e1171299ebd 100644 (file)
@@ -44,14 +44,14 @@ class Import_XML (libbe.command.Command):
     >>> import StringIO
     >>> import libbe.bugdir
     >>> bd = libbe.bugdir.SimpleBugDir(memory=False)
-    >>> cmd = Import_XML()
-    >>> cmd._storage = bd.storage
-    >>> cmd._setup_io = lambda i_enc,o_enc : None
-    >>> cmd.stdout = sys.stdout
+    >>> io = libbe.command.StringInputOutput()
+    >>> io.stdout = sys.stdout
+    >>> ui = libbe.command.UserInterface(io=io)
+    >>> ui.storage_callbacks.set_storage(bd.storage)
+    >>> cmd = Import_XML(ui=ui)
 
-    >>> cmd.stdin = StringIO.StringIO('<be-xml><comment><uuid>c</uuid><body>This is a comment about a</body></comment></be-xml>')
-    >>> cmd.stdin.encoding = 'ascii'
-    >>> ret = cmd.run({'comment-root':'/a'}, ['-'])
+    >>> ui.io.set_stdin('<be-xml><comment><uuid>c</uuid><body>This is a comment about a</body></comment></be-xml>')
+    >>> ret = ui.run(cmd, {'comment-root':'/a'}, ['-'])
     >>> bd.flush_reload()
     >>> bug = bd.bug_from_uuid('a')
     >>> bug.load_comments(load_full=False)
@@ -63,6 +63,7 @@ class Import_XML (libbe.command.Command):
     True
     >>> comment.in_reply_to is None
     True
+    >>> ui.cleanup()
     >>> bd.cleanup()
     """
     name = 'import-xml'
@@ -321,7 +322,10 @@ if libbe.TESTING == True:
         """
         def setUp(self):
             self.bugdir = libbe.bugdir.SimpleBugDir(memory=False)
-            self.cmd = Import_XML()
+            io = libbe.command.StringInputOutput()
+            self.ui = libbe.command.UserInterface(io=io)
+            self.ui.storage_callbacks.set_storage(self.bugdir.storage)
+            self.cmd = Import_XML(ui=self.ui)
             self.cmd._storage = self.bugdir.storage
             self.cmd._setup_io = lambda i_enc,o_enc : None
             bugA = self.bugdir.bug_from_uuid('a')
@@ -360,13 +364,12 @@ if libbe.TESTING == True:
               </bug>
             </be-xml>
             """
-            self.cmd.stdin = StringIO.StringIO(self.xml)
-            self.cmd.stdin.encoding = 'ascii'
-            self.cmd.stdout = StringIO.StringIO()
         def tearDown(self):
             self.bugdir.cleanup()
+            self.ui.cleanup()
         def _execute(self, params={}, args=[]):
-            self.cmd.run(params, args)
+            self.ui.io.set_stdin(self.xml)
+            self.ui.run(self.cmd, params, args)
             self.bugdir.flush_reload()
         def testCleanBugdir(self):
             uuids = list(self.bugdir.uuids())
index b52d8e88a9b9a92ad3d8c008eea7489a27147230..2a78147ad01da9316b4fe155f96dfb66a2cbdb3f 100644 (file)
@@ -30,9 +30,10 @@ class Init (libbe.command.Command):
     >>> import libbe.storage.vcs
     >>> import libbe.storage.vcs.base
     >>> import libbe.util.utility
+    >>> io = libbe.command.StringInputOutput()
+    >>> io.stdout = sys.stdout
+    >>> ui = libbe.command.UserInterface(io=io)
     >>> cmd = Init()
-    >>> cmd._setup_io = lambda i_enc,o_enc : None
-    >>> cmd.stdout = sys.stdout
 
     >>> dir = libbe.util.utility.Dir()
     >>> vcs = libbe.storage.vcs.vcs_by_name('None')
@@ -40,10 +41,10 @@ class Init (libbe.command.Command):
     >>> try:
     ...     vcs.connect()
     ... except libbe.storage.ConnectionError:
-    ...     True
-    True
-    >>> cmd._unconnected_storage = vcs
-    >>> cmd.run()
+    ...     'got error'
+    'got error'
+    >>> ui.storage_callbacks.set_unconnected_storage(vcs)
+    >>> ui.run(cmd)
     No revision control detected.
     BE repository initialized.
     >>> bd = libbe.bugdir.BugDir(vcs)
@@ -55,9 +56,9 @@ class Init (libbe.command.Command):
     >>> vcs = libbe.storage.vcs.installed_vcs()
     >>> vcs.repo = dir.path
     >>> vcs._vcs_init(vcs.repo)
-    >>> cmd._unconnected_storage = vcs
+    >>> ui.storage_callbacks.set_unconnected_storage(vcs)
     >>> if vcs.name in libbe.storage.vcs.base.VCS_ORDER:
-    ...     cmd.run() # doctest: +ELLIPSIS
+    ...     ui.run(cmd) # doctest: +ELLIPSIS
     ... else:
     ...     vcs.init()
     ...     vcs.connect()
index d48c7ee43aa3c1f119ffd478c57c82e991b3c634..3442f424ebb2da813c1488bd37b0d314dadf1394 100644 (file)
@@ -59,17 +59,19 @@ class List (libbe.command.Command):
     >>> import sys
     >>> import libbe.bugdir
     >>> bd = libbe.bugdir.SimpleBugDir(memory=False)
-    >>> cmd = List()
-    >>> cmd._storage = bd.storage
-    >>> cmd._setup_io = lambda i_enc,o_enc : None
-    >>> cmd.stdout = sys.stdout
+    >>> io = libbe.command.StringInputOutput()
+    >>> io.stdout = sys.stdout
+    >>> ui = libbe.command.UserInterface(io=io)
+    >>> ui.storage_callbacks.set_storage(bd.storage)
+    >>> cmd = List(ui=ui)
 
-    >>> ret = cmd.run()
+    >>> ret = ui.run(cmd)
     abc/a:om: Bug A
-    >>> ret = cmd.run({'status':'closed'})
+    >>> ret = ui.run(cmd, {'status':'closed'})
     abc/b:cm: Bug B
     >>> bd.storage.writeable
     True
+    >>> ui.cleanup()
     >>> bd.cleanup()
     """
 
index 328351e77fa73c1ff1731d810ea61356c860e79c..0c34f69c53069cd2706f8358993b0e2f882897f1 100644 (file)
@@ -30,10 +30,11 @@ class Merge (libbe.command.Command):
     >>> import libbe.bugdir
     >>> import libbe.comment
     >>> bd = libbe.bugdir.SimpleBugDir(memory=False)
-    >>> cmd = Merge()
-    >>> cmd._storage = bd.storage
-    >>> cmd._setup_io = lambda i_enc,o_enc : None
-    >>> cmd.stdout = sys.stdout
+    >>> io = libbe.command.StringInputOutput()
+    >>> io.stdout = sys.stdout
+    >>> ui = libbe.command.UserInterface(io=io)
+    >>> ui.storage_callbacks.set_bugdir(bd)
+    >>> cmd = Merge(ui=ui)
 
     >>> a = bd.bug_from_uuid('a')
     >>> a.comment_root.time = 0
@@ -49,7 +50,7 @@ class Merge (libbe.command.Command):
     >>> dummy = dummy.new_reply('1 2 3 4')
     >>> dummy.time = 2
 
-    >>> ret = cmd.run(args=['/a', '/b'])
+    >>> ret = ui.run(cmd, args=['/a', '/b'])
     Merged bugs #abc/a# and #abc/b#
     >>> bd.flush_reload()
     >>> a = bd.bug_from_uuid('a')
@@ -134,6 +135,7 @@ class Merge (libbe.command.Command):
     Merged into bug #abc/a#
     >>> print b.status
     closed
+    >>> ui.cleanup()
     >>> bd.cleanup()
     """
     name = 'merge'
index 57ff5dc07dcbf77c390d56fff9ad350cc869df43..a4700521bdba02d500ec10ab5a6d09aeb3b92fcd 100644 (file)
@@ -30,14 +30,15 @@ class New (libbe.command.Command):
     >>> import libbe.bugdir
     >>> import libbe.util.id
     >>> bd = libbe.bugdir.SimpleBugDir(memory=False)
+    >>> io = libbe.command.StringInputOutput()
+    >>> io.stdout = sys.stdout
+    >>> ui = libbe.command.UserInterface(io=io)
+    >>> ui.storage_callbacks.set_storage(bd.storage)
     >>> cmd = New()
-    >>> cmd._storage = bd.storage
-    >>> cmd._setup_io = lambda i_enc,o_enc : None
-    >>> cmd.stdout = sys.stdout
 
     >>> uuid_gen = libbe.util.id.uuid_gen
     >>> libbe.util.id.uuid_gen = lambda: 'X'
-    >>> ret = cmd.run(args=['this is a test',])
+    >>> ret = ui.run(cmd, args=['this is a test',])
     Created bug with ID abc/X
     >>> libbe.util.id.uuid_gen = uuid_gen
     >>> bd.flush_reload()
@@ -50,6 +51,7 @@ class New (libbe.command.Command):
     minor
     >>> print bug.status
     open
+    >>> ui.cleanup()
     >>> bd.cleanup()
     """
     name = 'new'
index 3b9d3f3af8920473f728b73db90783257db82c60..c6d481f7bd6c18f3cdd471d6876343359dc1f30f 100644 (file)
@@ -26,14 +26,15 @@ class Remove (libbe.command.Command):
     >>> import sys
     >>> import libbe.bugdir
     >>> bd = libbe.bugdir.SimpleBugDir(memory=False)
-    >>> cmd = Remove()
-    >>> cmd._storage = bd.storage
-    >>> cmd._setup_io = lambda i_enc,o_enc : None
-    >>> cmd.stdout = sys.stdout
+    >>> io = libbe.command.StringInputOutput()
+    >>> io.stdout = sys.stdout
+    >>> ui = libbe.command.UserInterface(io=io)
+    >>> ui.storage_callbacks.set_storage(bd.storage)
+    >>> cmd = Remove(ui=ui)
 
     >>> print bd.bug_from_uuid('b').status
     closed
-    >>> ret = cmd.run(args=['/b'])
+    >>> ret = ui.run(cmd, args=['/b'])
     Removed bug abc/b
     >>> bd.flush_reload()
     >>> try:
@@ -41,6 +42,7 @@ class Remove (libbe.command.Command):
     ... except libbe.bugdir.NoBugMatches:
     ...     print 'Bug not found'
     Bug not found
+    >>> ui.cleanup()
     >>> bd.cleanup()
     """
     name = 'remove'
index 4fe0117f22a9b6addb33ff68c03ae08bb5dcc2cb..46a63b4b5e038a51637d3f4db4305f2f257e7bc7 100644 (file)
@@ -34,19 +34,21 @@ class Set (libbe.command.Command):
     >>> import sys
     >>> import libbe.bugdir
     >>> bd = libbe.bugdir.SimpleBugDir(memory=False)
-    >>> cmd = Set()
-    >>> cmd._storage = bd.storage
-    >>> cmd._setup_io = lambda i_enc,o_enc : None
-    >>> cmd.stdout = sys.stdout
+    >>> io = libbe.command.StringInputOutput()
+    >>> io.stdout = sys.stdout
+    >>> ui = libbe.command.UserInterface(io=io)
+    >>> ui.storage_callbacks.set_storage(bd.storage)
+    >>> cmd = Set(ui=ui)
 
-    >>> ret = cmd.run(args=['target'])
+    >>> ret = ui.run(cmd, args=['target'])
     None
-    >>> ret = cmd.run(args=['target', 'abcdefg'])
-    >>> ret = cmd.run(args=['target'])
+    >>> ret = ui.run(cmd, args=['target', 'abcdefg'])
+    >>> ret = ui.run(cmd, args=['target'])
     abcdefg
-    >>> ret = cmd.run(args=['target', 'none'])
-    >>> ret = cmd.run(args=['target'])
+    >>> ret = ui.run(cmd, args=['target', 'none'])
+    >>> ret = ui.run(cmd, args=['target'])
     None
+    >>> ui.cleanup()
     >>> bd.cleanup()
     """
     name = 'set'
index 7c1d3052353570567199b479d90a8ce42190e3f0..3587325974546bd27648baaea6e6754400335227 100644 (file)
@@ -30,20 +30,22 @@ class Severity (libbe.command.Command):
     >>> import sys
     >>> import libbe.bugdir
     >>> bd = libbe.bugdir.SimpleBugDir(memory=False)
-    >>> cmd = Severity()
-    >>> cmd._storage = bd.storage
-    >>> cmd._setup_io = lambda i_enc,o_enc : None
-    >>> cmd.stdout = sys.stdout
+    >>> io = libbe.command.StringInputOutput()
+    >>> io.stdout = sys.stdout
+    >>> ui = libbe.command.UserInterface(io=io)
+    >>> ui.storage_callbacks.set_bugdir(bd)
+    >>> cmd = Severity(ui=ui)
 
     >>> bd.bug_from_uuid('a').severity
     'minor'
-    >>> ret = cmd.run(args=['wishlist', '/a'])
+    >>> ret = ui.run(cmd, args=['wishlist', '/a'])
     >>> bd.flush_reload()
     >>> bd.bug_from_uuid('a').severity
     'wishlist'
-    >>> ret = cmd.run(args=['none', '/a'])
+    >>> ret = ui.run(cmd, args=['none', '/a'])
     Traceback (most recent call last):
     UserError: Invalid severity level: none
+    >>> ui.cleanup()
     >>> bd.cleanup()
     """
     name = 'severity'
index 1a569a6102ea3e6478e6b4db71bf7a0d2bd7b30a..5ab6dc732feee382ec54e04e28bb75d8403d1d3d 100644 (file)
@@ -34,13 +34,14 @@ class Show (libbe.command.Command):
     >>> import sys
     >>> import libbe.bugdir
     >>> bd = libbe.bugdir.SimpleBugDir(memory=False)
-    >>> cmd = Show()
-    >>> cmd._storage = bd.storage
-    >>> cmd._setup_io = lambda i_enc,o_enc : None
-    >>> cmd.stdout = sys.stdout
-    >>> cmd.stdout.encoding = 'ascii'
-
-    >>> ret = cmd.run(args=['/a',])  # doctest: +ELLIPSIS
+    >>> io = libbe.command.StringInputOutput()
+    >>> io.stdout = sys.stdout
+    >>> io.stdout.encoding = 'ascii'
+    >>> ui = libbe.command.UserInterface(io=io)
+    >>> ui.storage_callbacks.set_bugdir(bd)
+    >>> cmd = Show(ui=ui)
+
+    >>> ret = ui.run(cmd, args=['/a',])  # doctest: +ELLIPSIS
               ID : a
       Short name : abc/a
         Severity : minor
@@ -52,7 +53,7 @@ class Show (libbe.command.Command):
     Bug A
     <BLANKLINE>
 
-    >>> ret = cmd.run({'xml':True}, ['/a'])  # doctest: +ELLIPSIS
+    >>> ret = ui.run(cmd, {'xml':True}, ['/a'])  # doctest: +ELLIPSIS
     <?xml version="1.0" encoding="..." ?>
     <be-xml>
       <version>
@@ -71,6 +72,7 @@ class Show (libbe.command.Command):
         <summary>Bug A</summary>
       </bug>
     </be-xml>
+    >>> ui.cleanup()
     >>> bd.cleanup()
     """
     name = 'show'
index 323963af513fa1f6367c9f5a05a5f337240c4f6b..57a44aa98bf14edbc68450ae14e961c7a4a36e9b 100644 (file)
@@ -27,20 +27,23 @@ class Status (libbe.command.Command):
     >>> import sys
     >>> import libbe.bugdir
     >>> bd = libbe.bugdir.SimpleBugDir(memory=False)
-    >>> cmd = Status()
+    >>> io = libbe.command.StringInputOutput()
+    >>> io.stdout = sys.stdout
+    >>> ui = libbe.command.UserInterface(io=io)
+    >>> ui.storage_callbacks.set_bugdir(bd)
+    >>> cmd = Status(ui=ui)
     >>> cmd._storage = bd.storage
-    >>> cmd._setup_io = lambda i_enc,o_enc : None
-    >>> cmd.stdout = sys.stdout
 
     >>> bd.bug_from_uuid('a').status
     'open'
-    >>> ret = cmd.run(args=['closed', '/a'])
+    >>> ret = ui.run(cmd, args=['closed', '/a'])
     >>> bd.flush_reload()
     >>> bd.bug_from_uuid('a').status
     'closed'
-    >>> ret = cmd.run(args=['none', '/a'])
+    >>> ret = ui.run(cmd, args=['none', '/a'])
     Traceback (most recent call last):
     UserError: Invalid status level: none
+    >>> ui.cleanup()
     >>> bd.cleanup()
     """
     name = 'status'
index a837f99b46afd5d825115ab2654f531f6c710378..78d6fe0277ecea98454b0c5d3053e5ee7494b5a9 100644 (file)
@@ -35,47 +35,49 @@ class Subscribe (libbe.command.Command):
     >>> import sys
     >>> import libbe.bugdir
     >>> bd = libbe.bugdir.SimpleBugDir(memory=False)
-    >>> cmd = Subscribe()
-    >>> cmd._bugdir = bd
-    >>> cmd._setup_io = lambda i_enc,o_enc : None
-    >>> cmd.stdout = sys.stdout
+    >>> io = libbe.command.StringInputOutput()
+    >>> io.stdout = sys.stdout
+    >>> ui = libbe.command.UserInterface(io=io)
+    >>> ui.storage_callbacks.set_bugdir(bd)
+    >>> cmd = Subscribe(ui=ui)
 
     >>> a = bd.bug_from_uuid('a')
     >>> print a.extra_strings
     []
-    >>> ret = cmd.run({'subscriber':'John Doe <j@doe.com>'}, ['/a']) # doctest: +NORMALIZE_WHITESPACE
+    >>> ret = ui.run(cmd, {'subscriber':'John Doe <j@doe.com>'}, ['/a']) # doctest: +NORMALIZE_WHITESPACE
     Subscriptions for abc/a:
     John Doe <j@doe.com>    all    *
     >>> bd.flush_reload()
     >>> a = bd.bug_from_uuid('a')
     >>> print a.extra_strings
     ['SUBSCRIBE:John Doe <j@doe.com>\\tall\\t*']
-    >>> ret = cmd.run({'subscriber':'Jane Doe <J@doe.com>', 'servers':'a.com,b.net'}, ['/a']) # doctest: +NORMALIZE_WHITESPACE
+    >>> ret = ui.run(cmd, {'subscriber':'Jane Doe <J@doe.com>', 'servers':'a.com,b.net'}, ['/a']) # doctest: +NORMALIZE_WHITESPACE
     Subscriptions for abc/a:
     Jane Doe <J@doe.com>    all    a.com,b.net
     John Doe <j@doe.com>    all    *
-    >>> ret = cmd.run({'subscriber':'Jane Doe <J@doe.com>', 'servers':'a.edu'}, ['/a']) # doctest: +NORMALIZE_WHITESPACE
+    >>> ret = ui.run(cmd, {'subscriber':'Jane Doe <J@doe.com>', 'servers':'a.edu'}, ['/a']) # doctest: +NORMALIZE_WHITESPACE
     Subscriptions for abc/a:
     Jane Doe <J@doe.com>    all    a.com,a.edu,b.net
     John Doe <j@doe.com>    all    *
-    >>> ret = cmd.run({'unsubscribe':True, 'subscriber':'Jane Doe <J@doe.com>', 'servers':'a.com'}, ['/a']) # doctest: +NORMALIZE_WHITESPACE
+    >>> ret = ui.run(cmd, {'unsubscribe':True, 'subscriber':'Jane Doe <J@doe.com>', 'servers':'a.com'}, ['/a']) # doctest: +NORMALIZE_WHITESPACE
     Subscriptions for abc/a:
     Jane Doe <J@doe.com>    all    a.edu,b.net
     John Doe <j@doe.com>    all    *
-    >>> ret = cmd.run({'subscriber':'Jane Doe <J@doe.com>', 'servers':'*'}, ['/a']) # doctest: +NORMALIZE_WHITESPACE
+    >>> ret = ui.run(cmd, {'subscriber':'Jane Doe <J@doe.com>', 'servers':'*'}, ['/a']) # doctest: +NORMALIZE_WHITESPACE
     Subscriptions for abc/a:
     Jane Doe <J@doe.com>    all    *
     John Doe <j@doe.com>    all    *
-    >>> ret = cmd.run({'unsubscribe':True, 'subscriber':'Jane Doe <J@doe.com>'}, ['/a']) # doctest: +NORMALIZE_WHITESPACE
+    >>> ret = ui.run(cmd, {'unsubscribe':True, 'subscriber':'Jane Doe <J@doe.com>'}, ['/a']) # doctest: +NORMALIZE_WHITESPACE
     Subscriptions for abc/a:
     John Doe <j@doe.com>    all    *
-    >>> ret = cmd.run({'unsubscribe':True, 'subscriber':'John Doe <j@doe.com>'}, ['/a'])
-    >>> ret = cmd.run({'subscriber':'Jane Doe <J@doe.com>', 'types':'new'}, ['DIR']) # doctest: +NORMALIZE_WHITESPACE
+    >>> ret = ui.run(cmd, {'unsubscribe':True, 'subscriber':'John Doe <j@doe.com>'}, ['/a'])
+    >>> ret = ui.run(cmd, {'subscriber':'Jane Doe <J@doe.com>', 'types':'new'}, ['DIR']) # doctest: +NORMALIZE_WHITESPACE
     Subscriptions for bug directory:
     Jane Doe <J@doe.com>    new    *
-    >>> ret = cmd.run({'subscriber':'Jane Doe <J@doe.com>'}, ['DIR']) # doctest: +NORMALIZE_WHITESPACE
+    >>> ret = ui.run(cmd, {'subscriber':'Jane Doe <J@doe.com>'}, ['DIR']) # doctest: +NORMALIZE_WHITESPACE
     Subscriptions for bug directory:
     Jane Doe <J@doe.com>    all    *
+    >>> ui.cleanup()
     >>> bd.cleanup()
     """
     name = 'subscribe'
index bdb3f312b2a68ca5748bf22bb48262fe224279e9..87589c042c3f91648bb3ea2407ce6f7eae204dfa 100644 (file)
@@ -29,33 +29,34 @@ class Tag (libbe.command.Command):
     >>> import sys
     >>> import libbe.bugdir
     >>> bd = libbe.bugdir.SimpleBugDir(memory=False)
-    >>> cmd = Tag()
-    >>> cmd._bugdir = bd
-    >>> cmd._setup_io = lambda i_enc,o_enc : None
-    >>> cmd.stdout = sys.stdout
+    >>> io = libbe.command.StringInputOutput()
+    >>> io.stdout = sys.stdout
+    >>> ui = libbe.command.UserInterface(io=io)
+    >>> ui.storage_callbacks.set_bugdir(bd)
+    >>> cmd = Tag(ui=ui)
 
     >>> a = bd.bug_from_uuid('a')
     >>> print a.extra_strings
     []
-    >>> ret = cmd.run(args=['/a', 'GUI'])
+    >>> ret = ui.run(cmd, args=['/a', 'GUI'])
     Tags for abc/a:
     GUI
     >>> bd.flush_reload()
     >>> a = bd.bug_from_uuid('a')
     >>> print a.extra_strings
     ['%(tag_tag)sGUI']
-    >>> ret = cmd.run(args=['/a', 'later'])
+    >>> ret = ui.run(cmd, args=['/a', 'later'])
     Tags for abc/a:
     GUI
     later
-    >>> ret = cmd.run(args=['/a'])
+    >>> ret = ui.run(cmd, args=['/a'])
     Tags for abc/a:
     GUI
     later
-    >>> ret = cmd.run({'list':True})
+    >>> ret = ui.run(cmd, {'list':True})
     GUI
     later
-    >>> ret = cmd.run(args=['/a', 'Alphabetically first'])
+    >>> ret = ui.run(cmd, args=['/a', 'Alphabetically first'])
     Tags for abc/a:
     Alphabetically first
     GUI
@@ -67,15 +68,16 @@ class Tag (libbe.command.Command):
     >>> a.extra_strings = []
     >>> print a.extra_strings
     []
-    >>> ret = cmd.run(args=['/a'])
+    >>> ret = ui.run(cmd, args=['/a'])
     >>> bd.flush_reload()
     >>> a = bd.bug_from_uuid('a')
     >>> print a.extra_strings
     []
-    >>> ret = cmd.run(args=['/a', 'Alphabetically first'])
+    >>> ret = ui.run(cmd, args=['/a', 'Alphabetically first'])
     Tags for abc/a:
     Alphabetically first
-    >>> ret = cmd.run({'remove':True}, ['/a', 'Alphabetically first'])
+    >>> ret = ui.run(cmd, {'remove':True}, ['/a', 'Alphabetically first'])
+    >>> ui.cleanup()
     >>> bd.cleanup()
     """ % {'tag_tag':TAG_TAG}
     name = 'tag'
index 3195f9555d73c3d152d029c505d660c7cc1e4457..9f8feae71b4b9ee81f0541d34a39851ba22f2bf6 100644 (file)
@@ -31,30 +31,32 @@ class Target (libbe.command.Command):
     >>> import os, StringIO, sys
     >>> import libbe.bugdir
     >>> bd = libbe.bugdir.SimpleBugDir(memory=False)
-    >>> cmd = Target()
-    >>> cmd._storage = bd.storage
-    >>> cmd._setup_io = lambda i_enc,o_enc : None
-    >>> cmd.stdout = sys.stdout
+    >>> io = libbe.command.StringInputOutput()
+    >>> io.stdout = sys.stdout
+    >>> ui = libbe.command.UserInterface(io=io)
+    >>> ui.storage_callbacks.set_storage(bd.storage)
+    >>> cmd = Target(ui=ui)
 
-    >>> ret = cmd.run(args=['/a'])
+    >>> ret = ui.run(cmd, args=['/a'])
     No target assigned.
-    >>> ret = cmd.run(args=['/a', 'tomorrow'])
-    >>> ret = cmd.run(args=['/a'])
+    >>> ret = ui.run(cmd, args=['/a', 'tomorrow'])
+    >>> ret = ui.run(cmd, args=['/a'])
     tomorrow
 
-    >>> cmd.stdout = StringIO.StringIO()
-    >>> ret = cmd.run({'resolve':True}, ['tomorrow'])
-    >>> output = cmd.stdout.getvalue().strip()
+    >>> ui.io.stdout = StringIO.StringIO()
+    >>> ret = ui.run(cmd, {'resolve':True}, ['tomorrow'])
+    >>> output = ui.io.get_stdout().strip()
     >>> target = bd.bug_from_uuid(output)
     >>> print target.summary
     tomorrow
     >>> print target.severity
     target
 
-    >>> cmd.stdout = sys.stdout
-    >>> ret = cmd.run(args=['/a', 'none'])
-    >>> ret = cmd.run(args=['/a'])
+    >>> ui.io.stdout = sys.stdout
+    >>> ret = ui.run(cmd, args=['/a', 'none'])
+    >>> ret = ui.run(cmd, args=['/a'])
     No target assigned.
+    >>> ui.cleanup()
     >>> bd.cleanup()
     """
     name = 'target'
index 9df474ee2e3e6bdd261657a41f521ef64feeb2a8..b36d2519d8dc2645a94a8afdc469b273d99b9867 100755 (executable)
@@ -145,7 +145,7 @@ class CmdOptionParser(optparse.OptionParser):
                 fragment = parser.rargs[0]
             self.complete(argument, fragment)
         else:
-            print command_option.callback(
+            print >> self.command.stdout, command_option.callback(
                 self.command, command_option, value)
         raise CallbackExit
 
@@ -154,16 +154,18 @@ class CmdOptionParser(optparse.OptionParser):
         if fragment != None:
             comps = [c for c in comps if c.startswith(fragment)]
         if len(comps) > 0:
-            print '\n'.join(comps)
+            print >> self.command.stdout, '\n'.join(comps)
         raise CallbackExit
 
-
 class BE (libbe.command.Command):
     """Class for parsing the command line arguments for `be`.
     This class does not contain a useful _run() method.  Call this
     module's main() function instead.
 
-    >>> be = BE()
+    >>> ui = libbe.command.UserInterface()
+    >>> ui.io.stdout = sys.stdout
+    >>> be = BE(ui=ui)
+    >>> ui.io.setup_command(be)
     >>> p = CmdOptionParser(be)
     >>> p.exit_after_callback = False
     >>> try:
@@ -228,8 +230,7 @@ class BE (libbe.command.Command):
     def _long_help(self):
         cmdlist = []
         for name in libbe.command.commands():
-            module = libbe.command.get_command(name)
-            Class = libbe.command.get_command_class(module, name)
+            Class = libbe.command.get_command_class(command_name=name)
             assert hasattr(Class, '__doc__') and Class.__doc__ != None, \
                 'Command class %s missing docstring' % Class
             cmdlist.append((name, Class.__doc__.splitlines()[0]))
@@ -249,31 +250,51 @@ class BE (libbe.command.Command):
     def full_version(self, *args):
         return libbe.version.version(verbose=True)
 
+def dispatch(ui, command, args):
+    parser = CmdOptionParser(command)
+    try:
+        options,args = parser.parse_args(args)
+        ret = ui.run(command, options, args)
+    except CallbackExit:
+        return 0
+    except libbe.command.UserError, e:
+        print >> ui.io.stdout, 'ERROR:\n', e
+        return 1
+    except libbe.storage.ConnectionError, e:
+        print >> ui.io.stdout, 'Connection Error:\n', e
+        return 1
+    except (libbe.util.id.MultipleIDMatches, libbe.util.id.NoIDMatches,
+            libbe.util.id.InvalidIDStructure), e:
+        print >> ui.io.stdout, 'Invalid id:\n', e
+        return 1
+    finally:
+        command.cleanup()
+    return ret
+
 def main():
-    be = BE()
+    io = libbe.command.StdInputOutput()
+    ui = libbe.command.UserInterface(io)
+    ui.restrict_file_access = False
+    ui.storage_callbacks = None
+    be = BE(ui=ui)
     parser = CmdOptionParser(be)
     try:
         options,args = parser.parse_args()
     except CallbackExit:
         return 0
     except libbe.command.UserError, e:
-        print 'ERROR:\n', e
+        print >> be.stdout, 'ERROR:\n', e
         return 1
 
-    command_name = args[0]
+    command_name = args.pop(0)
     try:
-        module = libbe.command.get_command(command_name)
+        Class = libbe.command.get_command_class(command_name=command_name)
     except libbe.command.UnknownCommand, e:
-        print e
+        print >> be.stdout, e
         return 1
-    Class = getattr(module, command_name.capitalize())
-    class GUCS (object):
-        def __init__(self, repo):
-            self.repo = repo
-        def __call__(self):
-            return libbe.storage.get_storage(self.repo)
-    command = Class(get_unconnected_storage=GUCS(options['repo']), ui=be)
-    parser = CmdOptionParser(command)
+
+    ui.storage_callbacks = libbe.command.StorageCallbacks(options['repo'])
+    command = Class(ui=ui)
 
     if command.name in ['comment']:
         paginate = 'never'
@@ -285,22 +306,9 @@ def main():
         paginate = 'never'
     libbe.ui.util.pager.run_pager(paginate)
 
-    try:
-        options,args = parser.parse_args(args[1:])
-        command.run(options, args)
-    except CallbackExit:
-        command.cleanup()
-        return 0
-    except libbe.command.UserError, e:
-        command.cleanup()
-        print 'ERROR:\n', e
-        return 1
-    except libbe.storage.ConnectionError, e:
-        command.cleanup()
-        print 'Connection Error:\n', e
-        return 1
-    command.cleanup()
-    return 0
+    ret = dispatch(ui, command, args)
+    ui.cleanup()
+    return ret
 
 if __name__ == '__main__':
     sys.exit(main())