##// END OF EJS Templates
Merge branch 'dollar-escape'
Thomas Kluyver -
r5376:16b9fed8 merge
parent child Browse files
Show More
@@ -1,237 +1,240 b''
1 1 # -*- coding: utf-8 -*-
2 2 """Tests for the key interactiveshell module.
3 3
4 4 Historically the main classes in interactiveshell have been under-tested. This
5 5 module should grow as many single-method tests as possible to trap many of the
6 6 recurring bugs we seem to encounter with high-level interaction.
7 7
8 8 Authors
9 9 -------
10 10 * Fernando Perez
11 11 """
12 12 #-----------------------------------------------------------------------------
13 13 # Copyright (C) 2011 The IPython Development Team
14 14 #
15 15 # Distributed under the terms of the BSD License. The full license is in
16 16 # the file COPYING, distributed as part of this software.
17 17 #-----------------------------------------------------------------------------
18 18
19 19 #-----------------------------------------------------------------------------
20 20 # Imports
21 21 #-----------------------------------------------------------------------------
22 22 # stdlib
23 23 import os
24 24 import shutil
25 25 import tempfile
26 26 import unittest
27 27 from os.path import join
28 28 from StringIO import StringIO
29 29
30 30 from IPython.testing import decorators as dec
31 31 from IPython.utils import io
32 32
33 33 #-----------------------------------------------------------------------------
34 34 # Tests
35 35 #-----------------------------------------------------------------------------
36 36
37 37 class InteractiveShellTestCase(unittest.TestCase):
38 38 def test_naked_string_cells(self):
39 39 """Test that cells with only naked strings are fully executed"""
40 40 ip = get_ipython()
41 41 # First, single-line inputs
42 42 ip.run_cell('"a"\n')
43 43 self.assertEquals(ip.user_ns['_'], 'a')
44 44 # And also multi-line cells
45 45 ip.run_cell('"""a\nb"""\n')
46 46 self.assertEquals(ip.user_ns['_'], 'a\nb')
47 47
48 48 def test_run_empty_cell(self):
49 49 """Just make sure we don't get a horrible error with a blank
50 50 cell of input. Yes, I did overlook that."""
51 51 ip = get_ipython()
52 52 old_xc = ip.execution_count
53 53 ip.run_cell('')
54 54 self.assertEquals(ip.execution_count, old_xc)
55 55
56 56 def test_run_cell_multiline(self):
57 57 """Multi-block, multi-line cells must execute correctly.
58 58 """
59 59 ip = get_ipython()
60 60 src = '\n'.join(["x=1",
61 61 "y=2",
62 62 "if 1:",
63 63 " x += 1",
64 64 " y += 1",])
65 65 ip.run_cell(src)
66 66 self.assertEquals(ip.user_ns['x'], 2)
67 67 self.assertEquals(ip.user_ns['y'], 3)
68 68
69 69 def test_multiline_string_cells(self):
70 70 "Code sprinkled with multiline strings should execute (GH-306)"
71 71 ip = get_ipython()
72 72 ip.run_cell('tmp=0')
73 73 self.assertEquals(ip.user_ns['tmp'], 0)
74 74 ip.run_cell('tmp=1;"""a\nb"""\n')
75 75 self.assertEquals(ip.user_ns['tmp'], 1)
76 76
77 77 def test_dont_cache_with_semicolon(self):
78 78 "Ending a line with semicolon should not cache the returned object (GH-307)"
79 79 ip = get_ipython()
80 80 oldlen = len(ip.user_ns['Out'])
81 81 a = ip.run_cell('1;', store_history=True)
82 82 newlen = len(ip.user_ns['Out'])
83 83 self.assertEquals(oldlen, newlen)
84 84 #also test the default caching behavior
85 85 ip.run_cell('1', store_history=True)
86 86 newlen = len(ip.user_ns['Out'])
87 87 self.assertEquals(oldlen+1, newlen)
88 88
89 89 def test_In_variable(self):
90 90 "Verify that In variable grows with user input (GH-284)"
91 91 ip = get_ipython()
92 92 oldlen = len(ip.user_ns['In'])
93 93 ip.run_cell('1;', store_history=True)
94 94 newlen = len(ip.user_ns['In'])
95 95 self.assertEquals(oldlen+1, newlen)
96 96 self.assertEquals(ip.user_ns['In'][-1],'1;')
97 97
98 98 def test_magic_names_in_string(self):
99 99 ip = get_ipython()
100 100 ip.run_cell('a = """\n%exit\n"""')
101 101 self.assertEquals(ip.user_ns['a'], '\n%exit\n')
102 102
103 103 def test_alias_crash(self):
104 104 """Errors in prefilter can't crash IPython"""
105 105 ip = get_ipython()
106 106 ip.run_cell('%alias parts echo first %s second %s')
107 107 # capture stderr:
108 108 save_err = io.stderr
109 109 io.stderr = StringIO()
110 110 ip.run_cell('parts 1')
111 111 err = io.stderr.getvalue()
112 112 io.stderr = save_err
113 113 self.assertEquals(err.split(':')[0], 'ERROR')
114 114
115 115 def test_trailing_newline(self):
116 116 """test that running !(command) does not raise a SyntaxError"""
117 117 ip = get_ipython()
118 118 ip.run_cell('!(true)\n', False)
119 119 ip.run_cell('!(true)\n\n\n', False)
120 120
121 121 def test_gh_597(self):
122 122 """Pretty-printing lists of objects with non-ascii reprs may cause
123 123 problems."""
124 124 class Spam(object):
125 125 def __repr__(self):
126 126 return "\xe9"*50
127 127 import IPython.core.formatters
128 128 f = IPython.core.formatters.PlainTextFormatter()
129 129 f([Spam(),Spam()])
130 130
131 131 def test_future_flags(self):
132 132 """Check that future flags are used for parsing code (gh-777)"""
133 133 ip = get_ipython()
134 134 ip.run_cell('from __future__ import print_function')
135 135 try:
136 136 ip.run_cell('prfunc_return_val = print(1,2, sep=" ")')
137 137 assert 'prfunc_return_val' in ip.user_ns
138 138 finally:
139 139 # Reset compiler flags so we don't mess up other tests.
140 140 ip.compile.reset_compiler_flags()
141 141
142 142 def test_future_unicode(self):
143 143 """Check that unicode_literals is imported from __future__ (gh #786)"""
144 144 ip = get_ipython()
145 145 try:
146 146 ip.run_cell(u'byte_str = "a"')
147 147 assert isinstance(ip.user_ns['byte_str'], str) # string literals are byte strings by default
148 148 ip.run_cell('from __future__ import unicode_literals')
149 149 ip.run_cell(u'unicode_str = "a"')
150 150 assert isinstance(ip.user_ns['unicode_str'], unicode) # strings literals are now unicode
151 151 finally:
152 152 # Reset compiler flags so we don't mess up other tests.
153 153 ip.compile.reset_compiler_flags()
154 154
155 155 def test_bad_custom_tb(self):
156 156 """Check that InteractiveShell is protected from bad custom exception handlers"""
157 157 ip = get_ipython()
158 158 from IPython.utils import io
159 159 save_stderr = io.stderr
160 160 try:
161 161 # capture stderr
162 162 io.stderr = StringIO()
163 163 ip.set_custom_exc((IOError,), lambda etype,value,tb: 1/0)
164 164 self.assertEquals(ip.custom_exceptions, (IOError,))
165 165 ip.run_cell(u'raise IOError("foo")')
166 166 self.assertEquals(ip.custom_exceptions, ())
167 167 self.assertTrue("Custom TB Handler failed" in io.stderr.getvalue())
168 168 finally:
169 169 io.stderr = save_stderr
170 170
171 171 def test_bad_custom_tb_return(self):
172 172 """Check that InteractiveShell is protected from bad return types in custom exception handlers"""
173 173 ip = get_ipython()
174 174 from IPython.utils import io
175 175 save_stderr = io.stderr
176 176 try:
177 177 # capture stderr
178 178 io.stderr = StringIO()
179 179 ip.set_custom_exc((NameError,),lambda etype,value,tb, tb_offset=None: 1)
180 180 self.assertEquals(ip.custom_exceptions, (NameError,))
181 181 ip.run_cell(u'a=abracadabra')
182 182 self.assertEquals(ip.custom_exceptions, ())
183 183 self.assertTrue("Custom TB Handler failed" in io.stderr.getvalue())
184 184 finally:
185 185 io.stderr = save_stderr
186 186
187 187 def test_drop_by_id(self):
188 188 ip = get_ipython()
189 189 myvars = {"a":object(), "b":object(), "c": object()}
190 190 ip.push(myvars, interactive=False)
191 191 for name in myvars:
192 192 assert name in ip.user_ns, name
193 193 assert name in ip.user_ns_hidden, name
194 194 ip.user_ns['b'] = 12
195 195 ip.drop_by_id(myvars)
196 196 for name in ["a", "c"]:
197 197 assert name not in ip.user_ns, name
198 198 assert name not in ip.user_ns_hidden, name
199 199 assert ip.user_ns['b'] == 12
200 200 ip.reset()
201 201
202 202 def test_var_expand(self):
203 203 ip = get_ipython()
204 ip.user_ns['f'] = 'Ca\xc3\xb1o'
204 ip.user_ns['f'] = u'Ca\xf1o'
205 self.assertEqual(ip.var_expand(u'echo $f'), u'echo Ca\xf1o')
206
207 ip.user_ns['f'] = b'Ca\xc3\xb1o'
205 208 # This should not raise any exception:
206 209 ip.var_expand(u'echo $f')
207 210
208 211
209 212 class TestSafeExecfileNonAsciiPath(unittest.TestCase):
210 213
211 214 def setUp(self):
212 215 self.BASETESTDIR = tempfile.mkdtemp()
213 216 self.TESTDIR = join(self.BASETESTDIR, u"Γ₯Àâ")
214 217 os.mkdir(self.TESTDIR)
215 218 with open(join(self.TESTDIR, u"Γ₯Àâtestscript.py"), "w") as sfile:
216 219 sfile.write("pass\n")
217 220 self.oldpath = os.getcwdu()
218 221 os.chdir(self.TESTDIR)
219 222 self.fname = u"Γ₯Àâtestscript.py"
220 223
221 224
222 225 def tearDown(self):
223 226 os.chdir(self.oldpath)
224 227 shutil.rmtree(self.BASETESTDIR)
225 228
226 229 def test_1(self):
227 230 """Test safe_execfile with non-ascii path
228 231 """
229 232 _ip.shell.safe_execfile(self.fname, {}, raise_exceptions=True)
230 233
231 234
232 235 class TestSystemRaw(unittest.TestCase):
233 236 def test_1(self):
234 237 """Test system_raw with non-ascii cmd
235 238 """
236 239 cmd = ur'''python -c "'Γ₯Àâ'" '''
237 240 _ip.shell.system_raw(cmd)
@@ -1,109 +1,113 b''
1 1 # encoding: utf-8
2 2 """Tests for IPython.utils.text"""
3 3
4 4 #-----------------------------------------------------------------------------
5 5 # Copyright (C) 2011 The IPython Development Team
6 6 #
7 7 # Distributed under the terms of the BSD License. The full license is in
8 8 # the file COPYING, distributed as part of this software.
9 9 #-----------------------------------------------------------------------------
10 10
11 11 #-----------------------------------------------------------------------------
12 12 # Imports
13 13 #-----------------------------------------------------------------------------
14 14
15 15 import os
16 16 import math
17 17
18 18 import nose.tools as nt
19 19
20 20 from nose import with_setup
21 21
22 22 from IPython.testing import decorators as dec
23 23 from IPython.utils import text
24 24
25 25 #-----------------------------------------------------------------------------
26 26 # Globals
27 27 #-----------------------------------------------------------------------------
28 28
29 29 def test_columnize():
30 30 """Basic columnize tests."""
31 31 size = 5
32 32 items = [l*size for l in 'abc']
33 33 out = text.columnize(items, displaywidth=80)
34 34 nt.assert_equals(out, 'aaaaa bbbbb ccccc\n')
35 35 out = text.columnize(items, displaywidth=10)
36 36 nt.assert_equals(out, 'aaaaa ccccc\nbbbbb\n')
37 37
38 38
39 39 def test_columnize_long():
40 40 """Test columnize with inputs longer than the display window"""
41 41 text.columnize(['a'*81, 'b'*81], displaywidth=80)
42 42 size = 11
43 43 items = [l*size for l in 'abc']
44 44 out = text.columnize(items, displaywidth=size-1)
45 45 nt.assert_equals(out, '\n'.join(items+['']))
46 46
47 47 def eval_formatter_check(f):
48 48 ns = dict(n=12, pi=math.pi, stuff='hello there', os=os, u=u"cafΓ©", b="cafΓ©")
49 49 s = f.format("{n} {n//4} {stuff.split()[0]}", **ns)
50 50 nt.assert_equals(s, "12 3 hello")
51 51 s = f.format(' '.join(['{n//%i}'%i for i in range(1,8)]), **ns)
52 52 nt.assert_equals(s, "12 6 4 3 2 2 1")
53 53 s = f.format('{[n//i for i in range(1,8)]}', **ns)
54 54 nt.assert_equals(s, "[12, 6, 4, 3, 2, 2, 1]")
55 55 s = f.format("{stuff!s}", **ns)
56 56 nt.assert_equals(s, ns['stuff'])
57 57 s = f.format("{stuff!r}", **ns)
58 58 nt.assert_equals(s, repr(ns['stuff']))
59 59
60 60 # Check with unicode:
61 61 s = f.format("{u}", **ns)
62 62 nt.assert_equals(s, ns['u'])
63 63 # This decodes in a platform dependent manner, but it shouldn't error out
64 64 s = f.format("{b}", **ns)
65 65
66 66 nt.assert_raises(NameError, f.format, '{dne}', **ns)
67 67
68 68 def eval_formatter_slicing_check(f):
69 69 ns = dict(n=12, pi=math.pi, stuff='hello there', os=os)
70 70 s = f.format(" {stuff.split()[:]} ", **ns)
71 71 nt.assert_equals(s, " ['hello', 'there'] ")
72 72 s = f.format(" {stuff.split()[::-1]} ", **ns)
73 73 nt.assert_equals(s, " ['there', 'hello'] ")
74 74 s = f.format("{stuff[::2]}", **ns)
75 75 nt.assert_equals(s, ns['stuff'][::2])
76 76
77 77 nt.assert_raises(SyntaxError, f.format, "{n:x}", **ns)
78 78
79 79
80 80 def eval_formatter_no_slicing_check(f):
81 81 ns = dict(n=12, pi=math.pi, stuff='hello there', os=os)
82 82
83 83 s = f.format('{n:x} {pi**2:+f}', **ns)
84 84 nt.assert_equals(s, "c +9.869604")
85 85
86 86 nt.assert_raises(SyntaxError, f.format, "{a[:]}")
87 87
88 88 def test_eval_formatter():
89 89 f = text.EvalFormatter()
90 90 eval_formatter_check(f)
91 91 eval_formatter_no_slicing_check(f)
92 92
93 93 def test_full_eval_formatter():
94 94 f = text.FullEvalFormatter()
95 95 eval_formatter_check(f)
96 96 eval_formatter_slicing_check(f)
97 97
98 98 def test_dollar_formatter():
99 99 f = text.DollarFormatter()
100 100 eval_formatter_check(f)
101 101 eval_formatter_slicing_check(f)
102 102
103 103 ns = dict(n=12, pi=math.pi, stuff='hello there', os=os)
104 104 s = f.format("$n", **ns)
105 105 nt.assert_equals(s, "12")
106 106 s = f.format("$n.real", **ns)
107 107 nt.assert_equals(s, "12")
108 108 s = f.format("$n/{stuff[:5]}", **ns)
109 109 nt.assert_equals(s, "12/hello")
110 s = f.format("$n $$HOME", **ns)
111 nt.assert_equals(s, "12 $HOME")
112 s = f.format("${foo}", foo="HOME")
113 nt.assert_equals(s, "$HOME")
@@ -1,754 +1,760 b''
1 1 # encoding: utf-8
2 2 """
3 3 Utilities for working with strings and text.
4 4 """
5 5
6 6 #-----------------------------------------------------------------------------
7 7 # Copyright (C) 2008-2009 The IPython Development Team
8 8 #
9 9 # Distributed under the terms of the BSD License. The full license is in
10 10 # the file COPYING, distributed as part of this software.
11 11 #-----------------------------------------------------------------------------
12 12
13 13 #-----------------------------------------------------------------------------
14 14 # Imports
15 15 #-----------------------------------------------------------------------------
16 16
17 17 import __main__
18 18
19 19 import locale
20 20 import os
21 21 import re
22 22 import shutil
23 23 import sys
24 24 import textwrap
25 25 from string import Formatter
26 26
27 27 from IPython.external.path import path
28 28 from IPython.testing.skipdoctest import skip_doctest_py3
29 29 from IPython.utils import py3compat
30 30 from IPython.utils.io import nlprint
31 31 from IPython.utils.data import flatten
32 32
33 33 #-----------------------------------------------------------------------------
34 34 # Code
35 35 #-----------------------------------------------------------------------------
36 36
37 37 # Less conservative replacement for sys.getdefaultencoding, that will try
38 38 # to match the environment.
39 39 # Defined here as central function, so if we find better choices, we
40 40 # won't need to make changes all over IPython.
41 41 def getdefaultencoding():
42 42 """Return IPython's guess for the default encoding for bytes as text.
43 43
44 44 Asks for stdin.encoding first, to match the calling Terminal, but that
45 45 is often None for subprocesses. Fall back on locale.getpreferredencoding()
46 46 which should be a sensible platform default (that respects LANG environment),
47 47 and finally to sys.getdefaultencoding() which is the most conservative option,
48 48 and usually ASCII.
49 49 """
50 50 enc = sys.stdin.encoding
51 51 if not enc or enc=='ascii':
52 52 try:
53 53 # There are reports of getpreferredencoding raising errors
54 54 # in some cases, which may well be fixed, but let's be conservative here.
55 55 enc = locale.getpreferredencoding()
56 56 except Exception:
57 57 pass
58 58 return enc or sys.getdefaultencoding()
59 59
60 60 def unquote_ends(istr):
61 61 """Remove a single pair of quotes from the endpoints of a string."""
62 62
63 63 if not istr:
64 64 return istr
65 65 if (istr[0]=="'" and istr[-1]=="'") or \
66 66 (istr[0]=='"' and istr[-1]=='"'):
67 67 return istr[1:-1]
68 68 else:
69 69 return istr
70 70
71 71
72 72 class LSString(str):
73 73 """String derivative with a special access attributes.
74 74
75 75 These are normal strings, but with the special attributes:
76 76
77 77 .l (or .list) : value as list (split on newlines).
78 78 .n (or .nlstr): original value (the string itself).
79 79 .s (or .spstr): value as whitespace-separated string.
80 80 .p (or .paths): list of path objects
81 81
82 82 Any values which require transformations are computed only once and
83 83 cached.
84 84
85 85 Such strings are very useful to efficiently interact with the shell, which
86 86 typically only understands whitespace-separated options for commands."""
87 87
88 88 def get_list(self):
89 89 try:
90 90 return self.__list
91 91 except AttributeError:
92 92 self.__list = self.split('\n')
93 93 return self.__list
94 94
95 95 l = list = property(get_list)
96 96
97 97 def get_spstr(self):
98 98 try:
99 99 return self.__spstr
100 100 except AttributeError:
101 101 self.__spstr = self.replace('\n',' ')
102 102 return self.__spstr
103 103
104 104 s = spstr = property(get_spstr)
105 105
106 106 def get_nlstr(self):
107 107 return self
108 108
109 109 n = nlstr = property(get_nlstr)
110 110
111 111 def get_paths(self):
112 112 try:
113 113 return self.__paths
114 114 except AttributeError:
115 115 self.__paths = [path(p) for p in self.split('\n') if os.path.exists(p)]
116 116 return self.__paths
117 117
118 118 p = paths = property(get_paths)
119 119
120 120 # FIXME: We need to reimplement type specific displayhook and then add this
121 121 # back as a custom printer. This should also be moved outside utils into the
122 122 # core.
123 123
124 124 # def print_lsstring(arg):
125 125 # """ Prettier (non-repr-like) and more informative printer for LSString """
126 126 # print "LSString (.p, .n, .l, .s available). Value:"
127 127 # print arg
128 128 #
129 129 #
130 130 # print_lsstring = result_display.when_type(LSString)(print_lsstring)
131 131
132 132
133 133 class SList(list):
134 134 """List derivative with a special access attributes.
135 135
136 136 These are normal lists, but with the special attributes:
137 137
138 138 .l (or .list) : value as list (the list itself).
139 139 .n (or .nlstr): value as a string, joined on newlines.
140 140 .s (or .spstr): value as a string, joined on spaces.
141 141 .p (or .paths): list of path objects
142 142
143 143 Any values which require transformations are computed only once and
144 144 cached."""
145 145
146 146 def get_list(self):
147 147 return self
148 148
149 149 l = list = property(get_list)
150 150
151 151 def get_spstr(self):
152 152 try:
153 153 return self.__spstr
154 154 except AttributeError:
155 155 self.__spstr = ' '.join(self)
156 156 return self.__spstr
157 157
158 158 s = spstr = property(get_spstr)
159 159
160 160 def get_nlstr(self):
161 161 try:
162 162 return self.__nlstr
163 163 except AttributeError:
164 164 self.__nlstr = '\n'.join(self)
165 165 return self.__nlstr
166 166
167 167 n = nlstr = property(get_nlstr)
168 168
169 169 def get_paths(self):
170 170 try:
171 171 return self.__paths
172 172 except AttributeError:
173 173 self.__paths = [path(p) for p in self if os.path.exists(p)]
174 174 return self.__paths
175 175
176 176 p = paths = property(get_paths)
177 177
178 178 def grep(self, pattern, prune = False, field = None):
179 179 """ Return all strings matching 'pattern' (a regex or callable)
180 180
181 181 This is case-insensitive. If prune is true, return all items
182 182 NOT matching the pattern.
183 183
184 184 If field is specified, the match must occur in the specified
185 185 whitespace-separated field.
186 186
187 187 Examples::
188 188
189 189 a.grep( lambda x: x.startswith('C') )
190 190 a.grep('Cha.*log', prune=1)
191 191 a.grep('chm', field=-1)
192 192 """
193 193
194 194 def match_target(s):
195 195 if field is None:
196 196 return s
197 197 parts = s.split()
198 198 try:
199 199 tgt = parts[field]
200 200 return tgt
201 201 except IndexError:
202 202 return ""
203 203
204 204 if isinstance(pattern, basestring):
205 205 pred = lambda x : re.search(pattern, x, re.IGNORECASE)
206 206 else:
207 207 pred = pattern
208 208 if not prune:
209 209 return SList([el for el in self if pred(match_target(el))])
210 210 else:
211 211 return SList([el for el in self if not pred(match_target(el))])
212 212
213 213 def fields(self, *fields):
214 214 """ Collect whitespace-separated fields from string list
215 215
216 216 Allows quick awk-like usage of string lists.
217 217
218 218 Example data (in var a, created by 'a = !ls -l')::
219 219 -rwxrwxrwx 1 ville None 18 Dec 14 2006 ChangeLog
220 220 drwxrwxrwx+ 6 ville None 0 Oct 24 18:05 IPython
221 221
222 222 a.fields(0) is ['-rwxrwxrwx', 'drwxrwxrwx+']
223 223 a.fields(1,0) is ['1 -rwxrwxrwx', '6 drwxrwxrwx+']
224 224 (note the joining by space).
225 225 a.fields(-1) is ['ChangeLog', 'IPython']
226 226
227 227 IndexErrors are ignored.
228 228
229 229 Without args, fields() just split()'s the strings.
230 230 """
231 231 if len(fields) == 0:
232 232 return [el.split() for el in self]
233 233
234 234 res = SList()
235 235 for el in [f.split() for f in self]:
236 236 lineparts = []
237 237
238 238 for fd in fields:
239 239 try:
240 240 lineparts.append(el[fd])
241 241 except IndexError:
242 242 pass
243 243 if lineparts:
244 244 res.append(" ".join(lineparts))
245 245
246 246 return res
247 247
248 248 def sort(self,field= None, nums = False):
249 249 """ sort by specified fields (see fields())
250 250
251 251 Example::
252 252 a.sort(1, nums = True)
253 253
254 254 Sorts a by second field, in numerical order (so that 21 > 3)
255 255
256 256 """
257 257
258 258 #decorate, sort, undecorate
259 259 if field is not None:
260 260 dsu = [[SList([line]).fields(field), line] for line in self]
261 261 else:
262 262 dsu = [[line, line] for line in self]
263 263 if nums:
264 264 for i in range(len(dsu)):
265 265 numstr = "".join([ch for ch in dsu[i][0] if ch.isdigit()])
266 266 try:
267 267 n = int(numstr)
268 268 except ValueError:
269 269 n = 0;
270 270 dsu[i][0] = n
271 271
272 272
273 273 dsu.sort()
274 274 return SList([t[1] for t in dsu])
275 275
276 276
277 277 # FIXME: We need to reimplement type specific displayhook and then add this
278 278 # back as a custom printer. This should also be moved outside utils into the
279 279 # core.
280 280
281 281 # def print_slist(arg):
282 282 # """ Prettier (non-repr-like) and more informative printer for SList """
283 283 # print "SList (.p, .n, .l, .s, .grep(), .fields(), sort() available):"
284 284 # if hasattr(arg, 'hideonce') and arg.hideonce:
285 285 # arg.hideonce = False
286 286 # return
287 287 #
288 288 # nlprint(arg)
289 289 #
290 290 # print_slist = result_display.when_type(SList)(print_slist)
291 291
292 292
293 293 def esc_quotes(strng):
294 294 """Return the input string with single and double quotes escaped out"""
295 295
296 296 return strng.replace('"','\\"').replace("'","\\'")
297 297
298 298
299 299 def qw(words,flat=0,sep=None,maxsplit=-1):
300 300 """Similar to Perl's qw() operator, but with some more options.
301 301
302 302 qw(words,flat=0,sep=' ',maxsplit=-1) -> words.split(sep,maxsplit)
303 303
304 304 words can also be a list itself, and with flat=1, the output will be
305 305 recursively flattened.
306 306
307 307 Examples:
308 308
309 309 >>> qw('1 2')
310 310 ['1', '2']
311 311
312 312 >>> qw(['a b','1 2',['m n','p q']])
313 313 [['a', 'b'], ['1', '2'], [['m', 'n'], ['p', 'q']]]
314 314
315 315 >>> qw(['a b','1 2',['m n','p q']],flat=1)
316 316 ['a', 'b', '1', '2', 'm', 'n', 'p', 'q']
317 317 """
318 318
319 319 if isinstance(words, basestring):
320 320 return [word.strip() for word in words.split(sep,maxsplit)
321 321 if word and not word.isspace() ]
322 322 if flat:
323 323 return flatten(map(qw,words,[1]*len(words)))
324 324 return map(qw,words)
325 325
326 326
327 327 def qwflat(words,sep=None,maxsplit=-1):
328 328 """Calls qw(words) in flat mode. It's just a convenient shorthand."""
329 329 return qw(words,1,sep,maxsplit)
330 330
331 331
332 332 def qw_lol(indata):
333 333 """qw_lol('a b') -> [['a','b']],
334 334 otherwise it's just a call to qw().
335 335
336 336 We need this to make sure the modules_some keys *always* end up as a
337 337 list of lists."""
338 338
339 339 if isinstance(indata, basestring):
340 340 return [qw(indata)]
341 341 else:
342 342 return qw(indata)
343 343
344 344
345 345 def grep(pat,list,case=1):
346 346 """Simple minded grep-like function.
347 347 grep(pat,list) returns occurrences of pat in list, None on failure.
348 348
349 349 It only does simple string matching, with no support for regexps. Use the
350 350 option case=0 for case-insensitive matching."""
351 351
352 352 # This is pretty crude. At least it should implement copying only references
353 353 # to the original data in case it's big. Now it copies the data for output.
354 354 out=[]
355 355 if case:
356 356 for term in list:
357 357 if term.find(pat)>-1: out.append(term)
358 358 else:
359 359 lpat=pat.lower()
360 360 for term in list:
361 361 if term.lower().find(lpat)>-1: out.append(term)
362 362
363 363 if len(out): return out
364 364 else: return None
365 365
366 366
367 367 def dgrep(pat,*opts):
368 368 """Return grep() on dir()+dir(__builtins__).
369 369
370 370 A very common use of grep() when working interactively."""
371 371
372 372 return grep(pat,dir(__main__)+dir(__main__.__builtins__),*opts)
373 373
374 374
375 375 def idgrep(pat):
376 376 """Case-insensitive dgrep()"""
377 377
378 378 return dgrep(pat,0)
379 379
380 380
381 381 def igrep(pat,list):
382 382 """Synonym for case-insensitive grep."""
383 383
384 384 return grep(pat,list,case=0)
385 385
386 386
387 387 def indent(instr,nspaces=4, ntabs=0, flatten=False):
388 388 """Indent a string a given number of spaces or tabstops.
389 389
390 390 indent(str,nspaces=4,ntabs=0) -> indent str by ntabs+nspaces.
391 391
392 392 Parameters
393 393 ----------
394 394
395 395 instr : basestring
396 396 The string to be indented.
397 397 nspaces : int (default: 4)
398 398 The number of spaces to be indented.
399 399 ntabs : int (default: 0)
400 400 The number of tabs to be indented.
401 401 flatten : bool (default: False)
402 402 Whether to scrub existing indentation. If True, all lines will be
403 403 aligned to the same indentation. If False, existing indentation will
404 404 be strictly increased.
405 405
406 406 Returns
407 407 -------
408 408
409 409 str|unicode : string indented by ntabs and nspaces.
410 410
411 411 """
412 412 if instr is None:
413 413 return
414 414 ind = '\t'*ntabs+' '*nspaces
415 415 if flatten:
416 416 pat = re.compile(r'^\s*', re.MULTILINE)
417 417 else:
418 418 pat = re.compile(r'^', re.MULTILINE)
419 419 outstr = re.sub(pat, ind, instr)
420 420 if outstr.endswith(os.linesep+ind):
421 421 return outstr[:-len(ind)]
422 422 else:
423 423 return outstr
424 424
425 425 def native_line_ends(filename,backup=1):
426 426 """Convert (in-place) a file to line-ends native to the current OS.
427 427
428 428 If the optional backup argument is given as false, no backup of the
429 429 original file is left. """
430 430
431 431 backup_suffixes = {'posix':'~','dos':'.bak','nt':'.bak','mac':'.bak'}
432 432
433 433 bak_filename = filename + backup_suffixes[os.name]
434 434
435 435 original = open(filename).read()
436 436 shutil.copy2(filename,bak_filename)
437 437 try:
438 438 new = open(filename,'wb')
439 439 new.write(os.linesep.join(original.splitlines()))
440 440 new.write(os.linesep) # ALWAYS put an eol at the end of the file
441 441 new.close()
442 442 except:
443 443 os.rename(bak_filename,filename)
444 444 if not backup:
445 445 try:
446 446 os.remove(bak_filename)
447 447 except:
448 448 pass
449 449
450 450
451 451 def list_strings(arg):
452 452 """Always return a list of strings, given a string or list of strings
453 453 as input.
454 454
455 455 :Examples:
456 456
457 457 In [7]: list_strings('A single string')
458 458 Out[7]: ['A single string']
459 459
460 460 In [8]: list_strings(['A single string in a list'])
461 461 Out[8]: ['A single string in a list']
462 462
463 463 In [9]: list_strings(['A','list','of','strings'])
464 464 Out[9]: ['A', 'list', 'of', 'strings']
465 465 """
466 466
467 467 if isinstance(arg,basestring): return [arg]
468 468 else: return arg
469 469
470 470
471 471 def marquee(txt='',width=78,mark='*'):
472 472 """Return the input string centered in a 'marquee'.
473 473
474 474 :Examples:
475 475
476 476 In [16]: marquee('A test',40)
477 477 Out[16]: '**************** A test ****************'
478 478
479 479 In [17]: marquee('A test',40,'-')
480 480 Out[17]: '---------------- A test ----------------'
481 481
482 482 In [18]: marquee('A test',40,' ')
483 483 Out[18]: ' A test '
484 484
485 485 """
486 486 if not txt:
487 487 return (mark*width)[:width]
488 488 nmark = (width-len(txt)-2)//len(mark)//2
489 489 if nmark < 0: nmark =0
490 490 marks = mark*nmark
491 491 return '%s %s %s' % (marks,txt,marks)
492 492
493 493
494 494 ini_spaces_re = re.compile(r'^(\s+)')
495 495
496 496 def num_ini_spaces(strng):
497 497 """Return the number of initial spaces in a string"""
498 498
499 499 ini_spaces = ini_spaces_re.match(strng)
500 500 if ini_spaces:
501 501 return ini_spaces.end()
502 502 else:
503 503 return 0
504 504
505 505
506 506 def format_screen(strng):
507 507 """Format a string for screen printing.
508 508
509 509 This removes some latex-type format codes."""
510 510 # Paragraph continue
511 511 par_re = re.compile(r'\\$',re.MULTILINE)
512 512 strng = par_re.sub('',strng)
513 513 return strng
514 514
515 515 def dedent(text):
516 516 """Equivalent of textwrap.dedent that ignores unindented first line.
517 517
518 518 This means it will still dedent strings like:
519 519 '''foo
520 520 is a bar
521 521 '''
522 522
523 523 For use in wrap_paragraphs.
524 524 """
525 525
526 526 if text.startswith('\n'):
527 527 # text starts with blank line, don't ignore the first line
528 528 return textwrap.dedent(text)
529 529
530 530 # split first line
531 531 splits = text.split('\n',1)
532 532 if len(splits) == 1:
533 533 # only one line
534 534 return textwrap.dedent(text)
535 535
536 536 first, rest = splits
537 537 # dedent everything but the first line
538 538 rest = textwrap.dedent(rest)
539 539 return '\n'.join([first, rest])
540 540
541 541 def wrap_paragraphs(text, ncols=80):
542 542 """Wrap multiple paragraphs to fit a specified width.
543 543
544 544 This is equivalent to textwrap.wrap, but with support for multiple
545 545 paragraphs, as separated by empty lines.
546 546
547 547 Returns
548 548 -------
549 549
550 550 list of complete paragraphs, wrapped to fill `ncols` columns.
551 551 """
552 552 paragraph_re = re.compile(r'\n(\s*\n)+', re.MULTILINE)
553 553 text = dedent(text).strip()
554 554 paragraphs = paragraph_re.split(text)[::2] # every other entry is space
555 555 out_ps = []
556 556 indent_re = re.compile(r'\n\s+', re.MULTILINE)
557 557 for p in paragraphs:
558 558 # presume indentation that survives dedent is meaningful formatting,
559 559 # so don't fill unless text is flush.
560 560 if indent_re.search(p) is None:
561 561 # wrap paragraph
562 562 p = textwrap.fill(p, ncols)
563 563 out_ps.append(p)
564 564 return out_ps
565 565
566 566
567 567 class EvalFormatter(Formatter):
568 568 """A String Formatter that allows evaluation of simple expressions.
569 569
570 570 Note that this version interprets a : as specifying a format string (as per
571 571 standard string formatting), so if slicing is required, you must explicitly
572 572 create a slice.
573 573
574 574 This is to be used in templating cases, such as the parallel batch
575 575 script templates, where simple arithmetic on arguments is useful.
576 576
577 577 Examples
578 578 --------
579 579
580 580 In [1]: f = EvalFormatter()
581 581 In [2]: f.format('{n//4}', n=8)
582 582 Out [2]: '2'
583 583
584 584 In [3]: f.format("{greeting[slice(2,4)]}", greeting="Hello")
585 585 Out [3]: 'll'
586 586 """
587 587 def get_field(self, name, args, kwargs):
588 588 v = eval(name, kwargs)
589 589 return v, name
590 590
591 591 @skip_doctest_py3
592 592 class FullEvalFormatter(Formatter):
593 593 """A String Formatter that allows evaluation of simple expressions.
594 594
595 595 Any time a format key is not found in the kwargs,
596 596 it will be tried as an expression in the kwargs namespace.
597 597
598 598 Note that this version allows slicing using [1:2], so you cannot specify
599 599 a format string. Use :class:`EvalFormatter` to permit format strings.
600 600
601 601 Examples
602 602 --------
603 603
604 604 In [1]: f = FullEvalFormatter()
605 605 In [2]: f.format('{n//4}', n=8)
606 606 Out[2]: u'2'
607 607
608 608 In [3]: f.format('{list(range(5))[2:4]}')
609 609 Out[3]: u'[2, 3]'
610 610
611 611 In [4]: f.format('{3*2}')
612 612 Out[4]: u'6'
613 613 """
614 614 # copied from Formatter._vformat with minor changes to allow eval
615 615 # and replace the format_spec code with slicing
616 616 def _vformat(self, format_string, args, kwargs, used_args, recursion_depth):
617 617 if recursion_depth < 0:
618 618 raise ValueError('Max string recursion exceeded')
619 619 result = []
620 620 for literal_text, field_name, format_spec, conversion in \
621 621 self.parse(format_string):
622 622
623 623 # output the literal text
624 624 if literal_text:
625 625 result.append(literal_text)
626 626
627 627 # if there's a field, output it
628 628 if field_name is not None:
629 629 # this is some markup, find the object and do
630 630 # the formatting
631 631
632 632 if format_spec:
633 633 # override format spec, to allow slicing:
634 634 field_name = ':'.join([field_name, format_spec])
635 635
636 636 # eval the contents of the field for the object
637 637 # to be formatted
638 638 obj = eval(field_name, kwargs)
639 639
640 640 # do any conversion on the resulting object
641 641 obj = self.convert_field(obj, conversion)
642 642
643 643 # format the object and append to the result
644 644 result.append(self.format_field(obj, ''))
645 645
646 646 return u''.join(py3compat.cast_unicode(s) for s in result)
647 647
648 648 @skip_doctest_py3
649 649 class DollarFormatter(FullEvalFormatter):
650 650 """Formatter allowing Itpl style $foo replacement, for names and attribute
651 651 access only. Standard {foo} replacement also works, and allows full
652 652 evaluation of its arguments.
653 653
654 654 Examples
655 655 --------
656 656 In [1]: f = DollarFormatter()
657 657 In [2]: f.format('{n//4}', n=8)
658 658 Out[2]: u'2'
659 659
660 660 In [3]: f.format('23 * 76 is $result', result=23*76)
661 661 Out[3]: u'23 * 76 is 1748'
662 662
663 663 In [4]: f.format('$a or {b}', a=1, b=2)
664 664 Out[4]: u'1 or 2'
665 665 """
666 _dollar_pattern = re.compile("(.*)\$([\w\.]+)")
666 _dollar_pattern = re.compile("(.*?)\$(\$?[\w\.]+)")
667 667 def parse(self, fmt_string):
668 668 for literal_txt, field_name, format_spec, conversion \
669 669 in Formatter.parse(self, fmt_string):
670 670
671 671 # Find $foo patterns in the literal text.
672 672 continue_from = 0
673 txt = ""
673 674 for m in self._dollar_pattern.finditer(literal_txt):
674 675 new_txt, new_field = m.group(1,2)
675 yield (new_txt, new_field, "", None)
676 # $$foo --> $foo
677 if new_field.startswith("$"):
678 txt += new_txt + new_field
679 else:
680 yield (txt + new_txt, new_field, "", None)
681 txt = ""
676 682 continue_from = m.end()
677 683
678 684 # Re-yield the {foo} style pattern
679 yield (literal_txt[continue_from:], field_name, format_spec, conversion)
685 yield (txt + literal_txt[continue_from:], field_name, format_spec, conversion)
680 686
681 687
682 688 def columnize(items, separator=' ', displaywidth=80):
683 689 """ Transform a list of strings into a single string with columns.
684 690
685 691 Parameters
686 692 ----------
687 693 items : sequence of strings
688 694 The strings to process.
689 695
690 696 separator : str, optional [default is two spaces]
691 697 The string that separates columns.
692 698
693 699 displaywidth : int, optional [default is 80]
694 700 Width of the display in number of characters.
695 701
696 702 Returns
697 703 -------
698 704 The formatted string.
699 705 """
700 706 # Note: this code is adapted from columnize 0.3.2.
701 707 # See http://code.google.com/p/pycolumnize/
702 708
703 709 # Some degenerate cases.
704 710 size = len(items)
705 711 if size == 0:
706 712 return '\n'
707 713 elif size == 1:
708 714 return '%s\n' % items[0]
709 715
710 716 # Special case: if any item is longer than the maximum width, there's no
711 717 # point in triggering the logic below...
712 718 item_len = map(len, items) # save these, we can reuse them below
713 719 longest = max(item_len)
714 720 if longest >= displaywidth:
715 721 return '\n'.join(items+[''])
716 722
717 723 # Try every row count from 1 upwards
718 724 array_index = lambda nrows, row, col: nrows*col + row
719 725 for nrows in range(1, size):
720 726 ncols = (size + nrows - 1) // nrows
721 727 colwidths = []
722 728 totwidth = -len(separator)
723 729 for col in range(ncols):
724 730 # Get max column width for this column
725 731 colwidth = 0
726 732 for row in range(nrows):
727 733 i = array_index(nrows, row, col)
728 734 if i >= size: break
729 735 x, len_x = items[i], item_len[i]
730 736 colwidth = max(colwidth, len_x)
731 737 colwidths.append(colwidth)
732 738 totwidth += colwidth + len(separator)
733 739 if totwidth > displaywidth:
734 740 break
735 741 if totwidth <= displaywidth:
736 742 break
737 743
738 744 # The smallest number of rows computed and the max widths for each
739 745 # column has been obtained. Now we just have to format each of the rows.
740 746 string = ''
741 747 for row in range(nrows):
742 748 texts = []
743 749 for col in range(ncols):
744 750 i = row + nrows*col
745 751 if i >= size:
746 752 texts.append('')
747 753 else:
748 754 texts.append(items[i])
749 755 while texts and not texts[-1]:
750 756 del texts[-1]
751 757 for col in range(len(texts)):
752 758 texts[col] = texts[col].ljust(colwidths[col])
753 759 string += '%s\n' % separator.join(texts)
754 760 return string
General Comments 0
You need to be logged in to leave comments. Login now