osm.py
790 lines
| 27.9 KiB
| text/x-python
|
PythonLexer
Fernando Perez
|
r6965 | """Implementation of magic functions for interaction with the OS. | ||
Note: this module is named 'osm' instead of 'os' to avoid a collision with the | ||||
builtin. | ||||
""" | ||||
Thomas Kluyver
|
r13348 | from __future__ import print_function | ||
Fernando Perez
|
r6965 | #----------------------------------------------------------------------------- | ||
# Copyright (c) 2012 The IPython Development Team. | ||||
# | ||||
# Distributed under the terms of the Modified BSD License. | ||||
# | ||||
# The full license is in the file COPYING.txt, distributed with this software. | ||||
#----------------------------------------------------------------------------- | ||||
#----------------------------------------------------------------------------- | ||||
# Imports | ||||
#----------------------------------------------------------------------------- | ||||
# Stdlib | ||||
MinRK
|
r7305 | import io | ||
Fernando Perez
|
r6965 | import os | ||
import re | ||||
import sys | ||||
from pprint import pformat | ||||
# Our own packages | ||||
MinRK
|
r7297 | from IPython.core import magic_arguments | ||
Fernando Perez
|
r6965 | from IPython.core import oinspect | ||
from IPython.core import page | ||||
Thomas Kluyver
|
r12627 | from IPython.core.alias import AliasError, Alias | ||
Thomas Kluyver
|
r9399 | from IPython.core.error import UsageError | ||
MinRK
|
r7297 | from IPython.core.magic import ( | ||
MinRK
|
r7401 | Magics, compress_dhist, magics_class, line_magic, cell_magic, line_cell_magic | ||
MinRK
|
r7297 | ) | ||
Fernando Perez
|
r6965 | from IPython.testing.skipdoctest import skip_doctest | ||
Jörgen Stenarson
|
r8313 | from IPython.utils.openpy import source_to_unicode | ||
Fernando Perez
|
r6965 | from IPython.utils.process import abbrev_cwd | ||
Thomas Kluyver
|
r13447 | from IPython.utils import py3compat | ||
Thomas Kluyver
|
r13363 | from IPython.utils.py3compat import unicode_type | ||
Fernando Perez
|
r6965 | from IPython.utils.terminal import set_term_title | ||
Robert Marchman
|
r9465 | |||
Fernando Perez
|
r6965 | #----------------------------------------------------------------------------- | ||
# Magic implementation classes | ||||
#----------------------------------------------------------------------------- | ||||
Fernando Perez
|
r6973 | @magics_class | ||
Fernando Perez
|
r6965 | class OSMagics(Magics): | ||
"""Magics to interact with the underlying OS (shell-type functionality). | ||||
""" | ||||
@skip_doctest | ||||
@line_magic | ||||
def alias(self, parameter_s=''): | ||||
"""Define an alias for a system command. | ||||
'%alias alias_name cmd' defines 'alias_name' as an alias for 'cmd' | ||||
Then, typing 'alias_name params' will execute the system command 'cmd | ||||
params' (from your underlying operating system). | ||||
Aliases have lower precedence than magic functions and Python normal | ||||
variables, so if 'foo' is both a Python variable and an alias, the | ||||
alias can not be executed until 'del foo' removes the Python variable. | ||||
You can use the %l specifier in an alias definition to represent the | ||||
whole line when the alias is called. For example:: | ||||
In [2]: alias bracket echo "Input in brackets: <%l>" | ||||
In [3]: bracket hello world | ||||
Input in brackets: <hello world> | ||||
You can also define aliases with parameters using %s specifiers (one | ||||
per parameter):: | ||||
In [1]: alias parts echo first %s second %s | ||||
In [2]: %parts A B | ||||
first A second B | ||||
In [3]: %parts A | ||||
Incorrect number of arguments: 2 expected. | ||||
parts is an alias to: 'echo first %s second %s' | ||||
Note that %l and %s are mutually exclusive. You can only use one or | ||||
the other in your aliases. | ||||
Aliases expand Python variables just like system calls using ! or !! | ||||
do: all expressions prefixed with '$' get expanded. For details of | ||||
the semantic rules, see PEP-215: | ||||
http://www.python.org/peps/pep-0215.html. This is the library used by | ||||
IPython for variable expansion. If you want to access a true shell | ||||
variable, an extra $ is necessary to prevent its expansion by | ||||
IPython:: | ||||
In [6]: alias show echo | ||||
In [7]: PATH='A Python string' | ||||
In [8]: show $PATH | ||||
A Python string | ||||
In [9]: show $$PATH | ||||
/usr/local/lf9560/bin:/usr/local/intel/compiler70/ia32/bin:... | ||||
Matthias Bussonnier
|
r21951 | You can use the alias facility to acess all of $PATH. See the %rehashx | ||
Matthias Bussonnier
|
r21953 | function, which automatically creates aliases for the contents of your | ||
Matthias Bussonnier
|
r21951 | $PATH. | ||
Fernando Perez
|
r6965 | |||
If called with no parameters, %alias prints the current alias table.""" | ||||
par = parameter_s.strip() | ||||
if not par: | ||||
aliases = sorted(self.shell.alias_manager.aliases) | ||||
# stored = self.shell.db.get('stored_aliases', {} ) | ||||
# for k, v in stored: | ||||
# atab.append(k, v[0]) | ||||
Thomas Kluyver
|
r13348 | print("Total number of aliases:", len(aliases)) | ||
Fernando Perez
|
r6965 | sys.stdout.flush() | ||
return aliases | ||||
# Now try to define a new one | ||||
try: | ||||
alias,cmd = par.split(None, 1) | ||||
Thomas Kluyver
|
r12595 | except TypeError: | ||
Thomas Kluyver
|
r13386 | print(oinspect.getdoc(self.alias)) | ||
Thomas Kluyver
|
r12595 | return | ||
try: | ||||
self.shell.alias_manager.define_alias(alias, cmd) | ||||
except AliasError as e: | ||||
print(e) | ||||
Fernando Perez
|
r6965 | # end magic_alias | ||
@line_magic | ||||
def unalias(self, parameter_s=''): | ||||
"""Remove an alias""" | ||||
aname = parameter_s.strip() | ||||
Thomas Kluyver
|
r12595 | try: | ||
self.shell.alias_manager.undefine_alias(aname) | ||||
except ValueError as e: | ||||
print(e) | ||||
return | ||||
Fernando Perez
|
r6965 | stored = self.shell.db.get('stored_aliases', {} ) | ||
if aname in stored: | ||||
Thomas Kluyver
|
r13348 | print("Removing %stored alias",aname) | ||
Fernando Perez
|
r6965 | del stored[aname] | ||
self.shell.db['stored_aliases'] = stored | ||||
@line_magic | ||||
def rehashx(self, parameter_s=''): | ||||
"""Update the alias table with all executable files in $PATH. | ||||
Matthias Bussonnier
|
r21951 | rehashx explicitly checks that every entry in $PATH is a file | ||
with execute access (os.X_OK). | ||||
Fernando Perez
|
r6965 | |||
Under Windows, it checks executability as a match against a | ||||
'|'-separated string of extensions, stored in the IPython config | ||||
variable win_exec_ext. This defaults to 'exe|com|bat'. | ||||
This function also resets the root module cache of module completer, | ||||
used on slow filesystems. | ||||
""" | ||||
from IPython.core.alias import InvalidAliasError | ||||
# for the benefit of module completer in ipy_completers.py | ||||
Antony Lee
|
r10125 | del self.shell.db['rootmodules_cache'] | ||
Fernando Perez
|
r6965 | |||
path = [os.path.abspath(os.path.expanduser(p)) for p in | ||||
os.environ.get('PATH','').split(os.pathsep)] | ||||
syscmdlist = [] | ||||
# Now define isexec in a cross platform manner. | ||||
if os.name == 'posix': | ||||
isexec = lambda fname:os.path.isfile(fname) and \ | ||||
os.access(fname,os.X_OK) | ||||
else: | ||||
try: | ||||
winext = os.environ['pathext'].replace(';','|').replace('.','') | ||||
except KeyError: | ||||
winext = 'exe|com|bat|py' | ||||
if 'py' not in winext: | ||||
winext += '|py' | ||||
execre = re.compile(r'(.*)\.(%s)$' % winext,re.IGNORECASE) | ||||
isexec = lambda fname:os.path.isfile(fname) and execre.match(fname) | ||||
Thomas Kluyver
|
r13447 | savedir = py3compat.getcwd() | ||
Fernando Perez
|
r6965 | |||
# Now walk the paths looking for executables to alias. | ||||
try: | ||||
# write the whole loop for posix/Windows so we don't have an if in | ||||
# the innermost part | ||||
if os.name == 'posix': | ||||
for pdir in path: | ||||
Jeroen Demeyer
|
r19132 | try: | ||
os.chdir(pdir) | ||||
dirlist = os.listdir(pdir) | ||||
except OSError: | ||||
continue | ||||
for ff in dirlist: | ||||
Fernando Perez
|
r6965 | if isexec(ff): | ||
try: | ||||
# Removes dots from the name since ipython | ||||
# will assume names with dots to be python. | ||||
Thomas Kluyver
|
r12601 | if not self.shell.alias_manager.is_alias(ff): | ||
Anders Hovmöller
|
r10161 | self.shell.alias_manager.define_alias( | ||
ff.replace('.',''), ff) | ||||
Fernando Perez
|
r6965 | except InvalidAliasError: | ||
pass | ||||
else: | ||||
syscmdlist.append(ff) | ||||
else: | ||||
Thomas Kluyver
|
r12627 | no_alias = Alias.blacklist | ||
Fernando Perez
|
r6965 | for pdir in path: | ||
Jeroen Demeyer
|
r19132 | try: | ||
os.chdir(pdir) | ||||
dirlist = os.listdir(pdir) | ||||
except OSError: | ||||
continue | ||||
for ff in dirlist: | ||||
Fernando Perez
|
r6965 | base, ext = os.path.splitext(ff) | ||
if isexec(ff) and base.lower() not in no_alias: | ||||
if ext.lower() == '.exe': | ||||
ff = base | ||||
try: | ||||
# Removes dots from the name since ipython | ||||
# will assume names with dots to be python. | ||||
self.shell.alias_manager.define_alias( | ||||
base.lower().replace('.',''), ff) | ||||
except InvalidAliasError: | ||||
pass | ||||
syscmdlist.append(ff) | ||||
self.shell.db['syscmdlist'] = syscmdlist | ||||
finally: | ||||
os.chdir(savedir) | ||||
@skip_doctest | ||||
@line_magic | ||||
def pwd(self, parameter_s=''): | ||||
"""Return the current working directory path. | ||||
Examples | ||||
-------- | ||||
:: | ||||
In [9]: pwd | ||||
Out[9]: '/home/tsuser/sprint/ipython' | ||||
""" | ||||
Thomas Kluyver
|
r13447 | return py3compat.getcwd() | ||
Fernando Perez
|
r6965 | |||
@skip_doctest | ||||
@line_magic | ||||
def cd(self, parameter_s=''): | ||||
"""Change the current working directory. | ||||
This command automatically maintains an internal list of directories | ||||
you visit during your IPython session, in the variable _dh. The | ||||
command %dhist shows this history nicely formatted. You can also | ||||
do 'cd -<tab>' to see directory history conveniently. | ||||
Usage: | ||||
cd 'dir': changes to directory 'dir'. | ||||
cd -: changes to the last visited directory. | ||||
cd -<n>: changes to the n-th directory in the directory history. | ||||
cd --foo: change to directory that matches 'foo' in history | ||||
cd -b <bookmark_name>: jump to a bookmark set by %bookmark | ||||
(note: cd <bookmark_name> is enough if there is no | ||||
directory <bookmark_name>, but a bookmark with the name exists.) | ||||
'cd -b <tab>' allows you to tab-complete bookmark names. | ||||
Options: | ||||
-q: quiet. Do not print the working directory after the cd command is | ||||
executed. By default IPython's cd command does print this directory, | ||||
since the default prompts do not display path information. | ||||
Note that !cd doesn't work for this purpose because the shell where | ||||
!command runs is immediately discarded after executing 'command'. | ||||
Examples | ||||
-------- | ||||
:: | ||||
In [10]: cd parent/child | ||||
/home/tsuser/parent/child | ||||
""" | ||||
Thomas Kluyver
|
r13447 | oldcwd = py3compat.getcwd() | ||
Fernando Perez
|
r6965 | numcd = re.match(r'(-)(\d+)$',parameter_s) | ||
# jump in directory history by number | ||||
if numcd: | ||||
nn = int(numcd.group(2)) | ||||
try: | ||||
ps = self.shell.user_ns['_dh'][nn] | ||||
except IndexError: | ||||
Thomas Kluyver
|
r13348 | print('The requested directory does not exist in history.') | ||
Fernando Perez
|
r6965 | return | ||
else: | ||||
opts = {} | ||||
elif parameter_s.startswith('--'): | ||||
ps = None | ||||
fallback = None | ||||
pat = parameter_s[2:] | ||||
dh = self.shell.user_ns['_dh'] | ||||
# first search only by basename (last component) | ||||
for ent in reversed(dh): | ||||
if pat in os.path.basename(ent) and os.path.isdir(ent): | ||||
ps = ent | ||||
break | ||||
if fallback is None and pat in ent and os.path.isdir(ent): | ||||
fallback = ent | ||||
# if we have no last part match, pick the first full path match | ||||
if ps is None: | ||||
ps = fallback | ||||
if ps is None: | ||||
Thomas Kluyver
|
r13348 | print("No matching entry in directory history") | ||
Fernando Perez
|
r6965 | return | ||
else: | ||||
opts = {} | ||||
else: | ||||
Antony Lee
|
r22418 | opts, ps = self.parse_options(parameter_s, 'qb', mode='string') | ||
Fernando Perez
|
r6965 | # jump to previous | ||
if ps == '-': | ||||
try: | ||||
ps = self.shell.user_ns['_dh'][-2] | ||||
except IndexError: | ||||
raise UsageError('%cd -: No previous directory to change to.') | ||||
# jump to bookmark if needed | ||||
else: | ||||
Fernando Perez
|
r6977 | if not os.path.isdir(ps) or 'b' in opts: | ||
Fernando Perez
|
r6965 | bkms = self.shell.db.get('bookmarks', {}) | ||
Fernando Perez
|
r6977 | if ps in bkms: | ||
Fernando Perez
|
r6965 | target = bkms[ps] | ||
Thomas Kluyver
|
r13348 | print('(bookmark:%s) -> %s' % (ps, target)) | ||
Fernando Perez
|
r6965 | ps = target | ||
else: | ||||
Fernando Perez
|
r6977 | if 'b' in opts: | ||
Fernando Perez
|
r6965 | raise UsageError("Bookmark '%s' not found. " | ||
"Use '%%bookmark -l' to see your bookmarks." % ps) | ||||
# at this point ps should point to the target dir | ||||
if ps: | ||||
try: | ||||
os.chdir(os.path.expanduser(ps)) | ||||
if hasattr(self.shell, 'term_title') and self.shell.term_title: | ||||
set_term_title('IPython: ' + abbrev_cwd()) | ||||
except OSError: | ||||
Thomas Kluyver
|
r13348 | print(sys.exc_info()[1]) | ||
Fernando Perez
|
r6965 | else: | ||
Thomas Kluyver
|
r13447 | cwd = py3compat.getcwd() | ||
Fernando Perez
|
r6965 | dhist = self.shell.user_ns['_dh'] | ||
if oldcwd != cwd: | ||||
dhist.append(cwd) | ||||
self.shell.db['dhist'] = compress_dhist(dhist)[-100:] | ||||
else: | ||||
os.chdir(self.shell.home_dir) | ||||
if hasattr(self.shell, 'term_title') and self.shell.term_title: | ||||
set_term_title('IPython: ' + '~') | ||||
Thomas Kluyver
|
r13447 | cwd = py3compat.getcwd() | ||
Fernando Perez
|
r6965 | dhist = self.shell.user_ns['_dh'] | ||
if oldcwd != cwd: | ||||
dhist.append(cwd) | ||||
self.shell.db['dhist'] = compress_dhist(dhist)[-100:] | ||||
if not 'q' in opts and self.shell.user_ns['_dh']: | ||||
Thomas Kluyver
|
r13348 | print(self.shell.user_ns['_dh'][-1]) | ||
Fernando Perez
|
r6965 | |||
@line_magic | ||||
def env(self, parameter_s=''): | ||||
mvr
|
r19335 | """Get, set, or list environment variables. | ||
Usage:\\ | ||||
%env: lists all environment variables/values | ||||
%env var: get value for var | ||||
%env var val: set value for var | ||||
%env var=val: set value for var | ||||
%env var=$val: set value for var, using python expansion if possible | ||||
""" | ||||
mvr
|
r18924 | if parameter_s.strip(): | ||
split = '=' if '=' in parameter_s else ' ' | ||||
bits = parameter_s.split(split) | ||||
if len(bits) == 1: | ||||
key = parameter_s.strip() | ||||
if key in os.environ: | ||||
return os.environ[key] | ||||
else: | ||||
err = "Environment does not have key: {0}".format(key) | ||||
raise UsageError(err) | ||||
if len(bits) > 1: | ||||
return self.set_env(parameter_s) | ||||
Fernando Perez
|
r6965 | return dict(os.environ) | ||
@line_magic | ||||
mvr
|
r18924 | def set_env(self, parameter_s): | ||
"""Set environment variables. Assumptions are that either "val" is a | ||||
name in the user namespace, or val is something that evaluates to a | ||||
string. | ||||
Usage:\\ | ||||
mvr
|
r19335 | %set_env var val: set value for var | ||
%set_env var=val: set value for var | ||||
%set_env var=$val: set value for var, using python expansion if possible | ||||
mvr
|
r18924 | """ | ||
split = '=' if '=' in parameter_s else ' ' | ||||
bits = parameter_s.split(split, 1) | ||||
if not parameter_s.strip() or len(bits)<2: | ||||
raise UsageError("usage is 'set_env var=val'") | ||||
var = bits[0].strip() | ||||
val = bits[1].strip() | ||||
if re.match(r'.*\s.*', var): | ||||
# an environment variable with whitespace is almost certainly | ||||
# not what the user intended. what's more likely is the wrong | ||||
# split was chosen, ie for "set_env cmd_args A=B", we chose | ||||
# '=' for the split and should have chosen ' '. to get around | ||||
# this, users should just assign directly to os.environ or use | ||||
# standard magic {var} expansion. | ||||
err = "refusing to set env var with whitespace: '{0}'" | ||||
err = err.format(val) | ||||
raise UsageError(err) | ||||
Thomas Kluyver
|
r18951 | os.environ[py3compat.cast_bytes_py2(var)] = py3compat.cast_bytes_py2(val) | ||
mvr
|
r18924 | print('env: {0}={1}'.format(var,val)) | ||
@line_magic | ||||
Fernando Perez
|
r6965 | def pushd(self, parameter_s=''): | ||
"""Place the current dir on stack and change directory. | ||||
Usage:\\ | ||||
%pushd ['dirname'] | ||||
""" | ||||
dir_s = self.shell.dir_stack | ||||
Antony Lee
|
r22418 | tgt = os.path.expanduser(parameter_s) | ||
Thomas Kluyver
|
r13447 | cwd = py3compat.getcwd().replace(self.shell.home_dir,'~') | ||
Fernando Perez
|
r6965 | if tgt: | ||
self.cd(parameter_s) | ||||
dir_s.insert(0,cwd) | ||||
return self.shell.magic('dirs') | ||||
@line_magic | ||||
def popd(self, parameter_s=''): | ||||
"""Change to directory popped off the top of the stack. | ||||
""" | ||||
if not self.shell.dir_stack: | ||||
raise UsageError("%popd on empty stack") | ||||
top = self.shell.dir_stack.pop(0) | ||||
self.cd(top) | ||||
Thomas Kluyver
|
r13348 | print("popd ->",top) | ||
Fernando Perez
|
r6965 | |||
@line_magic | ||||
def dirs(self, parameter_s=''): | ||||
"""Return the current directory stack.""" | ||||
return self.shell.dir_stack | ||||
@line_magic | ||||
def dhist(self, parameter_s=''): | ||||
"""Print your history of visited directories. | ||||
%dhist -> print full history\\ | ||||
%dhist n -> print last n entries only\\ | ||||
Thomas Kluyver
|
r9474 | %dhist n1 n2 -> print entries between n1 and n2 (n2 not included)\\ | ||
Fernando Perez
|
r6965 | |||
This history is automatically maintained by the %cd command, and | ||||
always available as the global list variable _dh. You can use %cd -<n> | ||||
to go to directory number <n>. | ||||
Note that most of time, you should view directory history by entering | ||||
cd -<TAB>. | ||||
""" | ||||
dh = self.shell.user_ns['_dh'] | ||||
if parameter_s: | ||||
try: | ||||
args = map(int,parameter_s.split()) | ||||
except: | ||||
self.arg_err(self.dhist) | ||||
return | ||||
if len(args) == 1: | ||||
ini,fin = max(len(dh)-(args[0]),0),len(dh) | ||||
elif len(args) == 2: | ||||
ini,fin = args | ||||
Thomas Kluyver
|
r9474 | fin = min(fin, len(dh)) | ||
Fernando Perez
|
r6965 | else: | ||
self.arg_err(self.dhist) | ||||
return | ||||
else: | ||||
ini,fin = 0,len(dh) | ||||
Thomas Kluyver
|
r13348 | print('Directory history (kept in _dh)') | ||
Thomas Kluyver
|
r9474 | for i in range(ini, fin): | ||
Thomas Kluyver
|
r13348 | print("%d: %s" % (i, dh[i])) | ||
Fernando Perez
|
r6965 | |||
@skip_doctest | ||||
@line_magic | ||||
def sc(self, parameter_s=''): | ||||
Aaron Culich
|
r7399 | """Shell capture - run shell command and capture output (DEPRECATED use !). | ||
Fernando Perez
|
r6965 | |||
DEPRECATED. Suboptimal, retained for backwards compatibility. | ||||
You should use the form 'var = !command' instead. Example: | ||||
"%sc -l myfiles = ls ~" should now be written as | ||||
"myfiles = !ls ~" | ||||
myfiles.s, myfiles.l and myfiles.n still apply as documented | ||||
below. | ||||
-- | ||||
%sc [options] varname=command | ||||
IPython will run the given command using commands.getoutput(), and | ||||
will then update the user's interactive namespace with a variable | ||||
called varname, containing the value of the call. Your command can | ||||
contain shell wildcards, pipes, etc. | ||||
The '=' sign in the syntax is mandatory, and the variable name you | ||||
supply must follow Python's standard conventions for valid names. | ||||
(A special format without variable name exists for internal use) | ||||
Options: | ||||
-l: list output. Split the output on newlines into a list before | ||||
assigning it to the given variable. By default the output is stored | ||||
as a single string. | ||||
-v: verbose. Print the contents of the variable. | ||||
In most cases you should not need to split as a list, because the | ||||
returned value is a special type of string which can automatically | ||||
provide its contents either as a list (split on newlines) or as a | ||||
space-separated string. These are convenient, respectively, either | ||||
for sequential processing or to be passed to a shell command. | ||||
For example:: | ||||
# Capture into variable a | ||||
In [1]: sc a=ls *py | ||||
# a is a string with embedded newlines | ||||
In [2]: a | ||||
Out[2]: 'setup.py\\nwin32_manual_post_install.py' | ||||
# which can be seen as a list: | ||||
In [3]: a.l | ||||
Out[3]: ['setup.py', 'win32_manual_post_install.py'] | ||||
# or as a whitespace-separated string: | ||||
In [4]: a.s | ||||
Out[4]: 'setup.py win32_manual_post_install.py' | ||||
# a.s is useful to pass as a single command line: | ||||
In [5]: !wc -l $a.s | ||||
146 setup.py | ||||
130 win32_manual_post_install.py | ||||
276 total | ||||
# while the list form is useful to loop over: | ||||
In [6]: for f in a.l: | ||||
...: !wc -l $f | ||||
...: | ||||
146 setup.py | ||||
130 win32_manual_post_install.py | ||||
Similarly, the lists returned by the -l option are also special, in | ||||
the sense that you can equally invoke the .s attribute on them to | ||||
automatically get a whitespace-separated string from their contents:: | ||||
In [7]: sc -l b=ls *py | ||||
In [8]: b | ||||
Out[8]: ['setup.py', 'win32_manual_post_install.py'] | ||||
In [9]: b.s | ||||
Out[9]: 'setup.py win32_manual_post_install.py' | ||||
In summary, both the lists and strings used for output capture have | ||||
the following special attributes:: | ||||
.l (or .list) : value as list. | ||||
.n (or .nlstr): value as newline-separated string. | ||||
.s (or .spstr): value as space-separated string. | ||||
""" | ||||
Fernando Perez
|
r6986 | opts,args = self.parse_options(parameter_s, 'lv') | ||
Fernando Perez
|
r6965 | # Try to get a variable name and command to run | ||
try: | ||||
# the variable name must be obtained from the parse_options | ||||
# output, which uses shlex.split to strip options out. | ||||
Fernando Perez
|
r6986 | var,_ = args.split('=', 1) | ||
Fernando Perez
|
r6965 | var = var.strip() | ||
# But the command has to be extracted from the original input | ||||
# parameter_s, not on what parse_options returns, to avoid the | ||||
# quote stripping which shlex.split performs on it. | ||||
Fernando Perez
|
r6986 | _,cmd = parameter_s.split('=', 1) | ||
Fernando Perez
|
r6965 | except ValueError: | ||
var,cmd = '','' | ||||
# If all looks ok, proceed | ||||
split = 'l' in opts | ||||
out = self.shell.getoutput(cmd, split=split) | ||||
Fernando Perez
|
r6977 | if 'v' in opts: | ||
Thomas Kluyver
|
r13348 | print('%s ==\n%s' % (var, pformat(out))) | ||
Fernando Perez
|
r6965 | if var: | ||
self.shell.user_ns.update({var:out}) | ||||
else: | ||||
return out | ||||
MinRK
|
r7401 | @line_cell_magic | ||
def sx(self, line='', cell=None): | ||||
Aaron Culich
|
r7399 | """Shell execute - run shell command and capture output (!! is short-hand). | ||
Fernando Perez
|
r6965 | |||
%sx command | ||||
IPython will run the given command using commands.getoutput(), and | ||||
return the result formatted as a list (split on '\\n'). Since the | ||||
output is _returned_, it will be stored in ipython's regular output | ||||
cache Out[N] and in the '_N' automatic variables. | ||||
Notes: | ||||
1) If an input line begins with '!!', then %sx is automatically | ||||
invoked. That is, while:: | ||||
!ls | ||||
causes ipython to simply issue system('ls'), typing:: | ||||
!!ls | ||||
is a shorthand equivalent to:: | ||||
%sx ls | ||||
2) %sx differs from %sc in that %sx automatically splits into a list, | ||||
like '%sc -l'. The reason for this is to make it as easy as possible | ||||
to process line-oriented shell output via further python commands. | ||||
%sc is meant to provide much finer control, but requires more | ||||
typing. | ||||
3) Just like %sc -l, this is a list with special attributes: | ||||
:: | ||||
.l (or .list) : value as list. | ||||
.n (or .nlstr): value as newline-separated string. | ||||
.s (or .spstr): value as whitespace-separated string. | ||||
This is very useful when trying to use such lists as arguments to | ||||
system commands.""" | ||||
MinRK
|
r7401 | |||
if cell is None: | ||||
# line magic | ||||
return self.shell.getoutput(line) | ||||
else: | ||||
opts,args = self.parse_options(line, '', 'out=') | ||||
output = self.shell.getoutput(cell) | ||||
out_name = opts.get('out', opts.get('o')) | ||||
if out_name: | ||||
self.shell.user_ns[out_name] = output | ||||
else: | ||||
return output | ||||
Fernando Perez
|
r6965 | |||
MinRK
|
r7401 | system = line_cell_magic('system')(sx) | ||
bang = cell_magic('!')(sx) | ||||
Fernando Perez
|
r6965 | |||
@line_magic | ||||
def bookmark(self, parameter_s=''): | ||||
"""Manage IPython's bookmark system. | ||||
%bookmark <name> - set bookmark to current dir | ||||
%bookmark <name> <dir> - set bookmark to <dir> | ||||
%bookmark -l - list all bookmarks | ||||
%bookmark -d <name> - remove bookmark | ||||
%bookmark -r - remove all bookmarks | ||||
You can later on access a bookmarked folder with:: | ||||
%cd -b <name> | ||||
or simply '%cd <name>' if there is no directory called <name> AND | ||||
there is such a bookmark defined. | ||||
Your bookmarks persist through IPython sessions, but they are | ||||
associated with each profile.""" | ||||
opts,args = self.parse_options(parameter_s,'drl',mode='list') | ||||
if len(args) > 2: | ||||
raise UsageError("%bookmark: too many arguments") | ||||
bkms = self.shell.db.get('bookmarks',{}) | ||||
Fernando Perez
|
r6977 | if 'd' in opts: | ||
Fernando Perez
|
r6965 | try: | ||
todel = args[0] | ||||
except IndexError: | ||||
raise UsageError( | ||||
"%bookmark -d: must provide a bookmark to delete") | ||||
else: | ||||
try: | ||||
del bkms[todel] | ||||
except KeyError: | ||||
raise UsageError( | ||||
"%%bookmark -d: Can't delete bookmark '%s'" % todel) | ||||
Fernando Perez
|
r6977 | elif 'r' in opts: | ||
Fernando Perez
|
r6965 | bkms = {} | ||
Fernando Perez
|
r6977 | elif 'l' in opts: | ||
Thomas Kluyver
|
r16369 | bks = sorted(bkms) | ||
Fernando Perez
|
r6965 | if bks: | ||
Fernando Perez
|
r6977 | size = max(map(len, bks)) | ||
Fernando Perez
|
r6965 | else: | ||
size = 0 | ||||
fmt = '%-'+str(size)+'s -> %s' | ||||
Thomas Kluyver
|
r13348 | print('Current bookmarks:') | ||
Fernando Perez
|
r6965 | for bk in bks: | ||
Thomas Kluyver
|
r13348 | print(fmt % (bk, bkms[bk])) | ||
Fernando Perez
|
r6965 | else: | ||
if not args: | ||||
raise UsageError("%bookmark: You must specify the bookmark name") | ||||
elif len(args)==1: | ||||
Thomas Kluyver
|
r13447 | bkms[args[0]] = py3compat.getcwd() | ||
Fernando Perez
|
r6965 | elif len(args)==2: | ||
bkms[args[0]] = args[1] | ||||
self.shell.db['bookmarks'] = bkms | ||||
@line_magic | ||||
def pycat(self, parameter_s=''): | ||||
"""Show a syntax-highlighted file through a pager. | ||||
This magic is similar to the cat utility, but it will assume the file | ||||
Matthias BUSSONNIER
|
r7061 | to be Python source and will show it with syntax highlighting. | ||
Fernando Perez
|
r6965 | |||
Matthias BUSSONNIER
|
r7061 | This magic command can either take a local filename, an url, | ||
an history range (see %history) or a macro as argument :: | ||||
%pycat myscript.py | ||||
%pycat 7-27 | ||||
%pycat myMacro | ||||
%pycat http://www.example.com/myscript.py | ||||
""" | ||||
Bradley M. Froehle
|
r8278 | if not parameter_s: | ||
raise UsageError('Missing filename, URL, input history range, ' | ||||
'or macro.') | ||||
Matthias BUSSONNIER
|
r7061 | |||
try : | ||||
Jörgen Stenarson
|
r8334 | cont = self.shell.find_user_code(parameter_s, skip_encoding_cookie=False) | ||
Matthias BUSSONNIER
|
r7782 | except (ValueError, IOError): | ||
Thomas Kluyver
|
r13348 | print("Error: no such file, variable, URL, history range or macro") | ||
Fernando Perez
|
r6965 | return | ||
Jörgen Stenarson
|
r8300 | page.page(self.shell.pycolorize(source_to_unicode(cont))) | ||
MinRK
|
r7297 | |||
@magic_arguments.magic_arguments() | ||||
@magic_arguments.argument( | ||||
MinRK
|
r10241 | '-a', '--append', action='store_true', default=False, | ||
help='Append contents of the cell to an existing file. ' | ||||
'The file will be created if it does not exist.' | ||||
MinRK
|
r7297 | ) | ||
@magic_arguments.argument( | ||||
Thomas Kluyver
|
r13363 | 'filename', type=unicode_type, | ||
MinRK
|
r7297 | help='file to write' | ||
) | ||||
@cell_magic | ||||
MinRK
|
r10634 | def writefile(self, line, cell): | ||
MinRK
|
r7308 | """Write the contents of the cell to a file. | ||
MinRK
|
r7297 | |||
MinRK
|
r10241 | The file will be overwritten unless the -a (--append) flag is specified. | ||
MinRK
|
r7297 | """ | ||
MinRK
|
r10634 | args = magic_arguments.parse_argstring(self.writefile, line) | ||
Antony Lee
|
r22418 | filename = os.path.expanduser(args.filename) | ||
MinRK
|
r7377 | if os.path.exists(filename): | ||
MinRK
|
r10241 | if args.append: | ||
Thomas Kluyver
|
r13348 | print("Appending to %s" % filename) | ||
MinRK
|
r7308 | else: | ||
Thomas Kluyver
|
r13348 | print("Overwriting %s" % filename) | ||
MinRK
|
r7297 | else: | ||
Thomas Kluyver
|
r13348 | print("Writing %s" % filename) | ||
MinRK
|
r7297 | |||
MinRK
|
r10241 | mode = 'a' if args.append else 'w' | ||
MinRK
|
r7377 | with io.open(filename, mode, encoding='utf-8') as f: | ||
MinRK
|
r7297 | f.write(cell) | ||