##// END OF EJS Templates
Merge branch 'rkern-magic-arguments'
Fernando Perez -
r3433:2fdab06a merge
parent child Browse files
Show More
@@ -0,0 +1,217 b''
1 ''' A decorator-based method of constructing IPython magics with `argparse`
2 option handling.
3
4 New magic functions can be defined like so::
5
6 from IPython.core.magic_arguments import (argument, magic_arguments,
7 parse_argstring)
8
9 @magic_arguments()
10 @argument('-o', '--option', help='An optional argument.')
11 @argument('arg', type=int, help='An integer positional argument.')
12 def magic_cool(self, arg):
13 """ A really cool magic command.
14
15 """
16 args = parse_argstring(magic_cool, arg)
17 ...
18
19 The `@magic_arguments` decorator marks the function as having argparse arguments.
20 The `@argument` decorator adds an argument using the same syntax as argparse's
21 `add_argument()` method. More sophisticated uses may also require the
22 `@argument_group` or `@kwds` decorator to customize the formatting and the
23 parsing.
24
25 Help text for the magic is automatically generated from the docstring and the
26 arguments::
27
28 In[1]: %cool?
29 %cool [-o OPTION] arg
30
31 A really cool magic command.
32
33 positional arguments:
34 arg An integer positional argument.
35
36 optional arguments:
37 -o OPTION, --option OPTION
38 An optional argument.
39
40 '''
41 #-----------------------------------------------------------------------------
42 # Copyright (c) 2010, IPython Development Team.
43 #
44 # Distributed under the terms of the Modified BSD License.
45 #
46 # The full license is in the file COPYING.txt, distributed with this software.
47 #-----------------------------------------------------------------------------
48
49 # Our own imports
50 from IPython.external import argparse
51 from IPython.core.error import UsageError
52 from IPython.utils.process import arg_split
53
54
55 class MagicArgumentParser(argparse.ArgumentParser):
56 """ An ArgumentParser tweaked for use by IPython magics.
57 """
58 def __init__(self,
59 prog=None,
60 usage=None,
61 description=None,
62 epilog=None,
63 version=None,
64 parents=None,
65 formatter_class=argparse.HelpFormatter,
66 prefix_chars='-',
67 argument_default=None,
68 conflict_handler='error',
69 add_help=False):
70 if parents is None:
71 parents = []
72 super(MagicArgumentParser, self).__init__(prog=prog, usage=usage,
73 description=description, epilog=epilog, version=version,
74 parents=parents, formatter_class=formatter_class,
75 prefix_chars=prefix_chars, argument_default=argument_default,
76 conflict_handler=conflict_handler, add_help=add_help)
77
78 def error(self, message):
79 """ Raise a catchable error instead of exiting.
80 """
81 raise UsageError(message)
82
83 def parse_argstring(self, argstring):
84 """ Split a string into an argument list and parse that argument list.
85 """
86 argv = arg_split(argstring)
87 return self.parse_args(argv)
88
89
90 def construct_parser(magic_func):
91 """ Construct an argument parser using the function decorations.
92 """
93 kwds = getattr(magic_func, 'argcmd_kwds', {})
94 if 'description' not in kwds:
95 kwds['description'] = getattr(magic_func, '__doc__', None)
96 arg_name = real_name(magic_func)
97 parser = MagicArgumentParser(arg_name, **kwds)
98 # Reverse the list of decorators in order to apply them in the
99 # order in which they appear in the source.
100 group = None
101 for deco in magic_func.decorators[::-1]:
102 result = deco.add_to_parser(parser, group)
103 if result is not None:
104 group = result
105
106 # Replace the starting 'usage: ' with IPython's %.
107 help_text = parser.format_help()
108 if help_text.startswith('usage: '):
109 help_text = help_text.replace('usage: ', '%', 1)
110 else:
111 help_text = '%' + help_text
112
113 # Replace the magic function's docstring with the full help text.
114 magic_func.__doc__ = help_text
115
116 return parser
117
118
119 def parse_argstring(magic_func, argstring):
120 """ Parse the string of arguments for the given magic function.
121 """
122 return magic_func.parser.parse_argstring(argstring)
123
124
125 def real_name(magic_func):
126 """ Find the real name of the magic.
127 """
128 magic_name = magic_func.__name__
129 if magic_name.startswith('magic_'):
130 magic_name = magic_name[len('magic_'):]
131 return getattr(magic_func, 'argcmd_name', magic_name)
132
133
134 class ArgDecorator(object):
135 """ Base class for decorators to add ArgumentParser information to a method.
136 """
137
138 def __call__(self, func):
139 if not getattr(func, 'has_arguments', False):
140 func.has_arguments = True
141 func.decorators = []
142 func.decorators.append(self)
143 return func
144
145 def add_to_parser(self, parser, group):
146 """ Add this object's information to the parser, if necessary.
147 """
148 pass
149
150
151 class magic_arguments(ArgDecorator):
152 """ Mark the magic as having argparse arguments and possibly adjust the
153 name.
154 """
155
156 def __init__(self, name=None):
157 self.name = name
158
159 def __call__(self, func):
160 if not getattr(func, 'has_arguments', False):
161 func.has_arguments = True
162 func.decorators = []
163 if self.name is not None:
164 func.argcmd_name = self.name
165 # This should be the first decorator in the list of decorators, thus the
166 # last to execute. Build the parser.
167 func.parser = construct_parser(func)
168 return func
169
170
171 class argument(ArgDecorator):
172 """ Store arguments and keywords to pass to add_argument().
173
174 Instances also serve to decorate command methods.
175 """
176 def __init__(self, *args, **kwds):
177 self.args = args
178 self.kwds = kwds
179
180 def add_to_parser(self, parser, group):
181 """ Add this object's information to the parser.
182 """
183 if group is not None:
184 parser = group
185 parser.add_argument(*self.args, **self.kwds)
186 return None
187
188
189 class argument_group(ArgDecorator):
190 """ Store arguments and keywords to pass to add_argument_group().
191
192 Instances also serve to decorate command methods.
193 """
194 def __init__(self, *args, **kwds):
195 self.args = args
196 self.kwds = kwds
197
198 def add_to_parser(self, parser, group):
199 """ Add this object's information to the parser.
200 """
201 return parser.add_argument_group(*self.args, **self.kwds)
202
203
204 class kwds(ArgDecorator):
205 """ Provide other keywords to the sub-parser constructor.
206 """
207 def __init__(self, **kwds):
208 self.kwds = kwds
209
210 def __call__(self, func):
211 func = super(kwds, self).__call__(func)
212 func.argcmd_kwds = self.kwds
213 return func
214
215
216 __all__ = ['magic_arguments', 'argument', 'argument_group', 'kwds',
217 'parse_argstring']
@@ -0,0 +1,121 b''
1 #-----------------------------------------------------------------------------
2 # Copyright (c) 2010, IPython Development Team.
3 #
4 # Distributed under the terms of the Modified BSD License.
5 #
6 # The full license is in the file COPYING.txt, distributed with this software.
7 #-----------------------------------------------------------------------------
8
9 from nose.tools import assert_equal, assert_true
10
11 from IPython.external import argparse
12 from IPython.core.magic_arguments import (argument, argument_group, kwds,
13 magic_arguments, parse_argstring, real_name)
14 from IPython.testing.decorators import parametric
15
16
17 @magic_arguments()
18 @argument('-f', '--foo', help="an argument")
19 def magic_foo1(self, args):
20 """ A docstring.
21 """
22 return parse_argstring(magic_foo1, args)
23
24
25 @magic_arguments()
26 def magic_foo2(self, args):
27 """ A docstring.
28 """
29 return parse_argstring(magic_foo2, args)
30
31
32 @magic_arguments()
33 @argument('-f', '--foo', help="an argument")
34 @argument_group('Group')
35 @argument('-b', '--bar', help="a grouped argument")
36 @argument_group('Second Group')
37 @argument('-z', '--baz', help="another grouped argument")
38 def magic_foo3(self, args):
39 """ A docstring.
40 """
41 return parse_argstring(magic_foo3, args)
42
43
44 @magic_arguments()
45 @kwds(argument_default=argparse.SUPPRESS)
46 @argument('-f', '--foo', help="an argument")
47 def magic_foo4(self, args):
48 """ A docstring.
49 """
50 return parse_argstring(magic_foo4, args)
51
52
53 @magic_arguments('frobnicate')
54 @argument('-f', '--foo', help="an argument")
55 def magic_foo5(self, args):
56 """ A docstring.
57 """
58 return parse_argstring(magic_foo5, args)
59
60
61 @magic_arguments()
62 @argument('-f', '--foo', help="an argument")
63 def magic_magic_foo(self, args):
64 """ A docstring.
65 """
66 return parse_argstring(magic_magic_foo, args)
67
68
69 @magic_arguments()
70 @argument('-f', '--foo', help="an argument")
71 def foo(self, args):
72 """ A docstring.
73 """
74 return parse_argstring(foo, args)
75
76
77 @parametric
78 def test_magic_arguments():
79 # Ideally, these would be doctests, but I could not get it to work.
80 yield assert_equal(magic_foo1.__doc__, '%foo1 [-f FOO]\n\nA docstring.\n\noptional arguments:\n -f FOO, --foo FOO an argument\n')
81 yield assert_equal(getattr(magic_foo1, 'argcmd_name', None), None)
82 yield assert_equal(real_name(magic_foo1), 'foo1')
83 yield assert_equal(magic_foo1(None, ''), argparse.Namespace(foo=None))
84 yield assert_true(hasattr(magic_foo1, 'has_arguments'))
85
86 yield assert_equal(magic_foo2.__doc__, '%foo2\n\nA docstring.\n')
87 yield assert_equal(getattr(magic_foo2, 'argcmd_name', None), None)
88 yield assert_equal(real_name(magic_foo2), 'foo2')
89 yield assert_equal(magic_foo2(None, ''), argparse.Namespace())
90 yield assert_true(hasattr(magic_foo2, 'has_arguments'))
91
92 yield assert_equal(magic_foo3.__doc__, '%foo3 [-f FOO] [-b BAR] [-z BAZ]\n\nA docstring.\n\noptional arguments:\n -f FOO, --foo FOO an argument\n\nGroup:\n -b BAR, --bar BAR a grouped argument\n\nSecond Group:\n -z BAZ, --baz BAZ another grouped argument\n')
93 yield assert_equal(getattr(magic_foo3, 'argcmd_name', None), None)
94 yield assert_equal(real_name(magic_foo3), 'foo3')
95 yield assert_equal(magic_foo3(None, ''),
96 argparse.Namespace(bar=None, baz=None, foo=None))
97 yield assert_true(hasattr(magic_foo3, 'has_arguments'))
98
99 yield assert_equal(magic_foo4.__doc__, '%foo4 [-f FOO]\n\nA docstring.\n\noptional arguments:\n -f FOO, --foo FOO an argument\n')
100 yield assert_equal(getattr(magic_foo4, 'argcmd_name', None), None)
101 yield assert_equal(real_name(magic_foo4), 'foo4')
102 yield assert_equal(magic_foo4(None, ''), argparse.Namespace())
103 yield assert_true(hasattr(magic_foo4, 'has_arguments'))
104
105 yield assert_equal(magic_foo5.__doc__, '%frobnicate [-f FOO]\n\nA docstring.\n\noptional arguments:\n -f FOO, --foo FOO an argument\n')
106 yield assert_equal(getattr(magic_foo5, 'argcmd_name', None), 'frobnicate')
107 yield assert_equal(real_name(magic_foo5), 'frobnicate')
108 yield assert_equal(magic_foo5(None, ''), argparse.Namespace(foo=None))
109 yield assert_true(hasattr(magic_foo5, 'has_arguments'))
110
111 yield assert_equal(magic_magic_foo.__doc__, '%magic_foo [-f FOO]\n\nA docstring.\n\noptional arguments:\n -f FOO, --foo FOO an argument\n')
112 yield assert_equal(getattr(magic_magic_foo, 'argcmd_name', None), None)
113 yield assert_equal(real_name(magic_magic_foo), 'magic_foo')
114 yield assert_equal(magic_magic_foo(None, ''), argparse.Namespace(foo=None))
115 yield assert_true(hasattr(magic_magic_foo, 'has_arguments'))
116
117 yield assert_equal(foo.__doc__, '%foo [-f FOO]\n\nA docstring.\n\noptional arguments:\n -f FOO, --foo FOO an argument\n')
118 yield assert_equal(getattr(foo, 'argcmd_name', None), None)
119 yield assert_equal(real_name(foo), 'foo')
120 yield assert_equal(foo(None, ''), argparse.Namespace(foo=None))
121 yield assert_true(hasattr(foo, 'has_arguments'))
@@ -1,140 +1,146 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 """
2 """
3 Utilities for working with external processes.
3 Utilities for working with external processes.
4 """
4 """
5
5
6 #-----------------------------------------------------------------------------
6 #-----------------------------------------------------------------------------
7 # Copyright (C) 2008-2009 The IPython Development Team
7 # Copyright (C) 2008-2009 The IPython Development Team
8 #
8 #
9 # Distributed under the terms of the BSD License. The full license is in
9 # Distributed under the terms of the BSD License. The full license is in
10 # the file COPYING, distributed as part of this software.
10 # the file COPYING, distributed as part of this software.
11 #-----------------------------------------------------------------------------
11 #-----------------------------------------------------------------------------
12
12
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14 # Imports
14 # Imports
15 #-----------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
16 from __future__ import print_function
16 from __future__ import print_function
17
17
18 # Stdlib
18 # Stdlib
19 import os
19 import os
20 import sys
20 import sys
21 import shlex
21 import shlex
22
22
23 # Our own
23 # Our own
24 if sys.platform == 'win32':
24 if sys.platform == 'win32':
25 from ._process_win32 import _find_cmd, system, getoutput, AvoidUNCPath
25 from ._process_win32 import _find_cmd, system, getoutput, AvoidUNCPath
26 else:
26 else:
27 from ._process_posix import _find_cmd, system, getoutput
27 from ._process_posix import _find_cmd, system, getoutput
28
28
29 from ._process_common import getoutputerror
29 from ._process_common import getoutputerror
30
30
31 #-----------------------------------------------------------------------------
31 #-----------------------------------------------------------------------------
32 # Code
32 # Code
33 #-----------------------------------------------------------------------------
33 #-----------------------------------------------------------------------------
34
34
35
35
36 class FindCmdError(Exception):
36 class FindCmdError(Exception):
37 pass
37 pass
38
38
39
39
40 def find_cmd(cmd):
40 def find_cmd(cmd):
41 """Find absolute path to executable cmd in a cross platform manner.
41 """Find absolute path to executable cmd in a cross platform manner.
42
42
43 This function tries to determine the full path to a command line program
43 This function tries to determine the full path to a command line program
44 using `which` on Unix/Linux/OS X and `win32api` on Windows. Most of the
44 using `which` on Unix/Linux/OS X and `win32api` on Windows. Most of the
45 time it will use the version that is first on the users `PATH`. If
45 time it will use the version that is first on the users `PATH`. If
46 cmd is `python` return `sys.executable`.
46 cmd is `python` return `sys.executable`.
47
47
48 Warning, don't use this to find IPython command line programs as there
48 Warning, don't use this to find IPython command line programs as there
49 is a risk you will find the wrong one. Instead find those using the
49 is a risk you will find the wrong one. Instead find those using the
50 following code and looking for the application itself::
50 following code and looking for the application itself::
51
51
52 from IPython.utils.path import get_ipython_module_path
52 from IPython.utils.path import get_ipython_module_path
53 from IPython.utils.process import pycmd2argv
53 from IPython.utils.process import pycmd2argv
54 argv = pycmd2argv(get_ipython_module_path('IPython.frontend.terminal.ipapp'))
54 argv = pycmd2argv(get_ipython_module_path('IPython.frontend.terminal.ipapp'))
55
55
56 Parameters
56 Parameters
57 ----------
57 ----------
58 cmd : str
58 cmd : str
59 The command line program to look for.
59 The command line program to look for.
60 """
60 """
61 if cmd == 'python':
61 if cmd == 'python':
62 return os.path.abspath(sys.executable)
62 return os.path.abspath(sys.executable)
63 try:
63 try:
64 path = _find_cmd(cmd).rstrip()
64 path = _find_cmd(cmd).rstrip()
65 except OSError:
65 except OSError:
66 raise FindCmdError('command could not be found: %s' % cmd)
66 raise FindCmdError('command could not be found: %s' % cmd)
67 # which returns empty if not found
67 # which returns empty if not found
68 if path == '':
68 if path == '':
69 raise FindCmdError('command could not be found: %s' % cmd)
69 raise FindCmdError('command could not be found: %s' % cmd)
70 return os.path.abspath(path)
70 return os.path.abspath(path)
71
71
72
72
73 def pycmd2argv(cmd):
73 def pycmd2argv(cmd):
74 r"""Take the path of a python command and return a list (argv-style).
74 r"""Take the path of a python command and return a list (argv-style).
75
75
76 This only works on Python based command line programs and will find the
76 This only works on Python based command line programs and will find the
77 location of the ``python`` executable using ``sys.executable`` to make
77 location of the ``python`` executable using ``sys.executable`` to make
78 sure the right version is used.
78 sure the right version is used.
79
79
80 For a given path ``cmd``, this returns [cmd] if cmd's extension is .exe,
80 For a given path ``cmd``, this returns [cmd] if cmd's extension is .exe,
81 .com or .bat, and [, cmd] otherwise.
81 .com or .bat, and [, cmd] otherwise.
82
82
83 Parameters
83 Parameters
84 ----------
84 ----------
85 cmd : string
85 cmd : string
86 The path of the command.
86 The path of the command.
87
87
88 Returns
88 Returns
89 -------
89 -------
90 argv-style list.
90 argv-style list.
91 """
91 """
92 ext = os.path.splitext(cmd)[1]
92 ext = os.path.splitext(cmd)[1]
93 if ext in ['.exe', '.com', '.bat']:
93 if ext in ['.exe', '.com', '.bat']:
94 return [cmd]
94 return [cmd]
95 else:
95 else:
96 if sys.platform == 'win32':
96 if sys.platform == 'win32':
97 # The -u option here turns on unbuffered output, which is required
97 # The -u option here turns on unbuffered output, which is required
98 # on Win32 to prevent wierd conflict and problems with Twisted.
98 # on Win32 to prevent wierd conflict and problems with Twisted.
99 # Also, use sys.executable to make sure we are picking up the
99 # Also, use sys.executable to make sure we are picking up the
100 # right python exe.
100 # right python exe.
101 return [sys.executable, '-u', cmd]
101 return [sys.executable, '-u', cmd]
102 else:
102 else:
103 return [sys.executable, cmd]
103 return [sys.executable, cmd]
104
104
105
105
106 def arg_split(s, posix=False):
106 def arg_split(s, posix=False):
107 """Split a command line's arguments in a shell-like manner.
107 """Split a command line's arguments in a shell-like manner.
108
108
109 This is a modified version of the standard library's shlex.split()
109 This is a modified version of the standard library's shlex.split()
110 function, but with a default of posix=False for splitting, so that quotes
110 function, but with a default of posix=False for splitting, so that quotes
111 in inputs are respected."""
111 in inputs are respected."""
112
112
113 # Unfortunately, python's shlex module is buggy with unicode input:
113 # Unfortunately, python's shlex module is buggy with unicode input:
114 # http://bugs.python.org/issue1170
114 # http://bugs.python.org/issue1170
115 # At least encoding the input when it's unicode seems to help, but there
115 # At least encoding the input when it's unicode seems to help, but there
116 # may be more problems lurking. Apparently this is fixed in python3.
116 # may be more problems lurking. Apparently this is fixed in python3.
117 is_unicode = False
117 if isinstance(s, unicode):
118 if isinstance(s, unicode):
118 s = s.encode(sys.stdin.encoding)
119 is_unicode = True
120 s = s.encode('utf-8')
119 lex = shlex.shlex(s, posix=posix)
121 lex = shlex.shlex(s, posix=posix)
120 lex.whitespace_split = True
122 lex.whitespace_split = True
121 return list(lex)
123 tokens = list(lex)
124 if is_unicode:
125 # Convert the tokens back to unicode.
126 tokens = [x.decode('utf-8') for x in tokens]
127 return tokens
122
128
123
129
124 def abbrev_cwd():
130 def abbrev_cwd():
125 """ Return abbreviated version of cwd, e.g. d:mydir """
131 """ Return abbreviated version of cwd, e.g. d:mydir """
126 cwd = os.getcwd().replace('\\','/')
132 cwd = os.getcwd().replace('\\','/')
127 drivepart = ''
133 drivepart = ''
128 tail = cwd
134 tail = cwd
129 if sys.platform == 'win32':
135 if sys.platform == 'win32':
130 if len(cwd) < 4:
136 if len(cwd) < 4:
131 return cwd
137 return cwd
132 drivepart,tail = os.path.splitdrive(cwd)
138 drivepart,tail = os.path.splitdrive(cwd)
133
139
134
140
135 parts = tail.split('/')
141 parts = tail.split('/')
136 if len(parts) > 2:
142 if len(parts) > 2:
137 tail = '/'.join(parts[-2:])
143 tail = '/'.join(parts[-2:])
138
144
139 return (drivepart + (
145 return (drivepart + (
140 cwd == '/' and '/' or tail))
146 cwd == '/' and '/' or tail))
@@ -1,95 +1,98 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 """
2 """
3 Tests for platutils.py
3 Tests for platutils.py
4 """
4 """
5
5
6 #-----------------------------------------------------------------------------
6 #-----------------------------------------------------------------------------
7 # Copyright (C) 2008-2009 The IPython Development Team
7 # Copyright (C) 2008-2009 The IPython Development Team
8 #
8 #
9 # Distributed under the terms of the BSD License. The full license is in
9 # Distributed under the terms of the BSD License. The full license is in
10 # the file COPYING, distributed as part of this software.
10 # the file COPYING, distributed as part of this software.
11 #-----------------------------------------------------------------------------
11 #-----------------------------------------------------------------------------
12
12
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14 # Imports
14 # Imports
15 #-----------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
16
16
17 import sys
17 import sys
18 from unittest import TestCase
18 from unittest import TestCase
19
19
20 import nose.tools as nt
20 import nose.tools as nt
21
21
22 from IPython.utils.process import (find_cmd, FindCmdError, arg_split,
22 from IPython.utils.process import (find_cmd, FindCmdError, arg_split,
23 system, getoutput, getoutputerror)
23 system, getoutput, getoutputerror)
24 from IPython.testing import decorators as dec
24 from IPython.testing import decorators as dec
25 from IPython.testing import tools as tt
25 from IPython.testing import tools as tt
26
26
27 #-----------------------------------------------------------------------------
27 #-----------------------------------------------------------------------------
28 # Tests
28 # Tests
29 #-----------------------------------------------------------------------------
29 #-----------------------------------------------------------------------------
30
30
31 def test_find_cmd_python():
31 def test_find_cmd_python():
32 """Make sure we find sys.exectable for python."""
32 """Make sure we find sys.exectable for python."""
33 nt.assert_equals(find_cmd('python'), sys.executable)
33 nt.assert_equals(find_cmd('python'), sys.executable)
34
34
35
35
36 @dec.skip_win32
36 @dec.skip_win32
37 def test_find_cmd_ls():
37 def test_find_cmd_ls():
38 """Make sure we can find the full path to ls."""
38 """Make sure we can find the full path to ls."""
39 path = find_cmd('ls')
39 path = find_cmd('ls')
40 nt.assert_true(path.endswith('ls'))
40 nt.assert_true(path.endswith('ls'))
41
41
42
42
43 def has_pywin32():
43 def has_pywin32():
44 try:
44 try:
45 import win32api
45 import win32api
46 except ImportError:
46 except ImportError:
47 return False
47 return False
48 return True
48 return True
49
49
50
50
51 @dec.onlyif(has_pywin32, "This test requires win32api to run")
51 @dec.onlyif(has_pywin32, "This test requires win32api to run")
52 def test_find_cmd_pythonw():
52 def test_find_cmd_pythonw():
53 """Try to find pythonw on Windows."""
53 """Try to find pythonw on Windows."""
54 path = find_cmd('pythonw')
54 path = find_cmd('pythonw')
55 nt.assert_true(path.endswith('pythonw.exe'))
55 nt.assert_true(path.endswith('pythonw.exe'))
56
56
57
57
58 @dec.onlyif(lambda : sys.platform != 'win32' or has_pywin32(),
58 @dec.onlyif(lambda : sys.platform != 'win32' or has_pywin32(),
59 "This test runs on posix or in win32 with win32api installed")
59 "This test runs on posix or in win32 with win32api installed")
60 def test_find_cmd_fail():
60 def test_find_cmd_fail():
61 """Make sure that FindCmdError is raised if we can't find the cmd."""
61 """Make sure that FindCmdError is raised if we can't find the cmd."""
62 nt.assert_raises(FindCmdError,find_cmd,'asdfasdf')
62 nt.assert_raises(FindCmdError,find_cmd,'asdfasdf')
63
63
64
64
65 def test_arg_split():
65 def test_arg_split():
66 """Ensure that argument lines are correctly split like in a shell."""
66 """Ensure that argument lines are correctly split like in a shell."""
67 tests = [['hi', ['hi']],
67 tests = [['hi', ['hi']],
68 [u'hi', [u'hi']],
68 [u'hi', [u'hi']],
69 ['hello there', ['hello', 'there']],
70 [u'h\N{LATIN SMALL LETTER A WITH CARON}llo', [u'h\N{LATIN SMALL LETTER A WITH CARON}llo']],
71 ['something "with quotes"', ['something', '"with quotes"']],
69 ]
72 ]
70 for argstr, argv in tests:
73 for argstr, argv in tests:
71 nt.assert_equal(arg_split(argstr), argv)
74 nt.assert_equal(arg_split(argstr), argv)
72
75
73
76
74 class SubProcessTestCase(TestCase, tt.TempFileMixin):
77 class SubProcessTestCase(TestCase, tt.TempFileMixin):
75 def setUp(self):
78 def setUp(self):
76 """Make a valid python temp file."""
79 """Make a valid python temp file."""
77 lines = ["from __future__ import print_function",
80 lines = ["from __future__ import print_function",
78 "import sys",
81 "import sys",
79 "print('on stdout', end='', file=sys.stdout)",
82 "print('on stdout', end='', file=sys.stdout)",
80 "print('on stderr', end='', file=sys.stderr)",
83 "print('on stderr', end='', file=sys.stderr)",
81 "sys.stdout.flush()",
84 "sys.stdout.flush()",
82 "sys.stderr.flush()"]
85 "sys.stderr.flush()"]
83 self.mktmp('\n'.join(lines))
86 self.mktmp('\n'.join(lines))
84
87
85 def test_system(self):
88 def test_system(self):
86 system('python "%s"' % self.fname)
89 system('python "%s"' % self.fname)
87
90
88 def test_getoutput(self):
91 def test_getoutput(self):
89 out = getoutput('python "%s"' % self.fname)
92 out = getoutput('python "%s"' % self.fname)
90 self.assertEquals(out, 'on stdout')
93 self.assertEquals(out, 'on stdout')
91
94
92 def test_getoutput(self):
95 def test_getoutput(self):
93 out, err = getoutputerror('python "%s"' % self.fname)
96 out, err = getoutputerror('python "%s"' % self.fname)
94 self.assertEquals(out, 'on stdout')
97 self.assertEquals(out, 'on stdout')
95 self.assertEquals(err, 'on stderr')
98 self.assertEquals(err, 'on stderr')
General Comments 0
You need to be logged in to leave comments. Login now