Show More
@@ -44,10 +44,11 b' from IPython.utils import py3compat' | |||
|
44 | 44 | from IPython.utils.io import capture_output |
|
45 | 45 | from IPython.utils.ipstruct import Struct |
|
46 | 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 | 48 | from IPython.utils.timing import clock, clock2 |
|
49 | 49 | from IPython.utils.warn import warn, error |
|
50 | 50 | |
|
51 | ||
|
51 | 52 | #----------------------------------------------------------------------------- |
|
52 | 53 | # Magic implementation classes |
|
53 | 54 | #----------------------------------------------------------------------------- |
@@ -324,7 +325,7 b' python-profiler package from non-free.""")' | |||
|
324 | 325 | """Run the named file inside IPython as a program. |
|
325 | 326 | |
|
326 | 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 | 330 | Parameters after the filename are passed as command-line arguments to |
|
330 | 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 | 346 | and sys.argv). This allows for very convenient loading of code for |
|
346 | 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 | 356 | Options: |
|
349 | 357 | |
|
350 | 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 | 448 | will run the example module. |
|
441 | 449 | |
|
450 | -G: disable shell-like glob expansion of arguments. | |
|
451 | ||
|
442 | 452 | """ |
|
443 | 453 | |
|
444 | 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 | 457 | mode='list', list_all=1) |
|
447 | 458 | if "m" in opts: |
|
448 | 459 | modulename = opts["m"][0] |
@@ -476,8 +487,11 b' python-profiler package from non-free.""")' | |||
|
476 | 487 | # were run from a system shell. |
|
477 | 488 | save_argv = sys.argv # save it for later restoring |
|
478 | 489 | |
|
479 | # simulate shell expansion on arguments, at least tilde expansion | |
|
480 | args = [ os.path.expanduser(a) for a in arg_lst[1:] ] | |
|
490 | if 'G' in opts: | |
|
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 | 496 | sys.argv = [filename] + args # put in the proper filename |
|
483 | 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 | 111 | @py3compat.doctest_refactor_print |
|
90 | 112 | def doctest_reset_del(): |
|
91 | 113 | """Test that resetting doesn't cause errors in __del__ methods. |
@@ -19,6 +19,7 b' import sys' | |||
|
19 | 19 | import tempfile |
|
20 | 20 | import warnings |
|
21 | 21 | from hashlib import md5 |
|
22 | import glob | |
|
22 | 23 | |
|
23 | 24 | import IPython |
|
24 | 25 | from IPython.testing.skipdoctest import skip_doctest |
@@ -355,6 +356,28 b' def expand_path(s):' | |||
|
355 | 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 | 381 | def target_outdated(target,deps): |
|
359 | 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 | 32 | from IPython.testing.tools import make_tempfile, AssertPrints |
|
33 | 33 | from IPython.utils import path, io |
|
34 | 34 | from IPython.utils import py3compat |
|
35 | from IPython.utils.tempdir import TemporaryDirectory | |
|
35 | 36 | |
|
36 | 37 | # Platform-dependent imports |
|
37 | 38 | try: |
@@ -444,3 +445,48 b' def test_unicode_in_filename():' | |||
|
444 | 445 | path.get_py_filename(u'fooéè.py', force_win32=False) |
|
445 | 446 | except IOError as ex: |
|
446 | 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