Show More
@@ -18,6 +18,7 b' import io' | |||||
18 | import os |
|
18 | import os | |
19 | import re |
|
19 | import re | |
20 | import sys |
|
20 | import sys | |
|
21 | from itertools import chain | |||
21 |
|
22 | |||
22 | # Our own packages |
|
23 | # Our own packages | |
23 | from IPython.core.error import TryNext, StdinNotImplementedError, UsageError |
|
24 | from IPython.core.error import TryNext, StdinNotImplementedError, UsageError | |
@@ -39,6 +40,43 b' class MacroToEdit(ValueError): pass' | |||||
39 |
|
40 | |||
40 | ipython_input_pat = re.compile(r"<ipython\-input\-(\d+)-[a-z\d]+>$") |
|
41 | ipython_input_pat = re.compile(r"<ipython\-input\-(\d+)-[a-z\d]+>$") | |
41 |
|
42 | |||
|
43 | # To match, e.g. 8-10 1:5 :10 3- | |||
|
44 | range_re = re.compile(r""" | |||
|
45 | (?P<start>\d+)? | |||
|
46 | ((?P<sep>[\-:]) | |||
|
47 | (?P<end>\d+)?)? | |||
|
48 | $""", re.VERBOSE) | |||
|
49 | ||||
|
50 | ||||
|
51 | def extract_code_ranges(ranges_str): | |||
|
52 | """Turn a string of range for %%load into 2-tuples of (start, stop) | |||
|
53 | ready to use as a slice of the content splitted by lines. | |||
|
54 | ||||
|
55 | Examples | |||
|
56 | -------- | |||
|
57 | list(extract_input_ranges("5-10 2")) | |||
|
58 | [(4, 10), (1, 2)] | |||
|
59 | """ | |||
|
60 | for range_str in ranges_str.split(): | |||
|
61 | rmatch = range_re.match(range_str) | |||
|
62 | if not rmatch: | |||
|
63 | continue | |||
|
64 | sep = rmatch.group("sep") | |||
|
65 | start = rmatch.group("start") | |||
|
66 | end = rmatch.group("end") | |||
|
67 | ||||
|
68 | if sep == '-': | |||
|
69 | start = int(start) - 1 if start else None | |||
|
70 | end = int(end) if end else None | |||
|
71 | elif sep == ':': | |||
|
72 | start = int(start) - 1 if start else None | |||
|
73 | end = int(end) - 1 if end else None | |||
|
74 | else: | |||
|
75 | end = int(start) | |||
|
76 | start = int(start) - 1 | |||
|
77 | yield (start, end) | |||
|
78 | ||||
|
79 | ||||
42 | class InteractivelyDefined(Exception): |
|
80 | class InteractivelyDefined(Exception): | |
43 | """Exception for interactively defined variable in magic_edit""" |
|
81 | """Exception for interactively defined variable in magic_edit""" | |
44 | def __init__(self, index): |
|
82 | def __init__(self, index): | |
@@ -174,6 +212,11 b' class CodeMagics(Magics):' | |||||
174 |
|
212 | |||
175 | Options: |
|
213 | Options: | |
176 | -------- |
|
214 | -------- | |
|
215 | -r <lines>: Specify lines or ranges of lines to load from the source. | |||
|
216 | Ranges could be specified as x-y (x..y) or in python-style x:y | |||
|
217 | (x..(y-1)). Both limits x and y can be left blank (meaning the | |||
|
218 | beginning and end of the file, respectively). | |||
|
219 | ||||
177 | -y : Don't ask confirmation for loading source above 200 000 characters. |
|
220 | -y : Don't ask confirmation for loading source above 200 000 characters. | |
178 |
|
221 | |||
179 | This magic command can either take a local filename, a URL, an history |
|
222 | This magic command can either take a local filename, a URL, an history | |
@@ -185,15 +228,27 b' class CodeMagics(Magics):' | |||||
185 | %load 7-27 |
|
228 | %load 7-27 | |
186 | %load myMacro |
|
229 | %load myMacro | |
187 | %load http://www.example.com/myscript.py |
|
230 | %load http://www.example.com/myscript.py | |
|
231 | %load -r 5-10 myscript.py | |||
|
232 | %load -r 10-20,30,40: foo.py | |||
188 | """ |
|
233 | """ | |
189 | opts,args = self.parse_options(arg_s,'y') |
|
234 | opts,args = self.parse_options(arg_s,'yr:') | |
|
235 | ||||
190 | if not args: |
|
236 | if not args: | |
191 | raise UsageError('Missing filename, URL, input history range, ' |
|
237 | raise UsageError('Missing filename, URL, input history range, ' | |
192 | 'or macro.') |
|
238 | 'or macro.') | |
193 |
|
239 | |||
194 | contents = self.shell.find_user_code(args) |
|
240 | contents = self.shell.find_user_code(args) | |
|
241 | ||||
|
242 | if 'r' in opts: | |||
|
243 | ranges = opts['r'].replace(',', ' ') | |||
|
244 | lines = contents.split('\n') | |||
|
245 | slices = extract_code_ranges(ranges) | |||
|
246 | contents = [lines[slice(*slc)] for slc in slices] | |||
|
247 | contents = '\n'.join(chain.from_iterable(contents)) | |||
|
248 | ||||
195 | l = len(contents) |
|
249 | l = len(contents) | |
196 |
|
250 | |||
|
251 | ||||
197 | # 200 000 is ~ 2500 full 80 caracter lines |
|
252 | # 200 000 is ~ 2500 full 80 caracter lines | |
198 | # so in average, more than 5000 lines |
|
253 | # so in average, more than 5000 lines | |
199 | if l > 200000 and 'y' not in opts: |
|
254 | if l > 200000 and 'y' not in opts: |
@@ -45,6 +45,21 b' from IPython.utils.process import find_cmd' | |||||
45 | @magic.magics_class |
|
45 | @magic.magics_class | |
46 | class DummyMagics(magic.Magics): pass |
|
46 | class DummyMagics(magic.Magics): pass | |
47 |
|
47 | |||
|
48 | def test_extract_code_ranges(): | |||
|
49 | instr = "1 3 5-6 7-9 10:15 17: :10 10- -13 :" | |||
|
50 | expected = [(0, 1), | |||
|
51 | (2, 3), | |||
|
52 | (4, 6), | |||
|
53 | (6, 9), | |||
|
54 | (9, 14), | |||
|
55 | (16, None), | |||
|
56 | (None, 9), | |||
|
57 | (9, None), | |||
|
58 | (None, 13), | |||
|
59 | (None, None)] | |||
|
60 | actual = list(code.extract_code_ranges(instr)) | |||
|
61 | nt.assert_equal(actual, expected) | |||
|
62 | ||||
48 | def test_rehashx(): |
|
63 | def test_rehashx(): | |
49 | # clear up everything |
|
64 | # clear up everything | |
50 | _ip = get_ipython() |
|
65 | _ip = get_ipython() |
General Comments 0
You need to be logged in to leave comments.
Login now