Andy Mikhailenko avatar Andy Mikhailenko committed 6bd64c9

BACKWARDS-INCOMPATIBLE. Added support for Python 3.x. Dropped support for Python ≤ 2.5. Included Benjamin Peterson's library "six" to keep the code clean while Python 2.x/3.x-compatible. Updated documentation.

Comments (0)

Files changed (14)

 ^cover/
 ,cover$
 
+^\.tox/
+
 # Arch Linux: makepkg
 ^src/
 ^pkg/
 # Maintainer: Andrey Mikhaylenko <neithere at gmail dot com>
 # Contributor: Fabien Devaux <fdev31 at gmail dot com>
 pkgname=python2-argh
-pkgver=0.14.2
+pkgver=0.15.0
 pkgrel=1
 pkgdesc="A simple argparse wrapper"
 arch=(any)
 url="http://bitbucket.org/neithere/argh/"
 license=('LGPL3')
-depends=('python2>=2.5' 'python2-argparse>=1.1')
+depends=('python2>=2.6' 'python2-argparse>=1.1')
 makedepends=('python2-distribute')
 provides=()
 conflicts=()
 options=(!emptydirs)
 install=
 source=(http://pypi.python.org/packages/source/a/argh/argh-${pkgver}.tar.gz)
-md5sums=('7711a0437cdaad18fcd600ee846d4939')
+md5sums=('aa47a35ea5904b3292c1e287977f8d53')
 
 build() {
    cd "${srcdir}/argh-${pkgver}"
-version = '0.14.2'
+version = '0.15.0'
 # -*- coding: utf-8 -*-
 #
-#  Copyright (c) 2010 Andrey Mikhailenko and contributors
+#  Copyright (c) 2010—2012 Andrey Mikhailenko and contributors
 #
 #  This file is part of Argh.
 #
    :members:
 
 """
-from exceptions import *
-from helpers import *
-from decorators import *
+from .exceptions import *
+from .helpers import *
+from .decorators import *

argh/completion.py

 # -*- coding: utf-8 -*-
 #
-#  Copyright (c) 2010 Andrey Mikhailenko and contributors
+#  Copyright (c) 2010—2012 Andrey Mikhailenko and contributors
 #
 #  This file is part of Argh.
 #
 
     choices = _autocomplete(root_parser, cwords, cword)
 
-    print ' '.join(choices)
+    print(' '.join(choices))
 
     sys.exit(1)
 

argh/constants.py

 # -*- coding: utf-8 -*-
 #
-#  Copyright (c) 2010 Andrey Mikhailenko and contributors
+#  Copyright (c) 2010—2012 Andrey Mikhailenko and contributors
 #
 #  This file is part of Argh.
 #

argh/decorators.py

 # -*- coding: utf-8 -*-
+#
+#  Copyright (c) 2010—2012 Andrey Mikhailenko and contributors
+#
+#  This file is part of Argh.
+#
+#  Argh is free software under terms of the GNU Lesser
+#  General Public License version 3 (LGPLv3) as published by the Free
+#  Software Foundation. See the file README for copying conditions.
+#
 """
 Command decorators
 ==================

argh/exceptions.py

+# -*- coding: utf-8 -*-
+#
+#  Copyright (c) 2010—2012 Andrey Mikhailenko and contributors
+#
+#  This file is part of Argh.
+#
+#  Argh is free software under terms of the GNU Lesser
+#  General Public License version 3 (LGPLv3) as published by the Free
+#  Software Foundation. See the file README for copying conditions.
+#
 """
 Exceptions
 ==========
 # -*- coding: utf-8 -*-
 #
-#  Copyright (c) 2010 Andrey Mikhailenko and contributors
+#  Copyright (c) 2010—2012 Andrey Mikhailenko and contributors
 #
 #  This file is part of Argh.
 #
 """
 import argparse
 import locale
-from StringIO import StringIO
 import sys
 from types import GeneratorType
 
+from argh.six import b, u, string_types, text_type, BytesIO
 from argh.exceptions import CommandError
 from argh.utils import get_subparsers
 from argh.completion import autocomplete
 
     if namespace:
         # make a namespace placeholder and register the commands within it
-        assert isinstance(namespace, str)
+        assert isinstance(namespace, string_types)
         subsubparser = subparsers.add_parser(namespace, help=title)
         subparsers = subsubparser.add_subparsers(title=title,
                                                  description=description,
     if output_file is None:
         # user wants a string; we create an internal temporary file-like object
         # and will return its contents as a string
-        f = StringIO()
+        f = BytesIO()
     else:
         # normally this is stdout; can be any file
         f = output_file
         f.write(output)
         if not raw_output:
             # in most cases user wants on message per line
-            f.write('\n')
+            f.write(b('\n'))
 
     if output_file is None:
         # user wanted a string; return contents of our temporary file-like obj
     is determined from terminal settings or, if none, from system settings.
     """
     # Convert string to Unicode
-    if not isinstance(line, unicode):
+    if not isinstance(line, text_type):
         try:
-            line = unicode(line)
+            line = text_type(line)
         except UnicodeDecodeError:
-            line = str(line).decode('utf-8')
+            line = b(line).decode('utf-8')
 
     # Choose output encoding
     if not encoding:
             # filter the namespace variables so that only those expected by the
             # actual function will pass
             f = args.function
-            expected_args = f.func_code.co_varnames[:f.func_code.co_argcount]
+            if hasattr(f, 'func_code'):
+                # Python 2
+                expected_args = f.func_code.co_varnames[:f.func_code.co_argcount]
+            else:
+                # Python 3
+                expected_args = f.__code__.co_varnames[:f.__code__.co_argcount]
             ok_args = [x for x in args._get_args() if x in expected_args]
             ok_kwargs = dict((k,v) for k,v in args._get_kwargs()
                              if k in expected_args)
         result = _call()
         for line in result:
             yield line
-    except tuple(wrappable_exceptions), e:
-        yield str(e)
+    except tuple(wrappable_exceptions) as e:
+        yield text_type(e)
 
 
 class ArghParser(argparse.ArgumentParser):
             False: ('y','N'),
         }
         y, n = defaults[default]
-        prompt = (u'%(action)s? (%(y)s/%(n)s)' % locals()).encode('utf-8')
+        prompt = u('{action}? ({y}/{n})').format(**locals()).encode('utf-8')
         choice = None
         try:
             if default is None:
+#
+# Copyright (c) 2010-2011 Benjamin Peterson
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy of
+# this software and associated documentation files (the "Software"), to deal in
+# the Software without restriction, including without limitation the rights to
+# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+# the Software, and to permit persons to whom the Software is furnished to do so,
+# subject to the following conditions:
+
+# The above copyright notice and this permission notice shall be included in all
+# copies or substantial portions of the Software.
+
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS
+# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+"""Utilities for writing code that runs on Python 2 and 3"""
+
+import operator
+import sys
+import types
+
+__author__ = "Benjamin Peterson <benjamin@python.org>"
+__version__ = "1.1.0"
+
+
+# True if we are running on Python 3.
+PY3 = sys.version_info[0] == 3
+
+if PY3:
+    string_types = str,
+    integer_types = int,
+    class_types = type,
+    text_type = str
+    binary_type = bytes
+
+    MAXSIZE = sys.maxsize
+else:
+    string_types = basestring,
+    integer_types = (int, long)
+    class_types = (type, types.ClassType)
+    text_type = unicode
+    binary_type = str
+
+    # It's possible to have sizeof(long) != sizeof(Py_ssize_t).
+    class X(object):
+        def __len__(self):
+            return 1 << 31
+    try:
+        len(X())
+    except OverflowError:
+        # 32-bit
+        MAXSIZE = int((1 << 31) - 1)
+    else:
+        # 64-bit
+        MAXSIZE = int((1 << 63) - 1)
+    del X
+
+
+def _add_doc(func, doc):
+    """Add documentation to a function."""
+    func.__doc__ = doc
+
+
+def _import_module(name):
+    """Import module, returning the module after the last dot."""
+    __import__(name)
+    return sys.modules[name]
+
+
+class _LazyDescr(object):
+
+    def __init__(self, name):
+        self.name = name
+
+    def __get__(self, obj, tp):
+        result = self._resolve()
+        setattr(obj, self.name, result)
+        # This is a bit ugly, but it avoids running this again.
+        delattr(tp, self.name)
+        return result
+
+
+class MovedModule(_LazyDescr):
+
+    def __init__(self, name, old, new=None):
+        super(MovedModule, self).__init__(name)
+        if PY3:
+            if new is None:
+                new = name
+            self.mod = new
+        else:
+            self.mod = old
+
+    def _resolve(self):
+        return _import_module(self.mod)
+
+
+class MovedAttribute(_LazyDescr):
+
+    def __init__(self, name, old_mod, new_mod, old_attr=None, new_attr=None):
+        super(MovedAttribute, self).__init__(name)
+        if PY3:
+            if new_mod is None:
+                new_mod = name
+            self.mod = new_mod
+            if new_attr is None:
+                if old_attr is None:
+                    new_attr = name
+                else:
+                    new_attr = old_attr
+            self.attr = new_attr
+        else:
+            self.mod = old_mod
+            if old_attr is None:
+                old_attr = name
+            self.attr = old_attr
+
+    def _resolve(self):
+        module = _import_module(self.mod)
+        return getattr(module, self.attr)
+
+
+
+class _MovedItems(types.ModuleType):
+    """Lazy loading of moved objects"""
+
+
+_moved_attributes = [
+    MovedAttribute("cStringIO", "cStringIO", "io", "StringIO"),
+    MovedAttribute("filter", "itertools", "builtins", "ifilter", "filter"),
+    MovedAttribute("map", "itertools", "builtins", "imap", "map"),
+    MovedAttribute("reload_module", "__builtin__", "imp", "reload"),
+    MovedAttribute("reduce", "__builtin__", "functools"),
+    MovedAttribute("StringIO", "StringIO", "io"),
+    MovedAttribute("xrange", "__builtin__", "builtins", "xrange", "range"),
+    MovedAttribute("zip", "itertools", "builtins", "izip", "zip"),
+
+    MovedModule("builtins", "__builtin__"),
+    MovedModule("configparser", "ConfigParser"),
+    MovedModule("copyreg", "copy_reg"),
+    MovedModule("http_cookiejar", "cookielib", "http.cookiejar"),
+    MovedModule("http_cookies", "Cookie", "http.cookies"),
+    MovedModule("html_entities", "htmlentitydefs", "html.entities"),
+    MovedModule("html_parser", "HTMLParser", "html.parser"),
+    MovedModule("http_client", "httplib", "http.client"),
+    MovedModule("BaseHTTPServer", "BaseHTTPServer", "http.server"),
+    MovedModule("CGIHTTPServer", "CGIHTTPServer", "http.server"),
+    MovedModule("SimpleHTTPServer", "SimpleHTTPServer", "http.server"),
+    MovedModule("cPickle", "cPickle", "pickle"),
+    MovedModule("queue", "Queue"),
+    MovedModule("reprlib", "repr"),
+    MovedModule("socketserver", "SocketServer"),
+    MovedModule("tkinter", "Tkinter"),
+    MovedModule("tkinter_dialog", "Dialog", "tkinter.dialog"),
+    MovedModule("tkinter_filedialog", "FileDialog", "tkinter.filedialog"),
+    MovedModule("tkinter_scrolledtext", "ScrolledText", "tkinter.scrolledtext"),
+    MovedModule("tkinter_simpledialog", "SimpleDialog", "tkinter.simpledialog"),
+    MovedModule("tkinter_tix", "Tix", "tkinter.tix"),
+    MovedModule("tkinter_constants", "Tkconstants", "tkinter.constants"),
+    MovedModule("tkinter_dnd", "Tkdnd", "tkinter.dnd"),
+    MovedModule("tkinter_colorchooser", "tkColorChooser",
+                "tkinter.colorchooser"),
+    MovedModule("tkinter_commondialog", "tkCommonDialog",
+                "tkinter.commondialog"),
+    MovedModule("tkinter_tkfiledialog", "tkFileDialog", "tkinter.filedialog"),
+    MovedModule("tkinter_font", "tkFont", "tkinter.font"),
+    MovedModule("tkinter_messagebox", "tkMessageBox", "tkinter.messagebox"),
+    MovedModule("tkinter_tksimpledialog", "tkSimpleDialog",
+                "tkinter.simpledialog"),
+    MovedModule("urllib_robotparser", "robotparser", "urllib.robotparser"),
+    MovedModule("winreg", "_winreg"),
+]
+for attr in _moved_attributes:
+    setattr(_MovedItems, attr.name, attr)
+del attr
+
+moves = sys.modules["six.moves"] = _MovedItems("moves")
+
+
+def add_move(move):
+    """Add an item to six.moves."""
+    setattr(_MovedItems, move.name, move)
+
+
+def remove_move(name):
+    """Remove item from six.moves."""
+    try:
+        delattr(_MovedItems, name)
+    except AttributeError:
+        try:
+            del moves.__dict__[name]
+        except KeyError:
+            raise AttributeError("no such move, %r" % (name,))
+
+
+if PY3:
+    _meth_func = "__func__"
+    _meth_self = "__self__"
+
+    _func_code = "__code__"
+    _func_defaults = "__defaults__"
+
+    _iterkeys = "keys"
+    _itervalues = "values"
+    _iteritems = "items"
+else:
+    _meth_func = "im_func"
+    _meth_self = "im_self"
+
+    _func_code = "func_code"
+    _func_defaults = "func_defaults"
+
+    _iterkeys = "iterkeys"
+    _itervalues = "itervalues"
+    _iteritems = "iteritems"
+
+
+try:
+    advance_iterator = next
+except NameError:
+    def advance_iterator(it):
+        return it.next()
+next = advance_iterator
+
+
+if PY3:
+    def get_unbound_function(unbound):
+        return unbound
+
+    Iterator = object
+
+    def callable(obj):
+        return any("__call__" in klass.__dict__ for klass in type(obj).__mro__)
+else:
+    def get_unbound_function(unbound):
+        return unbound.im_func
+
+    class Iterator(object):
+
+        def next(self):
+            return type(self).__next__(self)
+
+    callable = callable
+_add_doc(get_unbound_function,
+         """Get the function out of a possibly unbound function""")
+
+
+get_method_function = operator.attrgetter(_meth_func)
+get_method_self = operator.attrgetter(_meth_self)
+get_function_code = operator.attrgetter(_func_code)
+get_function_defaults = operator.attrgetter(_func_defaults)
+
+
+def iterkeys(d):
+    """Return an iterator over the keys of a dictionary."""
+    return getattr(d, _iterkeys)()
+
+def itervalues(d):
+    """Return an iterator over the values of a dictionary."""
+    return getattr(d, _itervalues)()
+
+def iteritems(d):
+    """Return an iterator over the (key, value) pairs of a dictionary."""
+    return getattr(d, _iteritems)()
+
+
+if PY3:
+    def b(s):
+        return s.encode("latin-1")
+    def u(s):
+        return s
+    if sys.version_info[1] <= 1:
+        def int2byte(i):
+            return bytes((i,))
+    else:
+        # This is about 2x faster than the implementation above on 3.2+
+        int2byte = operator.methodcaller("to_bytes", 1, "big")
+    import io
+    StringIO = io.StringIO
+    BytesIO = io.BytesIO
+else:
+    def b(s):
+        return s
+    def u(s):
+        return unicode(s, "unicode_escape")
+    int2byte = chr
+    import StringIO
+    StringIO = BytesIO = StringIO.StringIO
+_add_doc(b, """Byte literal""")
+_add_doc(u, """Text literal""")
+
+
+if PY3:
+    import builtins
+    exec_ = getattr(builtins, "exec")
+
+
+    def reraise(tp, value, tb=None):
+        if value.__traceback__ is not tb:
+            raise value.with_traceback(tb)
+        raise value
+
+
+    print_ = getattr(builtins, "print")
+    del builtins
+
+else:
+    def exec_(code, globs=None, locs=None):
+        """Execute code in a namespace."""
+        if globs is None:
+            frame = sys._getframe(1)
+            globs = frame.f_globals
+            if locs is None:
+                locs = frame.f_locals
+            del frame
+        elif locs is None:
+            locs = globs
+        exec("""exec code in globs, locs""")
+
+
+    exec_("""def reraise(tp, value, tb=None):
+    raise tp, value, tb
+""")
+
+
+    def print_(*args, **kwargs):
+        """The new-style print function."""
+        fp = kwargs.pop("file", sys.stdout)
+        if fp is None:
+            return
+        def write(data):
+            if not isinstance(data, basestring):
+                data = str(data)
+            fp.write(data)
+        want_unicode = False
+        sep = kwargs.pop("sep", None)
+        if sep is not None:
+            if isinstance(sep, unicode):
+                want_unicode = True
+            elif not isinstance(sep, str):
+                raise TypeError("sep must be None or a string")
+        end = kwargs.pop("end", None)
+        if end is not None:
+            if isinstance(end, unicode):
+                want_unicode = True
+            elif not isinstance(end, str):
+                raise TypeError("end must be None or a string")
+        if kwargs:
+            raise TypeError("invalid keyword arguments to print()")
+        if not want_unicode:
+            for arg in args:
+                if isinstance(arg, unicode):
+                    want_unicode = True
+                    break
+        if want_unicode:
+            newline = unicode("\n")
+            space = unicode(" ")
+        else:
+            newline = "\n"
+            space = " "
+        if sep is None:
+            sep = space
+        if end is None:
+            end = newline
+        for i, arg in enumerate(args):
+            if i:
+                write(sep)
+            write(arg)
+        write(end)
+
+_add_doc(reraise, """Reraise an exception.""")
+
+
+def with_metaclass(meta, base=object):
+    """Create a base class with a metaclass."""
+    return meta("NewBase", (base,), {})
 # -*- coding: utf-8 -*-
 #
-#  Copyright (c) 2010 Andrey Mikhailenko and contributors
+#  Copyright (c) 2010—2012 Andrey Mikhailenko and contributors
 #
 #  This file is part of Argh.
 #
 `argh`-aware code. Just keep in mind that :func:`~argh.helpers.dispatch` does
 some extra work that a custom dispatcher may not do.
 
+Dependencies
+------------
+
+The `argh` library is supported (and tested unless otherwise specified) on
+the following versions of Python:
+
+* 2.6 (`argparse` library is required)
+* 2.7 (including PyPy 1.8)
+* 3.1 (`argparse` library is required; **not** tested)
+* 3.2
+
+.. versionchanged:: 0.15
+   Added support for Python 3.x, dropped support for Python ≤ 2.5.
+
 Details
 -------
 
 Author
 ------
 
-Originally written by Andrey Mikhaylenko in 2010.
+Developed by Andrey Mikhaylenko since 2010.
 
 See :file:`AUTHORS` for a complete authors list of this application.
 
     CLI
         `Command-line interface`_. You should know what that is if you are
         here, right? :)
+
     DRY
         The `don't repeat yourself`_ principle.
 
 * :ref:`genindex`
 * :ref:`modindex`
 * :ref:`search`
-
 # -*- coding: utf-8 -*-
 
 import sys
-from StringIO import StringIO
+from argh.six import (
+    BytesIO, u, b, string_types, text_type, binary_type, iteritems
+)
 import unittest2 as unittest
 import argparse
 import argh.helpers
 @arg('--twice', default=False, help='repeat twice')
 def echo(args):
     repeat = 2 if args.twice else 1
-    return (u'you said %s' % args.text) * repeat
+    return (u('you said {0}').format(args.text)) * repeat
 
 @arg('text')
 @plain_signature
 def plain_echo(text):
-    return u'you said %s' % text
+    return u('you said {0}').format(text)
 
 @arg('--name', default='world')
 def hello(args):
-    return u'Hello %s!' % (args.name or 'world')
+    return u('Hello {0}!').format(args.name or 'world')
 
 @arg('buddy')
 def howdy(args):
-    return u'Howdy %s?' % args.buddy
+    return u('Howdy {0}?').format(args.buddy)
 
 @alias('aliased')
 def do_aliased(args):
 @arg('text')
 def strict_hello(args):
     assert args.text == 'world', 'Do it yourself'  # bad manners :-(
-    yield 'Hello %s' % args.text
+    yield 'Hello {0}'.format(args.text)
 
 @arg('text')
 @wrap_errors(AssertionError)
 def strict_hello_smart(args):
     assert args.text == 'world', 'Do it yourself'  # bad manners :-(
-    yield 'Hello %s' % args.text
+    yield 'Hello {0}'.format(args.text)
 
 @command
 def command_deco(text='Hello'):
 
 @command
 def command_deco_issue12(foo=1, fox=2):
-    yield u'foo {0}, fox {1}'.format(foo, fox)
+    yield u('foo {0}, fox {1}').format(foo, fox)
 
 
 class BaseArghTestCase(unittest.TestCase):
 
     def setUp(self):
         self.parser = DebugArghParser('PROG')
-        for namespace, commands in self.commands.iteritems():
+        for namespace, commands in iteritems(self.commands):
             self.parser.add_commands(commands, namespace=namespace)
 
     def _call_cmd(self, command_string, **kwargs):
-        if isinstance(command_string, basestring):
+        if isinstance(command_string, string_types):
             args = command_string.split()
         else:
             args = command_string
 
-        io = StringIO()
+        io = BytesIO()
         if 'output_file' not in kwargs:
             kwargs['output_file'] = io
 
         """
         try:
             result = self._call_cmd(command_string, **kwargs)
-        except SystemExit, error:
+        except SystemExit as error:
             self.fail('Argument parsing failed for {0!r}: {1!r}'.format(
                 command_string, error))
         self.assertEqual(result, expected_result)
 
     def assert_cmd_exits(self, command_string, message_regex=None):
         "When a command forces exit, it *may* fail, but may just print help."
-        message_regex = str(message_regex)  # make sure None -> "None"
+        message_regex = text_type(message_regex)  # make sure None -> "None"
         f = lambda: self.parser.dispatch(command_string.split())
         self.assertRaisesRegexp(SystemExit, message_regex, f)
 
         """(for cases when a commands doesn't fail but also (maybe) doesn't
         return results and just prints them.)
         """
-        result = self.assert_cmd_exits(command_string)
+        self.assert_cmd_exits(command_string)
 
 
 class ArghTestCase(BaseArghTestCase):
     def test_argv(self):
         _argv = sys.argv
         sys.argv = sys.argv[:1] + ['echo', 'hi there']
-        self.assert_cmd_returns(None, 'you said hi there\n')
+        self.assert_cmd_returns(None, b('you said hi there\n'))
         sys.argv = _argv
 
     def test_no_command(self):
 
     def test_echo(self):
         "A simple command is resolved to a function."
-        self.assert_cmd_returns('echo foo', 'you said foo\n')
+        self.assert_cmd_returns('echo foo', b('you said foo\n'))
 
     def test_bool_action(self):
         "Action `store_true`/`store_false` is inferred from default value."
-        self.assert_cmd_returns('echo --twice foo', 'you said fooyou said foo\n')
+        self.assert_cmd_returns('echo --twice foo', b('you said fooyou said foo\n'))
 
     def test_plain_signature(self):
         "Arguments can be passed to the function without a Namespace instance."
-        self.assert_cmd_returns('plain-echo bar', 'you said bar\n')
+        self.assert_cmd_returns('plain-echo bar', b('you said bar\n'))
 
     def test_bare_namespace(self):
         "A command can be resolved to a function, not a namespace."
 
     def test_namespaced_function(self):
         "A subcommand is resolved to a function."
-        self.assert_cmd_returns('greet hello', u'Hello world!\n')
-        self.assert_cmd_returns('greet hello --name=John', u'Hello John!\n')
+        self.assert_cmd_returns('greet hello', b('Hello world!\n'))
+        self.assert_cmd_returns('greet hello --name=John', b('Hello John!\n'))
         self.assert_cmd_fails('greet hello John', 'unrecognized arguments')
         self.assert_cmd_fails('greet howdy --name=John', 'too few arguments')
-        self.assert_cmd_returns('greet howdy John', u'Howdy John?\n')
+        self.assert_cmd_returns('greet howdy John', b('Howdy John?\n'))
 
     def test_alias(self):
-        self.assert_cmd_returns('aliased', 'ok\n')
+        self.assert_cmd_returns('aliased', b('ok\n'))
 
     def test_help_alias(self):
         self.assert_cmd_doesnt_fail('--help')
         """Positional arguments are resolved in the order in which the @arg
         decorators are defined.
         """
-        self.assert_cmd_returns('foo-bar foo bar', 'foo\nbar\n')
+        self.assert_cmd_returns('foo-bar foo bar', b('foo\nbar\n'))
 
     def test_raw_output(self):
         "If the raw_output flag is set, no extra whitespace is added"
-        self.assert_cmd_returns('foo-bar foo bar', 'foo\nbar\n')
-        self.assert_cmd_returns('foo-bar foo bar', 'foobar', raw_output=True)
+        self.assert_cmd_returns('foo-bar foo bar', b('foo\nbar\n'))
+        self.assert_cmd_returns('foo-bar foo bar', b('foobar'), raw_output=True)
 
     def test_output_file(self):
-        self.assert_cmd_returns('greet hello', 'Hello world!\n')
-        self.assert_cmd_returns('greet hello', 'Hello world!\n', output_file=None)
+        self.assert_cmd_returns('greet hello', b('Hello world!\n'))
+        self.assert_cmd_returns('greet hello', b('Hello world!\n'), output_file=None)
 
     def test_command_error(self):
-        self.assert_cmd_returns('whiner-plain', 'I feel depressed.\n')
-        self.assert_cmd_returns('whiner-iterable', 'Hello...\nI feel depressed.\n')
+        self.assert_cmd_returns('whiner-plain', b('I feel depressed.\n'))
+        self.assert_cmd_returns('whiner-iterable', b('Hello...\nI feel depressed.\n'))
 
     def test_custom_namespace(self):
         namespace = argparse.Namespace()
-        namespace.custom_value = "foo"
-        self.assert_cmd_returns('custom-namespace', 'foo\n',
+        namespace.custom_value = 'foo'
+        self.assert_cmd_returns('custom-namespace', b('foo\n'),
                                 namespace=namespace)
 
 
     def test_command_decorator(self):
         """The @command decorator creates arguments from function signature.
         """
-        self.assert_cmd_returns('command-deco', 'Hello\n')
-        self.assert_cmd_returns('command-deco --text=hi', 'hi\n')
+        self.assert_cmd_returns('command-deco', b('Hello\n'))
+        self.assert_cmd_returns('command-deco --text=hi', b('hi\n'))
 
     def test_regression_issue12(self):
         """Issue #12: @command was broken if there were more than one argument
         to begin with same character (i.e. short option names were inferred
         incorrectly).
         """
-        self.assert_cmd_returns('command-deco-issue12', 'foo 1, fox 2\n')
-        self.assert_cmd_returns('command-deco-issue12 --foo 3', 'foo 3, fox 2\n')
-        self.assert_cmd_returns('command-deco-issue12 --fox 3', 'foo 1, fox 3\n')
+        self.assert_cmd_returns('command-deco-issue12', b('foo 1, fox 2\n'))
+        self.assert_cmd_returns('command-deco-issue12 --foo 3', b('foo 3, fox 2\n'))
+        self.assert_cmd_returns('command-deco-issue12 --fox 3', b('foo 1, fox 3\n'))
         self.assert_cmd_fails('command-deco-issue12 -f 3', 'unrecognized')
 
 
         self.assertRaisesRegexp(AssertionError, 'Do it yourself', f)
 
     def test_error_wrapped(self):
-        self.parser.dispatch(['strict-hello-smart', 'John'])
-        self.assert_cmd_returns('strict-hello-smart John', 'Do it yourself\n')
-        self.assert_cmd_returns('strict-hello-smart world', 'Hello world\n')
+        self.assert_cmd_returns('strict-hello-smart John', b('Do it yourself\n'))
+        self.assert_cmd_returns('strict-hello-smart world', b('Hello world\n'))
 
 
 class NoCommandsTestCase(BaseArghTestCase):
     "Edge case: no commands defined"
     commands = {}
     def test_no_command(self):
-        self.assert_cmd_returns('', self.parser.format_usage(), raw_output=True)
-        self.assert_cmd_returns('', self.parser.format_usage()+'\n')
+        self.assert_cmd_returns('', b(self.parser.format_usage()), raw_output=True)
+        self.assert_cmd_returns('', b(self.parser.format_usage()+'\n'))
 
 
 class ConfirmTestCase(unittest.TestCase):
         argh.helpers.raw_input = raw_input_mock
 
         argh.confirm('do smth')
-        self.assertEqual(prompts[-1], 'do smth? (y/n)')
+        self.assertEqual(prompts[-1], b('do smth? (y/n)'))
 
         argh.confirm('do smth', default=None)
-        self.assertEqual(prompts[-1], 'do smth? (y/n)')
+        self.assertEqual(prompts[-1], b('do smth? (y/n)'))
 
         argh.confirm('do smth', default=True)
-        self.assertEqual(prompts[-1], 'do smth? (Y/n)')
+        self.assertEqual(prompts[-1], b('do smth? (Y/n)'))
 
         argh.confirm('do smth', default=False)
-        self.assertEqual(prompts[-1], 'do smth? (y/N)')
+        self.assertEqual(prompts[-1], b('do smth? (y/N)'))
 
     def test_encoding(self):
         "Unicode and bytes are accepted as prompt message"
         def raw_input_mock(prompt):
-            assert isinstance(prompt, str)
+            assert isinstance(prompt, binary_type)
         argh.helpers.raw_input = raw_input_mock
-        argh.confirm(u'привет')
+        argh.confirm(u('привет'))
 
 
 class CompletionTestCase(unittest.TestCase):
+[tox]
+envlist=py26,py27,pypy,py32
+indexserver=
+default = http://pypi.python.org/simple
+
+[testenv]
+deps=nose
+     rednose
+     unittest2
+commands=nosetests
+
+[testenv:py26]
+deps=nose
+     rednose
+     unittest2
+     argparse
+
+[testenv:py32]
+deps=nose
+     rednose
+     unittest2py3k
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.