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 | import ast | |||
21 | from itertools import chain |
|
22 | from itertools import chain | |
22 |
|
23 | |||
23 | # Our own packages |
|
24 | # Our own packages | |
@@ -47,7 +48,6 b' range_re = re.compile(r"""' | |||||
47 | (?P<end>\d+)?)? |
|
48 | (?P<end>\d+)?)? | |
48 | $""", re.VERBOSE) |
|
49 | $""", re.VERBOSE) | |
49 |
|
50 | |||
50 |
|
||||
51 | def extract_code_ranges(ranges_str): |
|
51 | def extract_code_ranges(ranges_str): | |
52 | """Turn a string of range for %%load into 2-tuples of (start, stop) |
|
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. |
|
53 | ready to use as a slice of the content splitted by lines. | |
@@ -76,6 +76,40 b' def extract_code_ranges(ranges_str):' | |||||
76 | start = int(start) - 1 |
|
76 | start = int(start) - 1 | |
77 | yield (start, end) |
|
77 | yield (start, end) | |
78 |
|
78 | |||
|
79 | def extract_symbols(code, symbols): | |||
|
80 | """ | |||
|
81 | Return a list of code fragments for each symbol parsed from code | |||
|
82 | For example, suppose code is:: | |||
|
83 | ||||
|
84 | a = 10 | |||
|
85 | ||||
|
86 | def b(): return 42 | |||
|
87 | ||||
|
88 | class A: pass | |||
|
89 | ||||
|
90 | >>> extract_symbols(code, 'A,b'): | |||
|
91 | class A: pass | |||
|
92 | ||||
|
93 | def b(): return 42 | |||
|
94 | """ | |||
|
95 | try: | |||
|
96 | py_code = ast.parse(code) | |||
|
97 | except SyntaxError: | |||
|
98 | return [] | |||
|
99 | marks = [(getattr(s, 'name', None), s.lineno) for s in py_code.body] | |||
|
100 | end = None | |||
|
101 | symbols_lines = {} | |||
|
102 | for name, start in reversed(marks): | |||
|
103 | if name: | |||
|
104 | symbols_lines[name] = (start - 1, end) | |||
|
105 | end = start - 1 | |||
|
106 | blocks = [] | |||
|
107 | code = code.split('\n') | |||
|
108 | for symbol in symbols.split(','): | |||
|
109 | if symbol in symbols_lines: | |||
|
110 | blocks.append('\n'.join(code[slice(*symbols_lines[symbol])])) | |||
|
111 | return blocks | |||
|
112 | ||||
79 |
|
113 | |||
80 | class InteractivelyDefined(Exception): |
|
114 | class InteractivelyDefined(Exception): | |
81 | """Exception for interactively defined variable in magic_edit""" |
|
115 | """Exception for interactively defined variable in magic_edit""" | |
@@ -217,6 +251,8 b' class CodeMagics(Magics):' | |||||
217 | (x..(y-1)). Both limits x and y can be left blank (meaning the |
|
251 | (x..(y-1)). Both limits x and y can be left blank (meaning the | |
218 | beginning and end of the file, respectively). |
|
252 | beginning and end of the file, respectively). | |
219 |
|
253 | |||
|
254 | -s <symbols>: Specify function or classes to load from python source. Could be | |||
|
255 | ||||
220 | -y : Don't ask confirmation for loading source above 200 000 characters. |
|
256 | -y : Don't ask confirmation for loading source above 200 000 characters. | |
221 |
|
257 | |||
222 | This magic command can either take a local filename, a URL, an history |
|
258 | This magic command can either take a local filename, a URL, an history | |
@@ -230,8 +266,9 b' class CodeMagics(Magics):' | |||||
230 | %load http://www.example.com/myscript.py |
|
266 | %load http://www.example.com/myscript.py | |
231 | %load -r 5-10 myscript.py |
|
267 | %load -r 5-10 myscript.py | |
232 | %load -r 10-20,30,40: foo.py |
|
268 | %load -r 10-20,30,40: foo.py | |
|
269 | %load -s MyClass,wonder_function myscript.py | |||
233 | """ |
|
270 | """ | |
234 | opts,args = self.parse_options(arg_s,'yr:') |
|
271 | opts,args = self.parse_options(arg_s,'ys:r:') | |
235 |
|
272 | |||
236 | if not args: |
|
273 | if not args: | |
237 | raise UsageError('Missing filename, URL, input history range, ' |
|
274 | raise UsageError('Missing filename, URL, input history range, ' | |
@@ -239,6 +276,9 b' class CodeMagics(Magics):' | |||||
239 |
|
276 | |||
240 | contents = self.shell.find_user_code(args) |
|
277 | contents = self.shell.find_user_code(args) | |
241 |
|
278 | |||
|
279 | if 's' in opts: | |||
|
280 | contents = '\n'.join(extract_symbols(contents, opts['s'])) | |||
|
281 | ||||
242 | if 'r' in opts: |
|
282 | if 'r' in opts: | |
243 | ranges = opts['r'].replace(',', ' ') |
|
283 | ranges = opts['r'].replace(',', ' ') | |
244 | lines = contents.split('\n') |
|
284 | lines = contents.split('\n') | |
@@ -248,7 +288,6 b' class CodeMagics(Magics):' | |||||
248 |
|
288 | |||
249 | l = len(contents) |
|
289 | l = len(contents) | |
250 |
|
290 | |||
251 |
|
||||
252 | # 200 000 is ~ 2500 full 80 caracter lines |
|
291 | # 200 000 is ~ 2500 full 80 caracter lines | |
253 | # so in average, more than 5000 lines |
|
292 | # so in average, more than 5000 lines | |
254 | if l > 200000 and 'y' not in opts: |
|
293 | if l > 200000 and 'y' not in opts: |
@@ -60,6 +60,28 b' def test_extract_code_ranges():' | |||||
60 | actual = list(code.extract_code_ranges(instr)) |
|
60 | actual = list(code.extract_code_ranges(instr)) | |
61 | nt.assert_equal(actual, expected) |
|
61 | nt.assert_equal(actual, expected) | |
62 |
|
62 | |||
|
63 | ||||
|
64 | def test_extract_symbols(): | |||
|
65 | source = """import foo\na = 10\ndef b():\n return 42\nclass A: pass\n""" | |||
|
66 | symbols_args = ["a", "b", "A", "A,b", "A,a", "z"] | |||
|
67 | expected = [[], | |||
|
68 | ["def b():\n return 42"], | |||
|
69 | ["class A: pass\n"], | |||
|
70 | ["class A: pass\n", "def b():\n return 42"], | |||
|
71 | ["class A: pass\n"], | |||
|
72 | []] | |||
|
73 | for symbols, exp in zip(symbols_args, expected): | |||
|
74 | nt.assert_equal(code.extract_symbols(source, symbols), exp) | |||
|
75 | ||||
|
76 | ||||
|
77 | def test_extract_symbols_ignores_non_python_code(): | |||
|
78 | source = ("=begin A Ruby program :)=end\n" | |||
|
79 | "def hello\n" | |||
|
80 | "puts 'Hello world'\n" | |||
|
81 | "end") | |||
|
82 | nt.assert_equal(code.extract_symbols(source, "hello"), []) | |||
|
83 | ||||
|
84 | ||||
63 | def test_rehashx(): |
|
85 | def test_rehashx(): | |
64 | # clear up everything |
|
86 | # clear up everything | |
65 | _ip = get_ipython() |
|
87 | _ip = get_ipython() |
General Comments 0
You need to be logged in to leave comments.
Login now