##// END OF EJS Templates
use pathlib for utils.text.paths...
Min RK -
Show More
@@ -1,218 +1,222 b''
1 1 # encoding: utf-8
2 2 """Tests for IPython.utils.text"""
3 3 from __future__ import print_function
4 4
5 5 #-----------------------------------------------------------------------------
6 6 # Copyright (C) 2011 The IPython Development Team
7 7 #
8 8 # Distributed under the terms of the BSD License. The full license is in
9 9 # the file COPYING, distributed as part of this software.
10 10 #-----------------------------------------------------------------------------
11 11
12 12 #-----------------------------------------------------------------------------
13 13 # Imports
14 14 #-----------------------------------------------------------------------------
15 15
16 16 import os
17 17 import math
18 18 import random
19 19 import sys
20 20
21 21 import nose.tools as nt
22 import path
22 try:
23 from pathlib import Path
24 except ImportError:
25 # Python 2 backport
26 from pathlib2 import Path
23 27
24 28 from IPython.utils import text
25 29
26 30 #-----------------------------------------------------------------------------
27 31 # Globals
28 32 #-----------------------------------------------------------------------------
29 33
30 34 def test_columnize():
31 35 """Basic columnize tests."""
32 36 size = 5
33 37 items = [l*size for l in 'abcd']
34 38
35 39 out = text.columnize(items, displaywidth=80)
36 40 nt.assert_equal(out, 'aaaaa bbbbb ccccc ddddd\n')
37 41 out = text.columnize(items, displaywidth=25)
38 42 nt.assert_equal(out, 'aaaaa ccccc\nbbbbb ddddd\n')
39 43 out = text.columnize(items, displaywidth=12)
40 44 nt.assert_equal(out, 'aaaaa ccccc\nbbbbb ddddd\n')
41 45 out = text.columnize(items, displaywidth=10)
42 46 nt.assert_equal(out, 'aaaaa\nbbbbb\nccccc\nddddd\n')
43 47
44 48 out = text.columnize(items, row_first=True, displaywidth=80)
45 49 nt.assert_equal(out, 'aaaaa bbbbb ccccc ddddd\n')
46 50 out = text.columnize(items, row_first=True, displaywidth=25)
47 51 nt.assert_equal(out, 'aaaaa bbbbb\nccccc ddddd\n')
48 52 out = text.columnize(items, row_first=True, displaywidth=12)
49 53 nt.assert_equal(out, 'aaaaa bbbbb\nccccc ddddd\n')
50 54 out = text.columnize(items, row_first=True, displaywidth=10)
51 55 nt.assert_equal(out, 'aaaaa\nbbbbb\nccccc\nddddd\n')
52 56
53 57 out = text.columnize(items, displaywidth=40, spread=True)
54 58 nt.assert_equal(out, 'aaaaa bbbbb ccccc ddddd\n')
55 59 out = text.columnize(items, displaywidth=20, spread=True)
56 60 nt.assert_equal(out, 'aaaaa ccccc\nbbbbb ddddd\n')
57 61 out = text.columnize(items, displaywidth=12, spread=True)
58 62 nt.assert_equal(out, 'aaaaa ccccc\nbbbbb ddddd\n')
59 63 out = text.columnize(items, displaywidth=10, spread=True)
60 64 nt.assert_equal(out, 'aaaaa\nbbbbb\nccccc\nddddd\n')
61 65
62 66
63 67 def test_columnize_random():
64 68 """Test with random input to hopfully catch edge case """
65 69 for row_first in [True, False]:
66 70 for nitems in [random.randint(2,70) for i in range(2,20)]:
67 71 displaywidth = random.randint(20,200)
68 72 rand_len = [random.randint(2,displaywidth) for i in range(nitems)]
69 73 items = ['x'*l for l in rand_len]
70 74 out = text.columnize(items, row_first=row_first, displaywidth=displaywidth)
71 75 longer_line = max([len(x) for x in out.split('\n')])
72 76 longer_element = max(rand_len)
73 77 if longer_line > displaywidth:
74 78 print("Columnize displayed something lager than displaywidth : %s " % longer_line)
75 79 print("longer element : %s " % longer_element)
76 80 print("displaywidth : %s " % displaywidth)
77 81 print("number of element : %s " % nitems)
78 82 print("size of each element :\n %s" % rand_len)
79 83 assert False, "row_first={0}".format(row_first)
80 84
81 85 def test_columnize_medium():
82 86 """Test with inputs than shouldn't be wider than 80"""
83 87 size = 40
84 88 items = [l*size for l in 'abc']
85 89 for row_first in [True, False]:
86 90 out = text.columnize(items, row_first=row_first, displaywidth=80)
87 91 nt.assert_equal(out, '\n'.join(items+['']), "row_first={0}".format(row_first))
88 92
89 93 def test_columnize_long():
90 94 """Test columnize with inputs longer than the display window"""
91 95 size = 11
92 96 items = [l*size for l in 'abc']
93 97 for row_first in [True, False]:
94 98 out = text.columnize(items, row_first=row_first, displaywidth=size-1)
95 99 nt.assert_equal(out, '\n'.join(items+['']), "row_first={0}".format(row_first))
96 100
97 101 def eval_formatter_check(f):
98 102 ns = dict(n=12, pi=math.pi, stuff='hello there', os=os, u=u"cafΓ©", b="cafΓ©")
99 103 s = f.format("{n} {n//4} {stuff.split()[0]}", **ns)
100 104 nt.assert_equal(s, "12 3 hello")
101 105 s = f.format(' '.join(['{n//%i}'%i for i in range(1,8)]), **ns)
102 106 nt.assert_equal(s, "12 6 4 3 2 2 1")
103 107 s = f.format('{[n//i for i in range(1,8)]}', **ns)
104 108 nt.assert_equal(s, "[12, 6, 4, 3, 2, 2, 1]")
105 109 s = f.format("{stuff!s}", **ns)
106 110 nt.assert_equal(s, ns['stuff'])
107 111 s = f.format("{stuff!r}", **ns)
108 112 nt.assert_equal(s, repr(ns['stuff']))
109 113
110 114 # Check with unicode:
111 115 s = f.format("{u}", **ns)
112 116 nt.assert_equal(s, ns['u'])
113 117 # This decodes in a platform dependent manner, but it shouldn't error out
114 118 s = f.format("{b}", **ns)
115 119
116 120 nt.assert_raises(NameError, f.format, '{dne}', **ns)
117 121
118 122 def eval_formatter_slicing_check(f):
119 123 ns = dict(n=12, pi=math.pi, stuff='hello there', os=os)
120 124 s = f.format(" {stuff.split()[:]} ", **ns)
121 125 nt.assert_equal(s, " ['hello', 'there'] ")
122 126 s = f.format(" {stuff.split()[::-1]} ", **ns)
123 127 nt.assert_equal(s, " ['there', 'hello'] ")
124 128 s = f.format("{stuff[::2]}", **ns)
125 129 nt.assert_equal(s, ns['stuff'][::2])
126 130
127 131 nt.assert_raises(SyntaxError, f.format, "{n:x}", **ns)
128 132
129 133 def eval_formatter_no_slicing_check(f):
130 134 ns = dict(n=12, pi=math.pi, stuff='hello there', os=os)
131 135
132 136 s = f.format('{n:x} {pi**2:+f}', **ns)
133 137 nt.assert_equal(s, "c +9.869604")
134 138
135 139 s = f.format('{stuff[slice(1,4)]}', **ns)
136 140 nt.assert_equal(s, 'ell')
137 141
138 142 if sys.version_info >= (3, 4):
139 143 # String formatting has changed in Python 3.4, so this now works.
140 144 s = f.format("{a[:]}", a=[1, 2])
141 145 nt.assert_equal(s, "[1, 2]")
142 146 else:
143 147 nt.assert_raises(SyntaxError, f.format, "{a[:]}")
144 148
145 149 def test_eval_formatter():
146 150 f = text.EvalFormatter()
147 151 eval_formatter_check(f)
148 152 eval_formatter_no_slicing_check(f)
149 153
150 154 def test_full_eval_formatter():
151 155 f = text.FullEvalFormatter()
152 156 eval_formatter_check(f)
153 157 eval_formatter_slicing_check(f)
154 158
155 159 def test_dollar_formatter():
156 160 f = text.DollarFormatter()
157 161 eval_formatter_check(f)
158 162 eval_formatter_slicing_check(f)
159 163
160 164 ns = dict(n=12, pi=math.pi, stuff='hello there', os=os)
161 165 s = f.format("$n", **ns)
162 166 nt.assert_equal(s, "12")
163 167 s = f.format("$n.real", **ns)
164 168 nt.assert_equal(s, "12")
165 169 s = f.format("$n/{stuff[:5]}", **ns)
166 170 nt.assert_equal(s, "12/hello")
167 171 s = f.format("$n $$HOME", **ns)
168 172 nt.assert_equal(s, "12 $HOME")
169 173 s = f.format("${foo}", foo="HOME")
170 174 nt.assert_equal(s, "$HOME")
171 175
172 176
173 177 def test_long_substr():
174 178 data = ['hi']
175 179 nt.assert_equal(text.long_substr(data), 'hi')
176 180
177 181
178 182 def test_long_substr2():
179 183 data = ['abc', 'abd', 'abf', 'ab']
180 184 nt.assert_equal(text.long_substr(data), 'ab')
181 185
182 186 def test_long_substr_empty():
183 187 data = []
184 188 nt.assert_equal(text.long_substr(data), '')
185 189
186 190 def test_strip_email():
187 191 src = """\
188 192 >> >>> def f(x):
189 193 >> ... return x+1
190 194 >> ...
191 195 >> >>> zz = f(2.5)"""
192 196 cln = """\
193 197 >>> def f(x):
194 198 ... return x+1
195 199 ...
196 200 >>> zz = f(2.5)"""
197 201 nt.assert_equal(text.strip_email_quotes(src), cln)
198 202
199 203
200 204 def test_strip_email2():
201 205 src = '> > > list()'
202 206 cln = 'list()'
203 207 nt.assert_equal(text.strip_email_quotes(src), cln)
204 208
205 209 def test_LSString():
206 210 lss = text.LSString("abc\ndef")
207 211 nt.assert_equal(lss.l, ['abc', 'def'])
208 212 nt.assert_equal(lss.s, 'abc def')
209 213 lss = text.LSString(os.getcwd())
210 nt.assert_is_instance(lss.p[0], path.path)
214 nt.assert_is_instance(lss.p[0], Path)
211 215
212 216 def test_SList():
213 217 sl = text.SList(['a 11', 'b 1', 'a 2'])
214 218 nt.assert_equal(sl.n, 'a 11\nb 1\na 2')
215 219 nt.assert_equal(sl.s, 'a 11 b 1 a 2')
216 220 nt.assert_equal(sl.grep(lambda x: x.startswith('a')), text.SList(['a 11', 'a 2']))
217 221 nt.assert_equal(sl.fields(0), text.SList(['a', 'b', 'a']))
218 222 nt.assert_equal(sl.sort(field=1, nums=True), text.SList(['b 1', 'a 2', 'a 11']))
@@ -1,780 +1,783 b''
1 1 # encoding: utf-8
2 2 """
3 3 Utilities for working with strings and text.
4 4
5 5 Inheritance diagram:
6 6
7 7 .. inheritance-diagram:: IPython.utils.text
8 8 :parts: 3
9 9 """
10 10 from __future__ import absolute_import
11 11
12 12 import os
13 13 import re
14 14 import sys
15 15 import textwrap
16 16 from string import Formatter
17 try:
18 from pathlib import Path
19 except ImportError:
20 # Python 2 backport
21 from pathlib2 import Path
17 22
18 23 from IPython.testing.skipdoctest import skip_doctest_py3, skip_doctest
19 24 from IPython.utils import py3compat
20 25
21 26 # datetime.strftime date format for ipython
22 27 if sys.platform == 'win32':
23 28 date_format = "%B %d, %Y"
24 29 else:
25 30 date_format = "%B %-d, %Y"
26 31
27 32 class LSString(str):
28 33 """String derivative with a special access attributes.
29 34
30 35 These are normal strings, but with the special attributes:
31 36
32 37 .l (or .list) : value as list (split on newlines).
33 38 .n (or .nlstr): original value (the string itself).
34 39 .s (or .spstr): value as whitespace-separated string.
35 40 .p (or .paths): list of path objects (requires path.py package)
36 41
37 42 Any values which require transformations are computed only once and
38 43 cached.
39 44
40 45 Such strings are very useful to efficiently interact with the shell, which
41 46 typically only understands whitespace-separated options for commands."""
42 47
43 48 def get_list(self):
44 49 try:
45 50 return self.__list
46 51 except AttributeError:
47 52 self.__list = self.split('\n')
48 53 return self.__list
49 54
50 55 l = list = property(get_list)
51 56
52 57 def get_spstr(self):
53 58 try:
54 59 return self.__spstr
55 60 except AttributeError:
56 61 self.__spstr = self.replace('\n',' ')
57 62 return self.__spstr
58 63
59 64 s = spstr = property(get_spstr)
60 65
61 66 def get_nlstr(self):
62 67 return self
63 68
64 69 n = nlstr = property(get_nlstr)
65 70
66 71 def get_paths(self):
67 from path import path
68 72 try:
69 73 return self.__paths
70 74 except AttributeError:
71 self.__paths = [path(p) for p in self.split('\n') if os.path.exists(p)]
75 self.__paths = [Path(p) for p in self.split('\n') if os.path.exists(p)]
72 76 return self.__paths
73 77
74 78 p = paths = property(get_paths)
75 79
76 80 # FIXME: We need to reimplement type specific displayhook and then add this
77 81 # back as a custom printer. This should also be moved outside utils into the
78 82 # core.
79 83
80 84 # def print_lsstring(arg):
81 85 # """ Prettier (non-repr-like) and more informative printer for LSString """
82 86 # print "LSString (.p, .n, .l, .s available). Value:"
83 87 # print arg
84 88 #
85 89 #
86 90 # print_lsstring = result_display.when_type(LSString)(print_lsstring)
87 91
88 92
89 93 class SList(list):
90 94 """List derivative with a special access attributes.
91 95
92 96 These are normal lists, but with the special attributes:
93 97
94 98 * .l (or .list) : value as list (the list itself).
95 99 * .n (or .nlstr): value as a string, joined on newlines.
96 100 * .s (or .spstr): value as a string, joined on spaces.
97 101 * .p (or .paths): list of path objects (requires path.py package)
98 102
99 103 Any values which require transformations are computed only once and
100 104 cached."""
101 105
102 106 def get_list(self):
103 107 return self
104 108
105 109 l = list = property(get_list)
106 110
107 111 def get_spstr(self):
108 112 try:
109 113 return self.__spstr
110 114 except AttributeError:
111 115 self.__spstr = ' '.join(self)
112 116 return self.__spstr
113 117
114 118 s = spstr = property(get_spstr)
115 119
116 120 def get_nlstr(self):
117 121 try:
118 122 return self.__nlstr
119 123 except AttributeError:
120 124 self.__nlstr = '\n'.join(self)
121 125 return self.__nlstr
122 126
123 127 n = nlstr = property(get_nlstr)
124 128
125 129 def get_paths(self):
126 from path import path
127 130 try:
128 131 return self.__paths
129 132 except AttributeError:
130 self.__paths = [path(p) for p in self if os.path.exists(p)]
133 self.__paths = [Path(p) for p in self if os.path.exists(p)]
131 134 return self.__paths
132 135
133 136 p = paths = property(get_paths)
134 137
135 138 def grep(self, pattern, prune = False, field = None):
136 139 """ Return all strings matching 'pattern' (a regex or callable)
137 140
138 141 This is case-insensitive. If prune is true, return all items
139 142 NOT matching the pattern.
140 143
141 144 If field is specified, the match must occur in the specified
142 145 whitespace-separated field.
143 146
144 147 Examples::
145 148
146 149 a.grep( lambda x: x.startswith('C') )
147 150 a.grep('Cha.*log', prune=1)
148 151 a.grep('chm', field=-1)
149 152 """
150 153
151 154 def match_target(s):
152 155 if field is None:
153 156 return s
154 157 parts = s.split()
155 158 try:
156 159 tgt = parts[field]
157 160 return tgt
158 161 except IndexError:
159 162 return ""
160 163
161 164 if isinstance(pattern, py3compat.string_types):
162 165 pred = lambda x : re.search(pattern, x, re.IGNORECASE)
163 166 else:
164 167 pred = pattern
165 168 if not prune:
166 169 return SList([el for el in self if pred(match_target(el))])
167 170 else:
168 171 return SList([el for el in self if not pred(match_target(el))])
169 172
170 173 def fields(self, *fields):
171 174 """ Collect whitespace-separated fields from string list
172 175
173 176 Allows quick awk-like usage of string lists.
174 177
175 178 Example data (in var a, created by 'a = !ls -l')::
176 179
177 180 -rwxrwxrwx 1 ville None 18 Dec 14 2006 ChangeLog
178 181 drwxrwxrwx+ 6 ville None 0 Oct 24 18:05 IPython
179 182
180 183 * ``a.fields(0)`` is ``['-rwxrwxrwx', 'drwxrwxrwx+']``
181 184 * ``a.fields(1,0)`` is ``['1 -rwxrwxrwx', '6 drwxrwxrwx+']``
182 185 (note the joining by space).
183 186 * ``a.fields(-1)`` is ``['ChangeLog', 'IPython']``
184 187
185 188 IndexErrors are ignored.
186 189
187 190 Without args, fields() just split()'s the strings.
188 191 """
189 192 if len(fields) == 0:
190 193 return [el.split() for el in self]
191 194
192 195 res = SList()
193 196 for el in [f.split() for f in self]:
194 197 lineparts = []
195 198
196 199 for fd in fields:
197 200 try:
198 201 lineparts.append(el[fd])
199 202 except IndexError:
200 203 pass
201 204 if lineparts:
202 205 res.append(" ".join(lineparts))
203 206
204 207 return res
205 208
206 209 def sort(self,field= None, nums = False):
207 210 """ sort by specified fields (see fields())
208 211
209 212 Example::
210 213
211 214 a.sort(1, nums = True)
212 215
213 216 Sorts a by second field, in numerical order (so that 21 > 3)
214 217
215 218 """
216 219
217 220 #decorate, sort, undecorate
218 221 if field is not None:
219 222 dsu = [[SList([line]).fields(field), line] for line in self]
220 223 else:
221 224 dsu = [[line, line] for line in self]
222 225 if nums:
223 226 for i in range(len(dsu)):
224 227 numstr = "".join([ch for ch in dsu[i][0] if ch.isdigit()])
225 228 try:
226 229 n = int(numstr)
227 230 except ValueError:
228 231 n = 0
229 232 dsu[i][0] = n
230 233
231 234
232 235 dsu.sort()
233 236 return SList([t[1] for t in dsu])
234 237
235 238
236 239 # FIXME: We need to reimplement type specific displayhook and then add this
237 240 # back as a custom printer. This should also be moved outside utils into the
238 241 # core.
239 242
240 243 # def print_slist(arg):
241 244 # """ Prettier (non-repr-like) and more informative printer for SList """
242 245 # print "SList (.p, .n, .l, .s, .grep(), .fields(), sort() available):"
243 246 # if hasattr(arg, 'hideonce') and arg.hideonce:
244 247 # arg.hideonce = False
245 248 # return
246 249 #
247 250 # nlprint(arg) # This was a nested list printer, now removed.
248 251 #
249 252 # print_slist = result_display.when_type(SList)(print_slist)
250 253
251 254
252 255 def indent(instr,nspaces=4, ntabs=0, flatten=False):
253 256 """Indent a string a given number of spaces or tabstops.
254 257
255 258 indent(str,nspaces=4,ntabs=0) -> indent str by ntabs+nspaces.
256 259
257 260 Parameters
258 261 ----------
259 262
260 263 instr : basestring
261 264 The string to be indented.
262 265 nspaces : int (default: 4)
263 266 The number of spaces to be indented.
264 267 ntabs : int (default: 0)
265 268 The number of tabs to be indented.
266 269 flatten : bool (default: False)
267 270 Whether to scrub existing indentation. If True, all lines will be
268 271 aligned to the same indentation. If False, existing indentation will
269 272 be strictly increased.
270 273
271 274 Returns
272 275 -------
273 276
274 277 str|unicode : string indented by ntabs and nspaces.
275 278
276 279 """
277 280 if instr is None:
278 281 return
279 282 ind = '\t'*ntabs+' '*nspaces
280 283 if flatten:
281 284 pat = re.compile(r'^\s*', re.MULTILINE)
282 285 else:
283 286 pat = re.compile(r'^', re.MULTILINE)
284 287 outstr = re.sub(pat, ind, instr)
285 288 if outstr.endswith(os.linesep+ind):
286 289 return outstr[:-len(ind)]
287 290 else:
288 291 return outstr
289 292
290 293
291 294 def list_strings(arg):
292 295 """Always return a list of strings, given a string or list of strings
293 296 as input.
294 297
295 298 Examples
296 299 --------
297 300 ::
298 301
299 302 In [7]: list_strings('A single string')
300 303 Out[7]: ['A single string']
301 304
302 305 In [8]: list_strings(['A single string in a list'])
303 306 Out[8]: ['A single string in a list']
304 307
305 308 In [9]: list_strings(['A','list','of','strings'])
306 309 Out[9]: ['A', 'list', 'of', 'strings']
307 310 """
308 311
309 312 if isinstance(arg, py3compat.string_types): return [arg]
310 313 else: return arg
311 314
312 315
313 316 def marquee(txt='',width=78,mark='*'):
314 317 """Return the input string centered in a 'marquee'.
315 318
316 319 Examples
317 320 --------
318 321 ::
319 322
320 323 In [16]: marquee('A test',40)
321 324 Out[16]: '**************** A test ****************'
322 325
323 326 In [17]: marquee('A test',40,'-')
324 327 Out[17]: '---------------- A test ----------------'
325 328
326 329 In [18]: marquee('A test',40,' ')
327 330 Out[18]: ' A test '
328 331
329 332 """
330 333 if not txt:
331 334 return (mark*width)[:width]
332 335 nmark = (width-len(txt)-2)//len(mark)//2
333 336 if nmark < 0: nmark =0
334 337 marks = mark*nmark
335 338 return '%s %s %s' % (marks,txt,marks)
336 339
337 340
338 341 ini_spaces_re = re.compile(r'^(\s+)')
339 342
340 343 def num_ini_spaces(strng):
341 344 """Return the number of initial spaces in a string"""
342 345
343 346 ini_spaces = ini_spaces_re.match(strng)
344 347 if ini_spaces:
345 348 return ini_spaces.end()
346 349 else:
347 350 return 0
348 351
349 352
350 353 def format_screen(strng):
351 354 """Format a string for screen printing.
352 355
353 356 This removes some latex-type format codes."""
354 357 # Paragraph continue
355 358 par_re = re.compile(r'\\$',re.MULTILINE)
356 359 strng = par_re.sub('',strng)
357 360 return strng
358 361
359 362
360 363 def dedent(text):
361 364 """Equivalent of textwrap.dedent that ignores unindented first line.
362 365
363 366 This means it will still dedent strings like:
364 367 '''foo
365 368 is a bar
366 369 '''
367 370
368 371 For use in wrap_paragraphs.
369 372 """
370 373
371 374 if text.startswith('\n'):
372 375 # text starts with blank line, don't ignore the first line
373 376 return textwrap.dedent(text)
374 377
375 378 # split first line
376 379 splits = text.split('\n',1)
377 380 if len(splits) == 1:
378 381 # only one line
379 382 return textwrap.dedent(text)
380 383
381 384 first, rest = splits
382 385 # dedent everything but the first line
383 386 rest = textwrap.dedent(rest)
384 387 return '\n'.join([first, rest])
385 388
386 389
387 390 def wrap_paragraphs(text, ncols=80):
388 391 """Wrap multiple paragraphs to fit a specified width.
389 392
390 393 This is equivalent to textwrap.wrap, but with support for multiple
391 394 paragraphs, as separated by empty lines.
392 395
393 396 Returns
394 397 -------
395 398
396 399 list of complete paragraphs, wrapped to fill `ncols` columns.
397 400 """
398 401 paragraph_re = re.compile(r'\n(\s*\n)+', re.MULTILINE)
399 402 text = dedent(text).strip()
400 403 paragraphs = paragraph_re.split(text)[::2] # every other entry is space
401 404 out_ps = []
402 405 indent_re = re.compile(r'\n\s+', re.MULTILINE)
403 406 for p in paragraphs:
404 407 # presume indentation that survives dedent is meaningful formatting,
405 408 # so don't fill unless text is flush.
406 409 if indent_re.search(p) is None:
407 410 # wrap paragraph
408 411 p = textwrap.fill(p, ncols)
409 412 out_ps.append(p)
410 413 return out_ps
411 414
412 415
413 416 def long_substr(data):
414 417 """Return the longest common substring in a list of strings.
415 418
416 419 Credit: http://stackoverflow.com/questions/2892931/longest-common-substring-from-more-than-two-strings-python
417 420 """
418 421 substr = ''
419 422 if len(data) > 1 and len(data[0]) > 0:
420 423 for i in range(len(data[0])):
421 424 for j in range(len(data[0])-i+1):
422 425 if j > len(substr) and all(data[0][i:i+j] in x for x in data):
423 426 substr = data[0][i:i+j]
424 427 elif len(data) == 1:
425 428 substr = data[0]
426 429 return substr
427 430
428 431
429 432 def strip_email_quotes(text):
430 433 """Strip leading email quotation characters ('>').
431 434
432 435 Removes any combination of leading '>' interspersed with whitespace that
433 436 appears *identically* in all lines of the input text.
434 437
435 438 Parameters
436 439 ----------
437 440 text : str
438 441
439 442 Examples
440 443 --------
441 444
442 445 Simple uses::
443 446
444 447 In [2]: strip_email_quotes('> > text')
445 448 Out[2]: 'text'
446 449
447 450 In [3]: strip_email_quotes('> > text\\n> > more')
448 451 Out[3]: 'text\\nmore'
449 452
450 453 Note how only the common prefix that appears in all lines is stripped::
451 454
452 455 In [4]: strip_email_quotes('> > text\\n> > more\\n> more...')
453 456 Out[4]: '> text\\n> more\\nmore...'
454 457
455 458 So if any line has no quote marks ('>') , then none are stripped from any
456 459 of them ::
457 460
458 461 In [5]: strip_email_quotes('> > text\\n> > more\\nlast different')
459 462 Out[5]: '> > text\\n> > more\\nlast different'
460 463 """
461 464 lines = text.splitlines()
462 465 matches = set()
463 466 for line in lines:
464 467 prefix = re.match(r'^(\s*>[ >]*)', line)
465 468 if prefix:
466 469 matches.add(prefix.group(1))
467 470 else:
468 471 break
469 472 else:
470 473 prefix = long_substr(list(matches))
471 474 if prefix:
472 475 strip = len(prefix)
473 476 text = '\n'.join([ ln[strip:] for ln in lines])
474 477 return text
475 478
476 479 def strip_ansi(source):
477 480 """
478 481 Remove ansi escape codes from text.
479 482
480 483 Parameters
481 484 ----------
482 485 source : str
483 486 Source to remove the ansi from
484 487 """
485 488 return re.sub(r'\033\[(\d|;)+?m', '', source)
486 489
487 490
488 491 class EvalFormatter(Formatter):
489 492 """A String Formatter that allows evaluation of simple expressions.
490 493
491 494 Note that this version interprets a : as specifying a format string (as per
492 495 standard string formatting), so if slicing is required, you must explicitly
493 496 create a slice.
494 497
495 498 This is to be used in templating cases, such as the parallel batch
496 499 script templates, where simple arithmetic on arguments is useful.
497 500
498 501 Examples
499 502 --------
500 503 ::
501 504
502 505 In [1]: f = EvalFormatter()
503 506 In [2]: f.format('{n//4}', n=8)
504 507 Out[2]: '2'
505 508
506 509 In [3]: f.format("{greeting[slice(2,4)]}", greeting="Hello")
507 510 Out[3]: 'll'
508 511 """
509 512 def get_field(self, name, args, kwargs):
510 513 v = eval(name, kwargs)
511 514 return v, name
512 515
513 516 #XXX: As of Python 3.4, the format string parsing no longer splits on a colon
514 517 # inside [], so EvalFormatter can handle slicing. Once we only support 3.4 and
515 518 # above, it should be possible to remove FullEvalFormatter.
516 519
517 520 @skip_doctest_py3
518 521 class FullEvalFormatter(Formatter):
519 522 """A String Formatter that allows evaluation of simple expressions.
520 523
521 524 Any time a format key is not found in the kwargs,
522 525 it will be tried as an expression in the kwargs namespace.
523 526
524 527 Note that this version allows slicing using [1:2], so you cannot specify
525 528 a format string. Use :class:`EvalFormatter` to permit format strings.
526 529
527 530 Examples
528 531 --------
529 532 ::
530 533
531 534 In [1]: f = FullEvalFormatter()
532 535 In [2]: f.format('{n//4}', n=8)
533 536 Out[2]: u'2'
534 537
535 538 In [3]: f.format('{list(range(5))[2:4]}')
536 539 Out[3]: u'[2, 3]'
537 540
538 541 In [4]: f.format('{3*2}')
539 542 Out[4]: u'6'
540 543 """
541 544 # copied from Formatter._vformat with minor changes to allow eval
542 545 # and replace the format_spec code with slicing
543 546 def vformat(self, format_string, args, kwargs):
544 547 result = []
545 548 for literal_text, field_name, format_spec, conversion in \
546 549 self.parse(format_string):
547 550
548 551 # output the literal text
549 552 if literal_text:
550 553 result.append(literal_text)
551 554
552 555 # if there's a field, output it
553 556 if field_name is not None:
554 557 # this is some markup, find the object and do
555 558 # the formatting
556 559
557 560 if format_spec:
558 561 # override format spec, to allow slicing:
559 562 field_name = ':'.join([field_name, format_spec])
560 563
561 564 # eval the contents of the field for the object
562 565 # to be formatted
563 566 obj = eval(field_name, kwargs)
564 567
565 568 # do any conversion on the resulting object
566 569 obj = self.convert_field(obj, conversion)
567 570
568 571 # format the object and append to the result
569 572 result.append(self.format_field(obj, ''))
570 573
571 574 return u''.join(py3compat.cast_unicode(s) for s in result)
572 575
573 576
574 577 @skip_doctest_py3
575 578 class DollarFormatter(FullEvalFormatter):
576 579 """Formatter allowing Itpl style $foo replacement, for names and attribute
577 580 access only. Standard {foo} replacement also works, and allows full
578 581 evaluation of its arguments.
579 582
580 583 Examples
581 584 --------
582 585 ::
583 586
584 587 In [1]: f = DollarFormatter()
585 588 In [2]: f.format('{n//4}', n=8)
586 589 Out[2]: u'2'
587 590
588 591 In [3]: f.format('23 * 76 is $result', result=23*76)
589 592 Out[3]: u'23 * 76 is 1748'
590 593
591 594 In [4]: f.format('$a or {b}', a=1, b=2)
592 595 Out[4]: u'1 or 2'
593 596 """
594 597 _dollar_pattern = re.compile("(.*?)\$(\$?[\w\.]+)")
595 598 def parse(self, fmt_string):
596 599 for literal_txt, field_name, format_spec, conversion \
597 600 in Formatter.parse(self, fmt_string):
598 601
599 602 # Find $foo patterns in the literal text.
600 603 continue_from = 0
601 604 txt = ""
602 605 for m in self._dollar_pattern.finditer(literal_txt):
603 606 new_txt, new_field = m.group(1,2)
604 607 # $$foo --> $foo
605 608 if new_field.startswith("$"):
606 609 txt += new_txt + new_field
607 610 else:
608 611 yield (txt + new_txt, new_field, "", None)
609 612 txt = ""
610 613 continue_from = m.end()
611 614
612 615 # Re-yield the {foo} style pattern
613 616 yield (txt + literal_txt[continue_from:], field_name, format_spec, conversion)
614 617
615 618 #-----------------------------------------------------------------------------
616 619 # Utils to columnize a list of string
617 620 #-----------------------------------------------------------------------------
618 621
619 622 def _col_chunks(l, max_rows, row_first=False):
620 623 """Yield successive max_rows-sized column chunks from l."""
621 624 if row_first:
622 625 ncols = (len(l) // max_rows) + (len(l) % max_rows > 0)
623 626 for i in py3compat.xrange(ncols):
624 627 yield [l[j] for j in py3compat.xrange(i, len(l), ncols)]
625 628 else:
626 629 for i in py3compat.xrange(0, len(l), max_rows):
627 630 yield l[i:(i + max_rows)]
628 631
629 632
630 633 def _find_optimal(rlist, row_first=False, separator_size=2, displaywidth=80):
631 634 """Calculate optimal info to columnize a list of string"""
632 635 for max_rows in range(1, len(rlist) + 1):
633 636 col_widths = list(map(max, _col_chunks(rlist, max_rows, row_first)))
634 637 sumlength = sum(col_widths)
635 638 ncols = len(col_widths)
636 639 if sumlength + separator_size * (ncols - 1) <= displaywidth:
637 640 break
638 641 return {'num_columns': ncols,
639 642 'optimal_separator_width': (displaywidth - sumlength) / (ncols - 1) if (ncols - 1) else 0,
640 643 'max_rows': max_rows,
641 644 'column_widths': col_widths
642 645 }
643 646
644 647
645 648 def _get_or_default(mylist, i, default=None):
646 649 """return list item number, or default if don't exist"""
647 650 if i >= len(mylist):
648 651 return default
649 652 else :
650 653 return mylist[i]
651 654
652 655
653 656 def compute_item_matrix(items, row_first=False, empty=None, *args, **kwargs) :
654 657 """Returns a nested list, and info to columnize items
655 658
656 659 Parameters
657 660 ----------
658 661
659 662 items
660 663 list of strings to columize
661 664 row_first : (default False)
662 665 Whether to compute columns for a row-first matrix instead of
663 666 column-first (default).
664 667 empty : (default None)
665 668 default value to fill list if needed
666 669 separator_size : int (default=2)
667 670 How much caracters will be used as a separation between each columns.
668 671 displaywidth : int (default=80)
669 672 The width of the area onto wich the columns should enter
670 673
671 674 Returns
672 675 -------
673 676
674 677 strings_matrix
675 678
676 679 nested list of string, the outer most list contains as many list as
677 680 rows, the innermost lists have each as many element as colums. If the
678 681 total number of elements in `items` does not equal the product of
679 682 rows*columns, the last element of some lists are filled with `None`.
680 683
681 684 dict_info
682 685 some info to make columnize easier:
683 686
684 687 num_columns
685 688 number of columns
686 689 max_rows
687 690 maximum number of rows (final number may be less)
688 691 column_widths
689 692 list of with of each columns
690 693 optimal_separator_width
691 694 best separator width between columns
692 695
693 696 Examples
694 697 --------
695 698 ::
696 699
697 700 In [1]: l = ['aaa','b','cc','d','eeeee','f','g','h','i','j','k','l']
698 701 ...: compute_item_matrix(l, displaywidth=12)
699 702 Out[1]:
700 703 ([['aaa', 'f', 'k'],
701 704 ['b', 'g', 'l'],
702 705 ['cc', 'h', None],
703 706 ['d', 'i', None],
704 707 ['eeeee', 'j', None]],
705 708 {'num_columns': 3,
706 709 'column_widths': [5, 1, 1],
707 710 'optimal_separator_width': 2,
708 711 'max_rows': 5})
709 712 """
710 713 info = _find_optimal(list(map(len, items)), row_first, *args, **kwargs)
711 714 nrow, ncol = info['max_rows'], info['num_columns']
712 715 if row_first:
713 716 return ([[_get_or_default(items, r * ncol + c, default=empty) for c in range(ncol)] for r in range(nrow)], info)
714 717 else:
715 718 return ([[_get_or_default(items, c * nrow + r, default=empty) for c in range(ncol)] for r in range(nrow)], info)
716 719
717 720
718 721 def columnize(items, row_first=False, separator=' ', displaywidth=80, spread=False):
719 722 """ Transform a list of strings into a single string with columns.
720 723
721 724 Parameters
722 725 ----------
723 726 items : sequence of strings
724 727 The strings to process.
725 728
726 729 row_first : (default False)
727 730 Whether to compute columns for a row-first matrix instead of
728 731 column-first (default).
729 732
730 733 separator : str, optional [default is two spaces]
731 734 The string that separates columns.
732 735
733 736 displaywidth : int, optional [default is 80]
734 737 Width of the display in number of characters.
735 738
736 739 Returns
737 740 -------
738 741 The formatted string.
739 742 """
740 743 if not items:
741 744 return '\n'
742 745 matrix, info = compute_item_matrix(items, row_first=row_first, separator_size=len(separator), displaywidth=displaywidth)
743 746 if spread:
744 747 separator = separator.ljust(int(info['optimal_separator_width']))
745 748 fmatrix = [filter(None, x) for x in matrix]
746 749 sjoin = lambda x : separator.join([ y.ljust(w, ' ') for y, w in zip(x, info['column_widths'])])
747 750 return '\n'.join(map(sjoin, fmatrix))+'\n'
748 751
749 752
750 753 def get_text_list(list_, last_sep=' and ', sep=", ", wrap_item_with=""):
751 754 """
752 755 Return a string with a natural enumeration of items
753 756
754 757 >>> get_text_list(['a', 'b', 'c', 'd'])
755 758 'a, b, c and d'
756 759 >>> get_text_list(['a', 'b', 'c'], ' or ')
757 760 'a, b or c'
758 761 >>> get_text_list(['a', 'b', 'c'], ', ')
759 762 'a, b, c'
760 763 >>> get_text_list(['a', 'b'], ' or ')
761 764 'a or b'
762 765 >>> get_text_list(['a'])
763 766 'a'
764 767 >>> get_text_list([])
765 768 ''
766 769 >>> get_text_list(['a', 'b'], wrap_item_with="`")
767 770 '`a` and `b`'
768 771 >>> get_text_list(['a', 'b', 'c', 'd'], " = ", sep=" + ")
769 772 'a + b + c = d'
770 773 """
771 774 if len(list_) == 0:
772 775 return ''
773 776 if wrap_item_with:
774 777 list_ = ['%s%s%s' % (wrap_item_with, item, wrap_item_with) for
775 778 item in list_]
776 779 if len(list_) == 1:
777 780 return list_[0]
778 781 return '%s%s%s' % (
779 782 sep.join(i for i in list_[:-1]),
780 783 last_sep, list_[-1])
@@ -1,298 +1,299 b''
1 1 #!/usr/bin/env python
2 2 # -*- coding: utf-8 -*-
3 3 """Setup script for IPython.
4 4
5 5 Under Posix environments it works like a typical setup.py script.
6 6 Under Windows, the command sdist is not supported, since IPython
7 7 requires utilities which are not available under Windows."""
8 8
9 9 #-----------------------------------------------------------------------------
10 10 # Copyright (c) 2008-2011, IPython Development Team.
11 11 # Copyright (c) 2001-2007, Fernando Perez <fernando.perez@colorado.edu>
12 12 # Copyright (c) 2001, Janko Hauser <jhauser@zscout.de>
13 13 # Copyright (c) 2001, Nathaniel Gray <n8gray@caltech.edu>
14 14 #
15 15 # Distributed under the terms of the Modified BSD License.
16 16 #
17 17 # The full license is in the file COPYING.rst, distributed with this software.
18 18 #-----------------------------------------------------------------------------
19 19
20 20 #-----------------------------------------------------------------------------
21 21 # Minimal Python version sanity check
22 22 #-----------------------------------------------------------------------------
23 23 from __future__ import print_function
24 24
25 25 import sys
26 26
27 27 # This check is also made in IPython/__init__, don't forget to update both when
28 28 # changing Python version requirements.
29 29 v = sys.version_info
30 30 if v[:2] < (2,7) or (v[0] >= 3 and v[:2] < (3,3)):
31 31 error = "ERROR: IPython requires Python version 2.7 or 3.3 or above."
32 32 print(error, file=sys.stderr)
33 33 sys.exit(1)
34 34
35 35 PY3 = (sys.version_info[0] >= 3)
36 36
37 37 # At least we're on the python version we need, move on.
38 38
39 39 #-------------------------------------------------------------------------------
40 40 # Imports
41 41 #-------------------------------------------------------------------------------
42 42
43 43 # Stdlib imports
44 44 import os
45 45
46 46 from glob import glob
47 47
48 48 # BEFORE importing distutils, remove MANIFEST. distutils doesn't properly
49 49 # update it when the contents of directories change.
50 50 if os.path.exists('MANIFEST'): os.remove('MANIFEST')
51 51
52 52 from distutils.core import setup
53 53
54 54 # Our own imports
55 55 from setupbase import target_update
56 56
57 57 from setupbase import (
58 58 setup_args,
59 59 find_packages,
60 60 find_package_data,
61 61 check_package_data_first,
62 62 find_entry_points,
63 63 build_scripts_entrypt,
64 64 find_data_files,
65 65 git_prebuild,
66 66 install_symlinked,
67 67 install_lib_symlink,
68 68 install_scripts_for_symlink,
69 69 unsymlink,
70 70 )
71 71
72 72 isfile = os.path.isfile
73 73 pjoin = os.path.join
74 74
75 75 #-------------------------------------------------------------------------------
76 76 # Handle OS specific things
77 77 #-------------------------------------------------------------------------------
78 78
79 79 if os.name in ('nt','dos'):
80 80 os_name = 'windows'
81 81 else:
82 82 os_name = os.name
83 83
84 84 # Under Windows, 'sdist' has not been supported. Now that the docs build with
85 85 # Sphinx it might work, but let's not turn it on until someone confirms that it
86 86 # actually works.
87 87 if os_name == 'windows' and 'sdist' in sys.argv:
88 88 print('The sdist command is not available under Windows. Exiting.')
89 89 sys.exit(1)
90 90
91 91
92 92 #-------------------------------------------------------------------------------
93 93 # Things related to the IPython documentation
94 94 #-------------------------------------------------------------------------------
95 95
96 96 # update the manuals when building a source dist
97 97 if len(sys.argv) >= 2 and sys.argv[1] in ('sdist','bdist_rpm'):
98 98
99 99 # List of things to be updated. Each entry is a triplet of args for
100 100 # target_update()
101 101 to_update = [
102 102 ('docs/man/ipython.1.gz',
103 103 ['docs/man/ipython.1'],
104 104 'cd docs/man && gzip -9c ipython.1 > ipython.1.gz'),
105 105 ]
106 106
107 107
108 108 [ target_update(*t) for t in to_update ]
109 109
110 110 #---------------------------------------------------------------------------
111 111 # Find all the packages, package data, and data_files
112 112 #---------------------------------------------------------------------------
113 113
114 114 packages = find_packages()
115 115 package_data = find_package_data()
116 116
117 117 data_files = find_data_files()
118 118
119 119 setup_args['packages'] = packages
120 120 setup_args['package_data'] = package_data
121 121 setup_args['data_files'] = data_files
122 122
123 123 #---------------------------------------------------------------------------
124 124 # custom distutils commands
125 125 #---------------------------------------------------------------------------
126 126 # imports here, so they are after setuptools import if there was one
127 127 from distutils.command.sdist import sdist
128 128 from distutils.command.upload import upload
129 129
130 130 class UploadWindowsInstallers(upload):
131 131
132 132 description = "Upload Windows installers to PyPI (only used from tools/release_windows.py)"
133 133 user_options = upload.user_options + [
134 134 ('files=', 'f', 'exe file (or glob) to upload')
135 135 ]
136 136 def initialize_options(self):
137 137 upload.initialize_options(self)
138 138 meta = self.distribution.metadata
139 139 base = '{name}-{version}'.format(
140 140 name=meta.get_name(),
141 141 version=meta.get_version()
142 142 )
143 143 self.files = os.path.join('dist', '%s.*.exe' % base)
144 144
145 145 def run(self):
146 146 for dist_file in glob(self.files):
147 147 self.upload_file('bdist_wininst', 'any', dist_file)
148 148
149 149 setup_args['cmdclass'] = {
150 150 'build_py': \
151 151 check_package_data_first(git_prebuild('IPython')),
152 152 'sdist' : git_prebuild('IPython', sdist),
153 153 'upload_wininst' : UploadWindowsInstallers,
154 154 'symlink': install_symlinked,
155 155 'install_lib_symlink': install_lib_symlink,
156 156 'install_scripts_sym': install_scripts_for_symlink,
157 157 'unsymlink': unsymlink,
158 158 }
159 159
160 160
161 161 #---------------------------------------------------------------------------
162 162 # Handle scripts, dependencies, and setuptools specific things
163 163 #---------------------------------------------------------------------------
164 164
165 165 # For some commands, use setuptools. Note that we do NOT list install here!
166 166 # If you want a setuptools-enhanced install, just run 'setupegg.py install'
167 167 needs_setuptools = set(('develop', 'release', 'bdist_egg', 'bdist_rpm',
168 168 'bdist', 'bdist_dumb', 'bdist_wininst', 'bdist_wheel',
169 169 'egg_info', 'easy_install', 'upload', 'install_egg_info',
170 170 ))
171 171
172 172 if len(needs_setuptools.intersection(sys.argv)) > 0:
173 173 import setuptools
174 174
175 175 # This dict is used for passing extra arguments that are setuptools
176 176 # specific to setup
177 177 setuptools_extra_args = {}
178 178
179 179 # setuptools requirements
180 180
181 181 extras_require = dict(
182 182 parallel = ['ipyparallel'],
183 183 qtconsole = ['qtconsole'],
184 184 doc = ['Sphinx>=1.3'],
185 test = ['nose>=0.10.1', 'requests', 'testpath', 'pygments', 'path.py'],
185 test = ['nose>=0.10.1', 'requests', 'testpath', 'pygments'],
186 186 terminal = [],
187 187 kernel = ['ipykernel'],
188 188 nbformat = ['nbformat'],
189 189 notebook = ['notebook', 'ipywidgets'],
190 190 nbconvert = ['nbconvert'],
191 191 )
192 192 install_requires = [
193 193 'setuptools>=18.5',
194 194 'decorator',
195 195 'pickleshare',
196 196 'simplegeneric>0.8',
197 197 'traitlets',
198 198 'prompt_toolkit>=0.60',
199 199 'pygments',
200 200 'backports.shutil_get_terminal_size',
201 201 ]
202 202
203 203 # Platform-specific dependencies:
204 204 # This is the correct way to specify these,
205 205 # but requires pip >= 6. pip < 6 ignores these.
206 206
207 207 extras_require.update({
208 ':python_version == "2.7" or python_version == "3.3"': ['pathlib2'],
208 209 ':sys_platform != "win32"': ['pexpect'],
209 210 ':sys_platform == "darwin"': ['appnope'],
210 211 ':sys_platform == "win32"': ['colorama'],
211 212 'test:python_version == "2.7"': ['mock'],
212 213 })
213 214 # FIXME: re-specify above platform dependencies for pip < 6
214 215 # These would result in non-portable bdists.
215 216 if not any(arg.startswith('bdist') for arg in sys.argv):
216 217 if sys.version_info < (3, 3):
217 218 extras_require['test'].append('mock')
218 219
219 220 if sys.platform == 'darwin':
220 221 install_requires.extend(['appnope'])
221 222 have_readline = False
222 223 try:
223 224 import readline
224 225 except ImportError:
225 226 pass
226 227 else:
227 228 if 'libedit' not in readline.__doc__:
228 229 have_readline = True
229 230 if not have_readline:
230 231 install_requires.extend(['gnureadline'])
231 232
232 233 if sys.platform.startswith('win'):
233 234 extras_require['terminal'].append('pyreadline>=2.0')
234 235 else:
235 236 install_requires.append('pexpect')
236 237
237 238 # workaround pypa/setuptools#147, where setuptools misspells
238 239 # platform_python_implementation as python_implementation
239 240 if 'setuptools' in sys.modules:
240 241 for key in list(extras_require):
241 242 if 'platform_python_implementation' in key:
242 243 new_key = key.replace('platform_python_implementation', 'python_implementation')
243 244 extras_require[new_key] = extras_require.pop(key)
244 245
245 246 everything = set()
246 247 for key, deps in extras_require.items():
247 248 if ':' not in key:
248 249 everything.update(deps)
249 250 extras_require['all'] = everything
250 251
251 252 if 'setuptools' in sys.modules:
252 253 setuptools_extra_args['zip_safe'] = False
253 254 setuptools_extra_args['entry_points'] = {
254 255 'console_scripts': find_entry_points(),
255 256 'pygments.lexers': [
256 257 'ipythonconsole = IPython.lib.lexers:IPythonConsoleLexer',
257 258 'ipython = IPython.lib.lexers:IPythonLexer',
258 259 'ipython3 = IPython.lib.lexers:IPython3Lexer',
259 260 ],
260 261 }
261 262 setup_args['extras_require'] = extras_require
262 263 requires = setup_args['install_requires'] = install_requires
263 264
264 265 # Script to be run by the windows binary installer after the default setup
265 266 # routine, to add shortcuts and similar windows-only things. Windows
266 267 # post-install scripts MUST reside in the scripts/ dir, otherwise distutils
267 268 # doesn't find them.
268 269 if 'bdist_wininst' in sys.argv:
269 270 if len(sys.argv) > 2 and \
270 271 ('sdist' in sys.argv or 'bdist_rpm' in sys.argv):
271 272 print("ERROR: bdist_wininst must be run alone. Exiting.", file=sys.stderr)
272 273 sys.exit(1)
273 274 setup_args['data_files'].append(
274 275 ['Scripts', ('scripts/ipython.ico', 'scripts/ipython_nb.ico')])
275 276 setup_args['scripts'] = [pjoin('scripts','ipython_win_post_install.py')]
276 277 setup_args['options'] = {"bdist_wininst":
277 278 {"install_script":
278 279 "ipython_win_post_install.py"}}
279 280
280 281 else:
281 282 # scripts has to be a non-empty list, or install_scripts isn't called
282 283 setup_args['scripts'] = [e.split('=')[0].strip() for e in find_entry_points()]
283 284
284 285 setup_args['cmdclass']['build_scripts'] = build_scripts_entrypt
285 286
286 287 #---------------------------------------------------------------------------
287 288 # Do the actual setup now
288 289 #---------------------------------------------------------------------------
289 290
290 291 setup_args.update(setuptools_extra_args)
291 292
292 293
293 294
294 295 def main():
295 296 setup(**setup_args)
296 297
297 298 if __name__ == '__main__':
298 299 main()
General Comments 0
You need to be logged in to leave comments. Login now