Show More
@@ -44,10 +44,11 b' from IPython.utils import py3compat' | |||||
44 | from IPython.utils.io import capture_output |
|
44 | from IPython.utils.io import capture_output | |
45 | from IPython.utils.ipstruct import Struct |
|
45 | from IPython.utils.ipstruct import Struct | |
46 | from IPython.utils.module_paths import find_mod |
|
46 | from IPython.utils.module_paths import find_mod | |
47 | from IPython.utils.path import get_py_filename, unquote_filename |
|
47 | from IPython.utils.path import get_py_filename, unquote_filename, shellglob | |
48 | from IPython.utils.timing import clock, clock2 |
|
48 | from IPython.utils.timing import clock, clock2 | |
49 | from IPython.utils.warn import warn, error |
|
49 | from IPython.utils.warn import warn, error | |
50 |
|
50 | |||
|
51 | ||||
51 | #----------------------------------------------------------------------------- |
|
52 | #----------------------------------------------------------------------------- | |
52 | # Magic implementation classes |
|
53 | # Magic implementation classes | |
53 | #----------------------------------------------------------------------------- |
|
54 | #----------------------------------------------------------------------------- | |
@@ -324,7 +325,7 b' python-profiler package from non-free.""")' | |||||
324 | """Run the named file inside IPython as a program. |
|
325 | """Run the named file inside IPython as a program. | |
325 |
|
326 | |||
326 | Usage:\\ |
|
327 | Usage:\\ | |
327 | %run [-n -i -t [-N<N>] -d [-b<N>] -p [profile options]] file [args] |
|
328 | %run [-n -i -t [-N<N>] -d [-b<N>] -p [profile options] -G] file [args] | |
328 |
|
329 | |||
329 | Parameters after the filename are passed as command-line arguments to |
|
330 | Parameters after the filename are passed as command-line arguments to | |
330 | the program (put in sys.argv). Then, control returns to IPython's |
|
331 | the program (put in sys.argv). Then, control returns to IPython's | |
@@ -345,6 +346,13 b' python-profiler package from non-free.""")' | |||||
345 | and sys.argv). This allows for very convenient loading of code for |
|
346 | and sys.argv). This allows for very convenient loading of code for | |
346 | interactive work, while giving each program a 'clean sheet' to run in. |
|
347 | interactive work, while giving each program a 'clean sheet' to run in. | |
347 |
|
348 | |||
|
349 | Arguments are expanded using shell-like glob match. Patterns | |||
|
350 | '*', '?', '[seq]' and '[!seq]' can be used. Additionally, | |||
|
351 | tilde '~' will be expanded into user's home directory. Unlike | |||
|
352 | real shells, quotation does not suppress expansions. Use | |||
|
353 | *two* back slashes (e.g., '\\\\*') to suppress expansions. | |||
|
354 | To completely disable these expansions, you can use -G flag. | |||
|
355 | ||||
348 | Options: |
|
356 | Options: | |
349 |
|
357 | |||
350 | -n: __name__ is NOT set to '__main__', but to the running file's name |
|
358 | -n: __name__ is NOT set to '__main__', but to the running file's name | |
@@ -439,10 +447,13 b' python-profiler package from non-free.""")' | |||||
439 |
|
447 | |||
440 | will run the example module. |
|
448 | will run the example module. | |
441 |
|
449 | |||
|
450 | -G: disable shell-like glob expansion of arguments. | |||
|
451 | ||||
442 | """ |
|
452 | """ | |
443 |
|
453 | |||
444 | # get arguments and set sys.argv for program to be run. |
|
454 | # get arguments and set sys.argv for program to be run. | |
445 |
opts, arg_lst = self.parse_options(parameter_s, |
|
455 | opts, arg_lst = self.parse_options(parameter_s, | |
|
456 | 'nidtN:b:pD:l:rs:T:em:G', | |||
446 | mode='list', list_all=1) |
|
457 | mode='list', list_all=1) | |
447 | if "m" in opts: |
|
458 | if "m" in opts: | |
448 | modulename = opts["m"][0] |
|
459 | modulename = opts["m"][0] | |
@@ -476,8 +487,11 b' python-profiler package from non-free.""")' | |||||
476 | # were run from a system shell. |
|
487 | # were run from a system shell. | |
477 | save_argv = sys.argv # save it for later restoring |
|
488 | save_argv = sys.argv # save it for later restoring | |
478 |
|
489 | |||
479 | # simulate shell expansion on arguments, at least tilde expansion |
|
490 | if 'G' in opts: | |
480 | args = [ os.path.expanduser(a) for a in arg_lst[1:] ] |
|
491 | args = arg_lst[1:] | |
|
492 | else: | |||
|
493 | # tilde and glob expansion | |||
|
494 | args = shellglob(map(os.path.expanduser, arg_lst[1:])) | |||
481 |
|
495 | |||
482 | sys.argv = [filename] + args # put in the proper filename |
|
496 | sys.argv = [filename] + args # put in the proper filename | |
483 | # protect sys.argv from potential unicode strings on Python 2: |
|
497 | # protect sys.argv from potential unicode strings on Python 2: |
@@ -86,6 +86,28 b' def doctest_run_builtins():' | |||||
86 | ....: |
|
86 | ....: | |
87 | """ |
|
87 | """ | |
88 |
|
88 | |||
|
89 | ||||
|
90 | def doctest_run_option_parser(): | |||
|
91 | r"""Test option parser in %run. | |||
|
92 | ||||
|
93 | In [1]: %run print_argv.py | |||
|
94 | [] | |||
|
95 | ||||
|
96 | In [2]: %run print_argv.py print*.py | |||
|
97 | ['print_argv.py'] | |||
|
98 | ||||
|
99 | In [3]: %run print_argv.py print\\*.py | |||
|
100 | ['print*.py'] | |||
|
101 | ||||
|
102 | In [4]: %run print_argv.py 'print*.py' | |||
|
103 | ['print_argv.py'] | |||
|
104 | ||||
|
105 | In [5]: %run -G print_argv.py print*.py | |||
|
106 | ['print*.py'] | |||
|
107 | ||||
|
108 | """ | |||
|
109 | ||||
|
110 | ||||
89 | @py3compat.doctest_refactor_print |
|
111 | @py3compat.doctest_refactor_print | |
90 | def doctest_reset_del(): |
|
112 | def doctest_reset_del(): | |
91 | """Test that resetting doesn't cause errors in __del__ methods. |
|
113 | """Test that resetting doesn't cause errors in __del__ methods. |
@@ -19,6 +19,7 b' import sys' | |||||
19 | import tempfile |
|
19 | import tempfile | |
20 | import warnings |
|
20 | import warnings | |
21 | from hashlib import md5 |
|
21 | from hashlib import md5 | |
|
22 | import glob | |||
22 |
|
23 | |||
23 | import IPython |
|
24 | import IPython | |
24 | from IPython.testing.skipdoctest import skip_doctest |
|
25 | from IPython.testing.skipdoctest import skip_doctest | |
@@ -355,6 +356,28 b' def expand_path(s):' | |||||
355 | return s |
|
356 | return s | |
356 |
|
357 | |||
357 |
|
358 | |||
|
359 | def unescape_glob(string): | |||
|
360 | """Unescape glob pattern in `string`.""" | |||
|
361 | def unescape(s): | |||
|
362 | for pattern in '*[]!?': | |||
|
363 | s = s.replace(r'\{0}'.format(pattern), pattern) | |||
|
364 | return s | |||
|
365 | return '\\'.join(map(unescape, string.split('\\\\'))) | |||
|
366 | ||||
|
367 | ||||
|
368 | def shellglob(args): | |||
|
369 | """ | |||
|
370 | Do glob expansion for each element in `args` and return a flattened list. | |||
|
371 | ||||
|
372 | Unmatched glob pattern will remain as-is in the returned list. | |||
|
373 | ||||
|
374 | """ | |||
|
375 | expanded = [] | |||
|
376 | for a in args: | |||
|
377 | expanded.extend(glob.glob(a) or [unescape_glob(a)]) | |||
|
378 | return expanded | |||
|
379 | ||||
|
380 | ||||
358 | def target_outdated(target,deps): |
|
381 | def target_outdated(target,deps): | |
359 | """Determine whether a target is out of date. |
|
382 | """Determine whether a target is out of date. | |
360 |
|
383 |
@@ -32,6 +32,7 b' from IPython.testing.decorators import skip_if_not_win32, skip_win32' | |||||
32 | from IPython.testing.tools import make_tempfile, AssertPrints |
|
32 | from IPython.testing.tools import make_tempfile, AssertPrints | |
33 | from IPython.utils import path, io |
|
33 | from IPython.utils import path, io | |
34 | from IPython.utils import py3compat |
|
34 | from IPython.utils import py3compat | |
|
35 | from IPython.utils.tempdir import TemporaryDirectory | |||
35 |
|
36 | |||
36 | # Platform-dependent imports |
|
37 | # Platform-dependent imports | |
37 | try: |
|
38 | try: | |
@@ -444,3 +445,48 b' def test_unicode_in_filename():' | |||||
444 | path.get_py_filename(u'fooéè.py', force_win32=False) |
|
445 | path.get_py_filename(u'fooéè.py', force_win32=False) | |
445 | except IOError as ex: |
|
446 | except IOError as ex: | |
446 | str(ex) |
|
447 | str(ex) | |
|
448 | ||||
|
449 | ||||
|
450 | def test_shellglob(): | |||
|
451 | """Test glob expansion for %run magic.""" | |||
|
452 | filenames_start_with_a = map('a{0}'.format, range(3)) | |||
|
453 | filenames_end_with_b = map('{0}b'.format, range(3)) | |||
|
454 | filenames = filenames_start_with_a + filenames_end_with_b | |||
|
455 | ||||
|
456 | with TemporaryDirectory() as td: | |||
|
457 | save = os.getcwdu() | |||
|
458 | try: | |||
|
459 | os.chdir(td) | |||
|
460 | ||||
|
461 | # Create empty files | |||
|
462 | for fname in filenames: | |||
|
463 | open(os.path.join(td, fname), 'w').close() | |||
|
464 | ||||
|
465 | def assert_match(patterns, matches): | |||
|
466 | # glob returns unordered list. that's why sorted is required. | |||
|
467 | nt.assert_equals(sorted(path.shellglob(patterns)), | |||
|
468 | sorted(matches)) | |||
|
469 | ||||
|
470 | assert_match(['*'], filenames) | |||
|
471 | assert_match(['a*'], filenames_start_with_a) | |||
|
472 | assert_match(['*c'], ['*c']) | |||
|
473 | assert_match(['*', 'a*', '*b', '*c'], | |||
|
474 | filenames | |||
|
475 | + filenames_start_with_a | |||
|
476 | + filenames_end_with_b | |||
|
477 | + ['*c']) | |||
|
478 | ||||
|
479 | assert_match([r'\*'], ['*']) | |||
|
480 | assert_match([r'a\*', 'a*'], ['a*'] + filenames_start_with_a) | |||
|
481 | assert_match(['a[012]'], filenames_start_with_a) | |||
|
482 | assert_match([r'a\[012]'], ['a[012]']) | |||
|
483 | finally: | |||
|
484 | os.chdir(save) | |||
|
485 | ||||
|
486 | ||||
|
487 | def test_unescape_glob(): | |||
|
488 | nt.assert_equals(path.unescape_glob(r'\*\[\!\]\?'), '*[!]?') | |||
|
489 | nt.assert_equals(path.unescape_glob(r'\\*'), r'\*') | |||
|
490 | nt.assert_equals(path.unescape_glob(r'\\\*'), r'\*') | |||
|
491 | nt.assert_equals(path.unescape_glob(r'\\a'), r'\a') | |||
|
492 | nt.assert_equals(path.unescape_glob(r'\a'), r'\a') |
General Comments 0
You need to be logged in to leave comments.
Login now