##// END OF EJS Templates
adds the optional parameter -s to the magic %load that loads specific function or classes from the python source given. forcing the patch ignoring whitespace changes
Martín Gaitán -
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