##// END OF EJS Templates
Merge pull request #13669 from achhina/fix-issue-12967...
Matthias Bussonnier -
r27758:c2c6349f merge
parent child Browse files
Show More
@@ -1,854 +1,855 b''
1 1 """Implementation of magic functions for interaction with the OS.
2 2
3 3 Note: this module is named 'osm' instead of 'os' to avoid a collision with the
4 4 builtin.
5 5 """
6 6 # Copyright (c) IPython Development Team.
7 7 # Distributed under the terms of the Modified BSD License.
8 8
9 9 import io
10 10 import os
11 import pathlib
11 12 import re
12 13 import sys
13 14 from pprint import pformat
14 15
15 16 from IPython.core import magic_arguments
16 17 from IPython.core import oinspect
17 18 from IPython.core import page
18 19 from IPython.core.alias import AliasError, Alias
19 20 from IPython.core.error import UsageError
20 21 from IPython.core.magic import (
21 22 Magics, compress_dhist, magics_class, line_magic, cell_magic, line_cell_magic
22 23 )
23 24 from IPython.testing.skipdoctest import skip_doctest
24 25 from IPython.utils.openpy import source_to_unicode
25 26 from IPython.utils.process import abbrev_cwd
26 27 from IPython.utils.terminal import set_term_title
27 28 from traitlets import Bool
28 29 from warnings import warn
29 30
30 31
31 32 @magics_class
32 33 class OSMagics(Magics):
33 34 """Magics to interact with the underlying OS (shell-type functionality).
34 35 """
35 36
36 37 cd_force_quiet = Bool(False,
37 38 help="Force %cd magic to be quiet even if -q is not passed."
38 39 ).tag(config=True)
39 40
40 41 def __init__(self, shell=None, **kwargs):
41 42
42 43 # Now define isexec in a cross platform manner.
43 44 self.is_posix = False
44 45 self.execre = None
45 46 if os.name == 'posix':
46 47 self.is_posix = True
47 48 else:
48 49 try:
49 50 winext = os.environ['pathext'].replace(';','|').replace('.','')
50 51 except KeyError:
51 52 winext = 'exe|com|bat|py'
52 53 try:
53 54 self.execre = re.compile(r'(.*)\.(%s)$' % winext,re.IGNORECASE)
54 55 except re.error:
55 56 warn("Seems like your pathext environmental "
56 57 "variable is malformed. Please check it to "
57 58 "enable a proper handle of file extensions "
58 59 "managed for your system")
59 60 winext = 'exe|com|bat|py'
60 61 self.execre = re.compile(r'(.*)\.(%s)$' % winext,re.IGNORECASE)
61 62
62 63 # call up the chain
63 64 super().__init__(shell=shell, **kwargs)
64 65
65 66
66 67 def _isexec_POSIX(self, file):
67 68 """
68 69 Test for executable on a POSIX system
69 70 """
70 71 if os.access(file.path, os.X_OK):
71 72 # will fail on maxOS if access is not X_OK
72 73 return file.is_file()
73 74 return False
74 75
75 76
76 77
77 78 def _isexec_WIN(self, file):
78 79 """
79 80 Test for executable file on non POSIX system
80 81 """
81 82 return file.is_file() and self.execre.match(file.name) is not None
82 83
83 84 def isexec(self, file):
84 85 """
85 86 Test for executable file on non POSIX system
86 87 """
87 88 if self.is_posix:
88 89 return self._isexec_POSIX(file)
89 90 else:
90 91 return self._isexec_WIN(file)
91 92
92 93
93 94 @skip_doctest
94 95 @line_magic
95 96 def alias(self, parameter_s=''):
96 97 """Define an alias for a system command.
97 98
98 99 '%alias alias_name cmd' defines 'alias_name' as an alias for 'cmd'
99 100
100 101 Then, typing 'alias_name params' will execute the system command 'cmd
101 102 params' (from your underlying operating system).
102 103
103 104 Aliases have lower precedence than magic functions and Python normal
104 105 variables, so if 'foo' is both a Python variable and an alias, the
105 106 alias can not be executed until 'del foo' removes the Python variable.
106 107
107 108 You can use the %l specifier in an alias definition to represent the
108 109 whole line when the alias is called. For example::
109 110
110 111 In [2]: alias bracket echo "Input in brackets: <%l>"
111 112 In [3]: bracket hello world
112 113 Input in brackets: <hello world>
113 114
114 115 You can also define aliases with parameters using %s specifiers (one
115 116 per parameter)::
116 117
117 118 In [1]: alias parts echo first %s second %s
118 119 In [2]: %parts A B
119 120 first A second B
120 121 In [3]: %parts A
121 122 Incorrect number of arguments: 2 expected.
122 123 parts is an alias to: 'echo first %s second %s'
123 124
124 125 Note that %l and %s are mutually exclusive. You can only use one or
125 126 the other in your aliases.
126 127
127 128 Aliases expand Python variables just like system calls using ! or !!
128 129 do: all expressions prefixed with '$' get expanded. For details of
129 130 the semantic rules, see PEP-215:
130 131 https://peps.python.org/pep-0215/. This is the library used by
131 132 IPython for variable expansion. If you want to access a true shell
132 133 variable, an extra $ is necessary to prevent its expansion by
133 134 IPython::
134 135
135 136 In [6]: alias show echo
136 137 In [7]: PATH='A Python string'
137 138 In [8]: show $PATH
138 139 A Python string
139 140 In [9]: show $$PATH
140 141 /usr/local/lf9560/bin:/usr/local/intel/compiler70/ia32/bin:...
141 142
142 143 You can use the alias facility to access all of $PATH. See the %rehashx
143 144 function, which automatically creates aliases for the contents of your
144 145 $PATH.
145 146
146 147 If called with no parameters, %alias prints the current alias table
147 148 for your system. For posix systems, the default aliases are 'cat',
148 149 'cp', 'mv', 'rm', 'rmdir', and 'mkdir', and other platform-specific
149 150 aliases are added. For windows-based systems, the default aliases are
150 151 'copy', 'ddir', 'echo', 'ls', 'ldir', 'mkdir', 'ren', and 'rmdir'.
151 152
152 153 You can see the definition of alias by adding a question mark in the
153 154 end::
154 155
155 156 In [1]: cat?
156 157 Repr: <alias cat for 'cat'>"""
157 158
158 159 par = parameter_s.strip()
159 160 if not par:
160 161 aliases = sorted(self.shell.alias_manager.aliases)
161 162 # stored = self.shell.db.get('stored_aliases', {} )
162 163 # for k, v in stored:
163 164 # atab.append(k, v[0])
164 165
165 166 print("Total number of aliases:", len(aliases))
166 167 sys.stdout.flush()
167 168 return aliases
168 169
169 170 # Now try to define a new one
170 171 try:
171 172 alias,cmd = par.split(None, 1)
172 173 except TypeError:
173 174 print(oinspect.getdoc(self.alias))
174 175 return
175 176
176 177 try:
177 178 self.shell.alias_manager.define_alias(alias, cmd)
178 179 except AliasError as e:
179 180 print(e)
180 181 # end magic_alias
181 182
182 183 @line_magic
183 184 def unalias(self, parameter_s=''):
184 185 """Remove an alias"""
185 186
186 187 aname = parameter_s.strip()
187 188 try:
188 189 self.shell.alias_manager.undefine_alias(aname)
189 190 except ValueError as e:
190 191 print(e)
191 192 return
192 193
193 194 stored = self.shell.db.get('stored_aliases', {} )
194 195 if aname in stored:
195 196 print("Removing %stored alias",aname)
196 197 del stored[aname]
197 198 self.shell.db['stored_aliases'] = stored
198 199
199 200 @line_magic
200 201 def rehashx(self, parameter_s=''):
201 202 """Update the alias table with all executable files in $PATH.
202 203
203 204 rehashx explicitly checks that every entry in $PATH is a file
204 205 with execute access (os.X_OK).
205 206
206 207 Under Windows, it checks executability as a match against a
207 208 '|'-separated string of extensions, stored in the IPython config
208 209 variable win_exec_ext. This defaults to 'exe|com|bat'.
209 210
210 211 This function also resets the root module cache of module completer,
211 212 used on slow filesystems.
212 213 """
213 214 from IPython.core.alias import InvalidAliasError
214 215
215 216 # for the benefit of module completer in ipy_completers.py
216 217 del self.shell.db['rootmodules_cache']
217 218
218 219 path = [os.path.abspath(os.path.expanduser(p)) for p in
219 220 os.environ.get('PATH','').split(os.pathsep)]
220 221
221 222 syscmdlist = []
222 223 savedir = os.getcwd()
223 224
224 225 # Now walk the paths looking for executables to alias.
225 226 try:
226 227 # write the whole loop for posix/Windows so we don't have an if in
227 228 # the innermost part
228 229 if self.is_posix:
229 230 for pdir in path:
230 231 try:
231 232 os.chdir(pdir)
232 233 except OSError:
233 234 continue
234 235
235 236 # for python 3.6+ rewrite to: with os.scandir(pdir) as dirlist:
236 237 dirlist = os.scandir(path=pdir)
237 238 for ff in dirlist:
238 239 if self.isexec(ff):
239 240 fname = ff.name
240 241 try:
241 242 # Removes dots from the name since ipython
242 243 # will assume names with dots to be python.
243 244 if not self.shell.alias_manager.is_alias(fname):
244 245 self.shell.alias_manager.define_alias(
245 246 fname.replace('.',''), fname)
246 247 except InvalidAliasError:
247 248 pass
248 249 else:
249 250 syscmdlist.append(fname)
250 251 else:
251 252 no_alias = Alias.blacklist
252 253 for pdir in path:
253 254 try:
254 255 os.chdir(pdir)
255 256 except OSError:
256 257 continue
257 258
258 259 # for python 3.6+ rewrite to: with os.scandir(pdir) as dirlist:
259 260 dirlist = os.scandir(pdir)
260 261 for ff in dirlist:
261 262 fname = ff.name
262 263 base, ext = os.path.splitext(fname)
263 264 if self.isexec(ff) and base.lower() not in no_alias:
264 265 if ext.lower() == '.exe':
265 266 fname = base
266 267 try:
267 268 # Removes dots from the name since ipython
268 269 # will assume names with dots to be python.
269 270 self.shell.alias_manager.define_alias(
270 271 base.lower().replace('.',''), fname)
271 272 except InvalidAliasError:
272 273 pass
273 274 syscmdlist.append(fname)
274 275
275 276 self.shell.db['syscmdlist'] = syscmdlist
276 277 finally:
277 278 os.chdir(savedir)
278 279
279 280 @skip_doctest
280 281 @line_magic
281 282 def pwd(self, parameter_s=''):
282 283 """Return the current working directory path.
283 284
284 285 Examples
285 286 --------
286 287 ::
287 288
288 289 In [9]: pwd
289 290 Out[9]: '/home/tsuser/sprint/ipython'
290 291 """
291 292 try:
292 293 return os.getcwd()
293 294 except FileNotFoundError as e:
294 295 raise UsageError("CWD no longer exists - please use %cd to change directory.") from e
295 296
296 297 @skip_doctest
297 298 @line_magic
298 299 def cd(self, parameter_s=''):
299 300 """Change the current working directory.
300 301
301 302 This command automatically maintains an internal list of directories
302 303 you visit during your IPython session, in the variable ``_dh``. The
303 304 command :magic:`%dhist` shows this history nicely formatted. You can
304 305 also do ``cd -<tab>`` to see directory history conveniently.
305 306 Usage:
306 307
307 308 - ``cd 'dir'``: changes to directory 'dir'.
308 309 - ``cd -``: changes to the last visited directory.
309 310 - ``cd -<n>``: changes to the n-th directory in the directory history.
310 311 - ``cd --foo``: change to directory that matches 'foo' in history
311 312 - ``cd -b <bookmark_name>``: jump to a bookmark set by %bookmark
312 313 - Hitting a tab key after ``cd -b`` allows you to tab-complete
313 314 bookmark names.
314 315
315 316 .. note::
316 317 ``cd <bookmark_name>`` is enough if there is no directory
317 318 ``<bookmark_name>``, but a bookmark with the name exists.
318 319
319 320 Options:
320 321
321 322 -q Be quiet. Do not print the working directory after the
322 323 cd command is executed. By default IPython's cd
323 324 command does print this directory, since the default
324 325 prompts do not display path information.
325 326
326 327 .. note::
327 328 Note that ``!cd`` doesn't work for this purpose because the shell
328 329 where ``!command`` runs is immediately discarded after executing
329 330 'command'.
330 331
331 332 Examples
332 333 --------
333 334 ::
334 335
335 336 In [10]: cd parent/child
336 337 /home/tsuser/parent/child
337 338 """
338 339
339 340 try:
340 341 oldcwd = os.getcwd()
341 342 except FileNotFoundError:
342 343 # Happens if the CWD has been deleted.
343 344 oldcwd = None
344 345
345 346 numcd = re.match(r'(-)(\d+)$',parameter_s)
346 347 # jump in directory history by number
347 348 if numcd:
348 349 nn = int(numcd.group(2))
349 350 try:
350 351 ps = self.shell.user_ns['_dh'][nn]
351 352 except IndexError:
352 353 print('The requested directory does not exist in history.')
353 354 return
354 355 else:
355 356 opts = {}
356 357 elif parameter_s.startswith('--'):
357 358 ps = None
358 359 fallback = None
359 360 pat = parameter_s[2:]
360 361 dh = self.shell.user_ns['_dh']
361 362 # first search only by basename (last component)
362 363 for ent in reversed(dh):
363 364 if pat in os.path.basename(ent) and os.path.isdir(ent):
364 365 ps = ent
365 366 break
366 367
367 368 if fallback is None and pat in ent and os.path.isdir(ent):
368 369 fallback = ent
369 370
370 371 # if we have no last part match, pick the first full path match
371 372 if ps is None:
372 373 ps = fallback
373 374
374 375 if ps is None:
375 376 print("No matching entry in directory history")
376 377 return
377 378 else:
378 379 opts = {}
379 380
380 381
381 382 else:
382 383 opts, ps = self.parse_options(parameter_s, 'qb', mode='string')
383 384 # jump to previous
384 385 if ps == '-':
385 386 try:
386 387 ps = self.shell.user_ns['_dh'][-2]
387 388 except IndexError as e:
388 389 raise UsageError('%cd -: No previous directory to change to.') from e
389 390 # jump to bookmark if needed
390 391 else:
391 392 if not os.path.isdir(ps) or 'b' in opts:
392 393 bkms = self.shell.db.get('bookmarks', {})
393 394
394 395 if ps in bkms:
395 396 target = bkms[ps]
396 397 print('(bookmark:%s) -> %s' % (ps, target))
397 398 ps = target
398 399 else:
399 400 if 'b' in opts:
400 401 raise UsageError("Bookmark '%s' not found. "
401 402 "Use '%%bookmark -l' to see your bookmarks." % ps)
402 403
403 404 # at this point ps should point to the target dir
404 405 if ps:
405 406 try:
406 407 os.chdir(os.path.expanduser(ps))
407 408 if hasattr(self.shell, 'term_title') and self.shell.term_title:
408 409 set_term_title(self.shell.term_title_format.format(cwd=abbrev_cwd()))
409 410 except OSError:
410 411 print(sys.exc_info()[1])
411 412 else:
412 cwd = os.getcwd()
413 cwd = pathlib.Path.cwd()
413 414 dhist = self.shell.user_ns['_dh']
414 415 if oldcwd != cwd:
415 416 dhist.append(cwd)
416 417 self.shell.db['dhist'] = compress_dhist(dhist)[-100:]
417 418
418 419 else:
419 420 os.chdir(self.shell.home_dir)
420 421 if hasattr(self.shell, 'term_title') and self.shell.term_title:
421 422 set_term_title(self.shell.term_title_format.format(cwd="~"))
422 cwd = os.getcwd()
423 cwd = pathlib.Path.cwd()
423 424 dhist = self.shell.user_ns['_dh']
424 425
425 426 if oldcwd != cwd:
426 427 dhist.append(cwd)
427 428 self.shell.db['dhist'] = compress_dhist(dhist)[-100:]
428 429 if not 'q' in opts and not self.cd_force_quiet and self.shell.user_ns['_dh']:
429 430 print(self.shell.user_ns['_dh'][-1])
430 431
431 432 @line_magic
432 433 def env(self, parameter_s=''):
433 434 """Get, set, or list environment variables.
434 435
435 436 Usage:\\
436 437
437 438 :``%env``: lists all environment variables/values
438 439 :``%env var``: get value for var
439 440 :``%env var val``: set value for var
440 441 :``%env var=val``: set value for var
441 442 :``%env var=$val``: set value for var, using python expansion if possible
442 443 """
443 444 if parameter_s.strip():
444 445 split = '=' if '=' in parameter_s else ' '
445 446 bits = parameter_s.split(split)
446 447 if len(bits) == 1:
447 448 key = parameter_s.strip()
448 449 if key in os.environ:
449 450 return os.environ[key]
450 451 else:
451 452 err = "Environment does not have key: {0}".format(key)
452 453 raise UsageError(err)
453 454 if len(bits) > 1:
454 455 return self.set_env(parameter_s)
455 456 env = dict(os.environ)
456 457 # hide likely secrets when printing the whole environment
457 458 for key in list(env):
458 459 if any(s in key.lower() for s in ('key', 'token', 'secret')):
459 460 env[key] = '<hidden>'
460 461
461 462 return env
462 463
463 464 @line_magic
464 465 def set_env(self, parameter_s):
465 466 """Set environment variables. Assumptions are that either "val" is a
466 467 name in the user namespace, or val is something that evaluates to a
467 468 string.
468 469
469 470 Usage:\\
470 471 %set_env var val: set value for var
471 472 %set_env var=val: set value for var
472 473 %set_env var=$val: set value for var, using python expansion if possible
473 474 """
474 475 split = '=' if '=' in parameter_s else ' '
475 476 bits = parameter_s.split(split, 1)
476 477 if not parameter_s.strip() or len(bits)<2:
477 478 raise UsageError("usage is 'set_env var=val'")
478 479 var = bits[0].strip()
479 480 val = bits[1].strip()
480 481 if re.match(r'.*\s.*', var):
481 482 # an environment variable with whitespace is almost certainly
482 483 # not what the user intended. what's more likely is the wrong
483 484 # split was chosen, ie for "set_env cmd_args A=B", we chose
484 485 # '=' for the split and should have chosen ' '. to get around
485 486 # this, users should just assign directly to os.environ or use
486 487 # standard magic {var} expansion.
487 488 err = "refusing to set env var with whitespace: '{0}'"
488 489 err = err.format(val)
489 490 raise UsageError(err)
490 491 os.environ[var] = val
491 492 print('env: {0}={1}'.format(var,val))
492 493
493 494 @line_magic
494 495 def pushd(self, parameter_s=''):
495 496 """Place the current dir on stack and change directory.
496 497
497 498 Usage:\\
498 499 %pushd ['dirname']
499 500 """
500 501
501 502 dir_s = self.shell.dir_stack
502 503 tgt = os.path.expanduser(parameter_s)
503 504 cwd = os.getcwd().replace(self.shell.home_dir,'~')
504 505 if tgt:
505 506 self.cd(parameter_s)
506 507 dir_s.insert(0,cwd)
507 508 return self.shell.run_line_magic('dirs', '')
508 509
509 510 @line_magic
510 511 def popd(self, parameter_s=''):
511 512 """Change to directory popped off the top of the stack.
512 513 """
513 514 if not self.shell.dir_stack:
514 515 raise UsageError("%popd on empty stack")
515 516 top = self.shell.dir_stack.pop(0)
516 517 self.cd(top)
517 518 print("popd ->",top)
518 519
519 520 @line_magic
520 521 def dirs(self, parameter_s=''):
521 522 """Return the current directory stack."""
522 523
523 524 return self.shell.dir_stack
524 525
525 526 @line_magic
526 527 def dhist(self, parameter_s=''):
527 528 """Print your history of visited directories.
528 529
529 530 %dhist -> print full history\\
530 531 %dhist n -> print last n entries only\\
531 532 %dhist n1 n2 -> print entries between n1 and n2 (n2 not included)\\
532 533
533 534 This history is automatically maintained by the %cd command, and
534 535 always available as the global list variable _dh. You can use %cd -<n>
535 536 to go to directory number <n>.
536 537
537 538 Note that most of time, you should view directory history by entering
538 539 cd -<TAB>.
539 540
540 541 """
541 542
542 543 dh = self.shell.user_ns['_dh']
543 544 if parameter_s:
544 545 try:
545 546 args = map(int,parameter_s.split())
546 547 except:
547 548 self.arg_err(self.dhist)
548 549 return
549 550 if len(args) == 1:
550 551 ini,fin = max(len(dh)-(args[0]),0),len(dh)
551 552 elif len(args) == 2:
552 553 ini,fin = args
553 554 fin = min(fin, len(dh))
554 555 else:
555 556 self.arg_err(self.dhist)
556 557 return
557 558 else:
558 559 ini,fin = 0,len(dh)
559 560 print('Directory history (kept in _dh)')
560 561 for i in range(ini, fin):
561 562 print("%d: %s" % (i, dh[i]))
562 563
563 564 @skip_doctest
564 565 @line_magic
565 566 def sc(self, parameter_s=''):
566 567 """Shell capture - run shell command and capture output (DEPRECATED use !).
567 568
568 569 DEPRECATED. Suboptimal, retained for backwards compatibility.
569 570
570 571 You should use the form 'var = !command' instead. Example:
571 572
572 573 "%sc -l myfiles = ls ~" should now be written as
573 574
574 575 "myfiles = !ls ~"
575 576
576 577 myfiles.s, myfiles.l and myfiles.n still apply as documented
577 578 below.
578 579
579 580 --
580 581 %sc [options] varname=command
581 582
582 583 IPython will run the given command using commands.getoutput(), and
583 584 will then update the user's interactive namespace with a variable
584 585 called varname, containing the value of the call. Your command can
585 586 contain shell wildcards, pipes, etc.
586 587
587 588 The '=' sign in the syntax is mandatory, and the variable name you
588 589 supply must follow Python's standard conventions for valid names.
589 590
590 591 (A special format without variable name exists for internal use)
591 592
592 593 Options:
593 594
594 595 -l: list output. Split the output on newlines into a list before
595 596 assigning it to the given variable. By default the output is stored
596 597 as a single string.
597 598
598 599 -v: verbose. Print the contents of the variable.
599 600
600 601 In most cases you should not need to split as a list, because the
601 602 returned value is a special type of string which can automatically
602 603 provide its contents either as a list (split on newlines) or as a
603 604 space-separated string. These are convenient, respectively, either
604 605 for sequential processing or to be passed to a shell command.
605 606
606 607 For example::
607 608
608 609 # Capture into variable a
609 610 In [1]: sc a=ls *py
610 611
611 612 # a is a string with embedded newlines
612 613 In [2]: a
613 614 Out[2]: 'setup.py\\nwin32_manual_post_install.py'
614 615
615 616 # which can be seen as a list:
616 617 In [3]: a.l
617 618 Out[3]: ['setup.py', 'win32_manual_post_install.py']
618 619
619 620 # or as a whitespace-separated string:
620 621 In [4]: a.s
621 622 Out[4]: 'setup.py win32_manual_post_install.py'
622 623
623 624 # a.s is useful to pass as a single command line:
624 625 In [5]: !wc -l $a.s
625 626 146 setup.py
626 627 130 win32_manual_post_install.py
627 628 276 total
628 629
629 630 # while the list form is useful to loop over:
630 631 In [6]: for f in a.l:
631 632 ...: !wc -l $f
632 633 ...:
633 634 146 setup.py
634 635 130 win32_manual_post_install.py
635 636
636 637 Similarly, the lists returned by the -l option are also special, in
637 638 the sense that you can equally invoke the .s attribute on them to
638 639 automatically get a whitespace-separated string from their contents::
639 640
640 641 In [7]: sc -l b=ls *py
641 642
642 643 In [8]: b
643 644 Out[8]: ['setup.py', 'win32_manual_post_install.py']
644 645
645 646 In [9]: b.s
646 647 Out[9]: 'setup.py win32_manual_post_install.py'
647 648
648 649 In summary, both the lists and strings used for output capture have
649 650 the following special attributes::
650 651
651 652 .l (or .list) : value as list.
652 653 .n (or .nlstr): value as newline-separated string.
653 654 .s (or .spstr): value as space-separated string.
654 655 """
655 656
656 657 opts,args = self.parse_options(parameter_s, 'lv')
657 658 # Try to get a variable name and command to run
658 659 try:
659 660 # the variable name must be obtained from the parse_options
660 661 # output, which uses shlex.split to strip options out.
661 662 var,_ = args.split('=', 1)
662 663 var = var.strip()
663 664 # But the command has to be extracted from the original input
664 665 # parameter_s, not on what parse_options returns, to avoid the
665 666 # quote stripping which shlex.split performs on it.
666 667 _,cmd = parameter_s.split('=', 1)
667 668 except ValueError:
668 669 var,cmd = '',''
669 670 # If all looks ok, proceed
670 671 split = 'l' in opts
671 672 out = self.shell.getoutput(cmd, split=split)
672 673 if 'v' in opts:
673 674 print('%s ==\n%s' % (var, pformat(out)))
674 675 if var:
675 676 self.shell.user_ns.update({var:out})
676 677 else:
677 678 return out
678 679
679 680 @line_cell_magic
680 681 def sx(self, line='', cell=None):
681 682 """Shell execute - run shell command and capture output (!! is short-hand).
682 683
683 684 %sx command
684 685
685 686 IPython will run the given command using commands.getoutput(), and
686 687 return the result formatted as a list (split on '\\n'). Since the
687 688 output is _returned_, it will be stored in ipython's regular output
688 689 cache Out[N] and in the '_N' automatic variables.
689 690
690 691 Notes:
691 692
692 693 1) If an input line begins with '!!', then %sx is automatically
693 694 invoked. That is, while::
694 695
695 696 !ls
696 697
697 698 causes ipython to simply issue system('ls'), typing::
698 699
699 700 !!ls
700 701
701 702 is a shorthand equivalent to::
702 703
703 704 %sx ls
704 705
705 706 2) %sx differs from %sc in that %sx automatically splits into a list,
706 707 like '%sc -l'. The reason for this is to make it as easy as possible
707 708 to process line-oriented shell output via further python commands.
708 709 %sc is meant to provide much finer control, but requires more
709 710 typing.
710 711
711 712 3) Just like %sc -l, this is a list with special attributes:
712 713 ::
713 714
714 715 .l (or .list) : value as list.
715 716 .n (or .nlstr): value as newline-separated string.
716 717 .s (or .spstr): value as whitespace-separated string.
717 718
718 719 This is very useful when trying to use such lists as arguments to
719 720 system commands."""
720 721
721 722 if cell is None:
722 723 # line magic
723 724 return self.shell.getoutput(line)
724 725 else:
725 726 opts,args = self.parse_options(line, '', 'out=')
726 727 output = self.shell.getoutput(cell)
727 728 out_name = opts.get('out', opts.get('o'))
728 729 if out_name:
729 730 self.shell.user_ns[out_name] = output
730 731 else:
731 732 return output
732 733
733 734 system = line_cell_magic('system')(sx)
734 735 bang = cell_magic('!')(sx)
735 736
736 737 @line_magic
737 738 def bookmark(self, parameter_s=''):
738 739 """Manage IPython's bookmark system.
739 740
740 741 %bookmark <name> - set bookmark to current dir
741 742 %bookmark <name> <dir> - set bookmark to <dir>
742 743 %bookmark -l - list all bookmarks
743 744 %bookmark -d <name> - remove bookmark
744 745 %bookmark -r - remove all bookmarks
745 746
746 747 You can later on access a bookmarked folder with::
747 748
748 749 %cd -b <name>
749 750
750 751 or simply '%cd <name>' if there is no directory called <name> AND
751 752 there is such a bookmark defined.
752 753
753 754 Your bookmarks persist through IPython sessions, but they are
754 755 associated with each profile."""
755 756
756 757 opts,args = self.parse_options(parameter_s,'drl',mode='list')
757 758 if len(args) > 2:
758 759 raise UsageError("%bookmark: too many arguments")
759 760
760 761 bkms = self.shell.db.get('bookmarks',{})
761 762
762 763 if 'd' in opts:
763 764 try:
764 765 todel = args[0]
765 766 except IndexError as e:
766 767 raise UsageError(
767 768 "%bookmark -d: must provide a bookmark to delete") from e
768 769 else:
769 770 try:
770 771 del bkms[todel]
771 772 except KeyError as e:
772 773 raise UsageError(
773 774 "%%bookmark -d: Can't delete bookmark '%s'" % todel) from e
774 775
775 776 elif 'r' in opts:
776 777 bkms = {}
777 778 elif 'l' in opts:
778 779 bks = sorted(bkms)
779 780 if bks:
780 781 size = max(map(len, bks))
781 782 else:
782 783 size = 0
783 784 fmt = '%-'+str(size)+'s -> %s'
784 785 print('Current bookmarks:')
785 786 for bk in bks:
786 787 print(fmt % (bk, bkms[bk]))
787 788 else:
788 789 if not args:
789 790 raise UsageError("%bookmark: You must specify the bookmark name")
790 791 elif len(args)==1:
791 792 bkms[args[0]] = os.getcwd()
792 793 elif len(args)==2:
793 794 bkms[args[0]] = args[1]
794 795 self.shell.db['bookmarks'] = bkms
795 796
796 797 @line_magic
797 798 def pycat(self, parameter_s=''):
798 799 """Show a syntax-highlighted file through a pager.
799 800
800 801 This magic is similar to the cat utility, but it will assume the file
801 802 to be Python source and will show it with syntax highlighting.
802 803
803 804 This magic command can either take a local filename, an url,
804 805 an history range (see %history) or a macro as argument.
805 806
806 807 If no parameter is given, prints out history of current session up to
807 808 this point. ::
808 809
809 810 %pycat myscript.py
810 811 %pycat 7-27
811 812 %pycat myMacro
812 813 %pycat http://www.example.com/myscript.py
813 814 """
814 815 try:
815 816 cont = self.shell.find_user_code(parameter_s, skip_encoding_cookie=False)
816 817 except (ValueError, IOError):
817 818 print("Error: no such file, variable, URL, history range or macro")
818 819 return
819 820
820 821 page.page(self.shell.pycolorize(source_to_unicode(cont)))
821 822
822 823 @magic_arguments.magic_arguments()
823 824 @magic_arguments.argument(
824 825 '-a', '--append', action='store_true', default=False,
825 826 help='Append contents of the cell to an existing file. '
826 827 'The file will be created if it does not exist.'
827 828 )
828 829 @magic_arguments.argument(
829 830 'filename', type=str,
830 831 help='file to write'
831 832 )
832 833 @cell_magic
833 834 def writefile(self, line, cell):
834 835 """Write the contents of the cell to a file.
835 836
836 837 The file will be overwritten unless the -a (--append) flag is specified.
837 838 """
838 839 args = magic_arguments.parse_argstring(self.writefile, line)
839 840 if re.match(r'^(\'.*\')|(".*")$', args.filename):
840 841 filename = os.path.expanduser(args.filename[1:-1])
841 842 else:
842 843 filename = os.path.expanduser(args.filename)
843 844
844 845 if os.path.exists(filename):
845 846 if args.append:
846 847 print("Appending to %s" % filename)
847 848 else:
848 849 print("Overwriting %s" % filename)
849 850 else:
850 851 print("Writing %s" % filename)
851 852
852 853 mode = 'a' if args.append else 'w'
853 854 with io.open(filename, mode, encoding='utf-8') as f:
854 855 f.write(cell)
General Comments 0
You need to be logged in to leave comments. Login now