osm.py
855 lines
| 30.0 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
|
r23487 | # Copyright (c) IPython Development Team. | ||
# Distributed under the terms of the Modified BSD License. | ||||
MinRK
|
r7305 | import io | ||
Fernando Perez
|
r6965 | import os | ||
Aman
|
r27754 | import pathlib | ||
Fernando Perez
|
r6965 | import re | ||
import sys | ||||
from pprint import pformat | ||||
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 | ||
from IPython.utils.terminal import set_term_title | ||||
Reuben Morais
|
r24831 | from traitlets import Bool | ||
dalthviz
|
r25807 | from warnings import warn | ||
Robert Marchman
|
r9465 | |||
Thomas Kluyver
|
r23487 | |||
Fernando Perez
|
r6973 | @magics_class | ||
Fernando Perez
|
r6965 | class OSMagics(Magics): | ||
"""Magics to interact with the underlying OS (shell-type functionality). | ||||
""" | ||||
Reuben Morais
|
r24831 | cd_force_quiet = Bool(False, | ||
help="Force %cd magic to be quiet even if -q is not passed." | ||||
).tag(config=True) | ||||
Kory Donati
|
r24645 | def __init__(self, shell=None, **kwargs): | ||
# Now define isexec in a cross platform manner. | ||||
kd2718
|
r24650 | self.is_posix = False | ||
Kory Donati
|
r24645 | self.execre = None | ||
if os.name == 'posix': | ||||
self.is_posix = True | ||||
else: | ||||
try: | ||||
winext = os.environ['pathext'].replace(';','|').replace('.','') | ||||
except KeyError: | ||||
winext = 'exe|com|bat|py' | ||||
dalthviz
|
r25807 | try: | ||
self.execre = re.compile(r'(.*)\.(%s)$' % winext,re.IGNORECASE) | ||||
except re.error: | ||||
warn("Seems like your pathext environmental " | ||||
"variable is malformed. Please check it to " | ||||
"enable a proper handle of file extensions " | ||||
"managed for your system") | ||||
winext = 'exe|com|bat|py' | ||||
self.execre = re.compile(r'(.*)\.(%s)$' % winext,re.IGNORECASE) | ||||
Kory Donati
|
r24645 | |||
# call up the chain | ||||
Kory Donati
|
r24669 | super().__init__(shell=shell, **kwargs) | ||
Kory Donati
|
r24645 | |||
kd2718
|
r24650 | def _isexec_POSIX(self, file): | ||
Kory Donati
|
r24645 | """ | ||
Matthias Bussonnier
|
r27294 | Test for executable on a POSIX system | ||
Kory Donati
|
r24645 | """ | ||
Matthias Bussonnier
|
r24787 | if os.access(file.path, os.X_OK): | ||
# will fail on maxOS if access is not X_OK | ||||
return file.is_file() | ||||
return False | ||||
Kory Donati
|
r24645 | |||
kd2718
|
r24650 | def _isexec_WIN(self, file): | ||
Kory Donati
|
r24645 | """ | ||
Matthias Bussonnier
|
r27294 | Test for executable file on non POSIX system | ||
Kory Donati
|
r24645 | """ | ||
kd2718
|
r24650 | return file.is_file() and self.execre.match(file.name) is not None | ||
Kory Donati
|
r24645 | |||
kd2718
|
r24650 | def isexec(self, file): | ||
Kory Donati
|
r24645 | """ | ||
Matthias Bussonnier
|
r27294 | Test for executable file on non POSIX system | ||
Kory Donati
|
r24645 | """ | ||
if self.is_posix: | ||||
kd2718
|
r24650 | return self._isexec_POSIX(file) | ||
Kory Donati
|
r24645 | else: | ||
kd2718
|
r24650 | return self._isexec_WIN(file) | ||
Kory Donati
|
r24645 | |||
Fernando Perez
|
r6965 | @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: | ||||
Matthias Bussonnier
|
r27588 | https://peps.python.org/pep-0215/. This is the library used by | ||
Fernando Perez
|
r6965 | 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:... | ||||
Sang Min Park
|
r23607 | You can use the alias facility to access 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 | |||
Sang Min Park
|
r23607 | If called with no parameters, %alias prints the current alias table | ||
for your system. For posix systems, the default aliases are 'cat', | ||||
'cp', 'mv', 'rm', 'rmdir', and 'mkdir', and other platform-specific | ||||
aliases are added. For windows-based systems, the default aliases are | ||||
'copy', 'ddir', 'echo', 'ls', 'ldir', 'mkdir', 'ren', and 'rmdir'. | ||||
You can see the definition of alias by adding a question mark in the | ||||
end:: | ||||
In [1]: cat? | ||||
Repr: <alias cat for 'cat'>""" | ||||
Fernando Perez
|
r6965 | |||
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 = [] | ||||
Srinivas Reddy Thatiparthy
|
r23045 | savedir = os.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 | ||||
kd2718
|
r24650 | if self.is_posix: | ||
Fernando Perez
|
r6965 | for pdir in path: | ||
Jeroen Demeyer
|
r19132 | try: | ||
os.chdir(pdir) | ||||
except OSError: | ||||
continue | ||||
kd2718
|
r24652 | |||
kd2718
|
r24685 | # for python 3.6+ rewrite to: with os.scandir(pdir) as dirlist: | ||
kd2718
|
r24651 | dirlist = os.scandir(path=pdir) | ||
for ff in dirlist: | ||||
if self.isexec(ff): | ||||
fname = ff.name | ||||
try: | ||||
# Removes dots from the name since ipython | ||||
# will assume names with dots to be python. | ||||
if not self.shell.alias_manager.is_alias(fname): | ||||
self.shell.alias_manager.define_alias( | ||||
fname.replace('.',''), fname) | ||||
except InvalidAliasError: | ||||
pass | ||||
else: | ||||
syscmdlist.append(fname) | ||||
Fernando Perez
|
r6965 | else: | ||
Thomas Kluyver
|
r12627 | no_alias = Alias.blacklist | ||
Fernando Perez
|
r6965 | for pdir in path: | ||
Jeroen Demeyer
|
r19132 | try: | ||
os.chdir(pdir) | ||||
except OSError: | ||||
continue | ||||
kd2718
|
r24652 | |||
kd2718
|
r24685 | # for python 3.6+ rewrite to: with os.scandir(pdir) as dirlist: | ||
kd2718
|
r24652 | dirlist = os.scandir(pdir) | ||
for ff in dirlist: | ||||
fname = ff.name | ||||
base, ext = os.path.splitext(fname) | ||||
if self.isexec(ff) and base.lower() not in no_alias: | ||||
if ext.lower() == '.exe': | ||||
fname = 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('.',''), fname) | ||||
except InvalidAliasError: | ||||
pass | ||||
syscmdlist.append(fname) | ||||
kd2718
|
r24685 | |||
Fernando Perez
|
r6965 | 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
|
r23487 | try: | ||
return os.getcwd() | ||||
Ram Rachum
|
r25827 | except FileNotFoundError as e: | ||
raise UsageError("CWD no longer exists - please use %cd to change directory.") from e | ||||
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 | ||||
Blazej Michalik
|
r26414 | you visit during your IPython session, in the variable ``_dh``. The | ||
command :magic:`%dhist` shows this history nicely formatted. You can | ||||
also do ``cd -<tab>`` to see directory history conveniently. | ||||
Fernando Perez
|
r6965 | Usage: | ||
Blazej Michalik
|
r26414 | - ``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 | ||||
- Hitting a tab key after ``cd -b`` allows you to tab-complete | ||||
bookmark names. | ||||
Fernando Perez
|
r6965 | |||
Blazej Michalik
|
r26414 | .. note:: | ||
``cd <bookmark_name>`` is enough if there is no directory | ||||
``<bookmark_name>``, but a bookmark with the name exists. | ||||
Fernando Perez
|
r6965 | |||
Options: | ||||
Blazej Michalik
|
r26414 | -q Be 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:: | ||||
Note that ``!cd`` doesn't work for this purpose because the shell | ||||
where ``!command`` runs is immediately discarded after executing | ||||
'command'. | ||||
Fernando Perez
|
r6965 | |||
Examples | ||||
-------- | ||||
:: | ||||
In [10]: cd parent/child | ||||
/home/tsuser/parent/child | ||||
""" | ||||
Thomas Kluyver
|
r23487 | try: | ||
oldcwd = os.getcwd() | ||||
except FileNotFoundError: | ||||
# Happens if the CWD has been deleted. | ||||
oldcwd = None | ||||
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] | ||||
Ram Rachum
|
r25827 | except IndexError as e: | ||
raise UsageError('%cd -: No previous directory to change to.') from e | ||||
Fernando Perez
|
r6965 | # 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: | ||||
Tory Haavik
|
r23623 | set_term_title(self.shell.term_title_format.format(cwd=abbrev_cwd())) | ||
Fernando Perez
|
r6965 | except OSError: | ||
Thomas Kluyver
|
r13348 | print(sys.exc_info()[1]) | ||
Fernando Perez
|
r6965 | else: | ||
Aman
|
r27754 | cwd = pathlib.Path.cwd() | ||
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: | ||||
Tory Haavik
|
r23623 | set_term_title(self.shell.term_title_format.format(cwd="~")) | ||
Aman
|
r27754 | cwd = pathlib.Path.cwd() | ||
Fernando Perez
|
r6965 | dhist = self.shell.user_ns['_dh'] | ||
if oldcwd != cwd: | ||||
dhist.append(cwd) | ||||
self.shell.db['dhist'] = compress_dhist(dhist)[-100:] | ||||
Reuben Morais
|
r24831 | if not 'q' in opts and not self.cd_force_quiet 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:\\ | ||||
Boyuan Liu
|
r26589 | :``%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
|
r19335 | """ | ||
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) | ||||
Min RK
|
r25191 | env = dict(os.environ) | ||
# hide likely secrets when printing the whole environment | ||||
for key in list(env): | ||||
if any(s in key.lower() for s in ('key', 'token', 'secret')): | ||||
env[key] = '<hidden>' | ||||
return env | ||||
Fernando Perez
|
r6965 | |||
@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:\\ | ||||
Deepyaman Datta
|
r28067 | :``%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) | ||||
Srinivas Reddy Thatiparthy
|
r23666 | os.environ[var] = 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) | ||
Srinivas Reddy Thatiparthy
|
r23045 | cwd = os.getcwd().replace(self.shell.home_dir,'~') | ||
Fernando Perez
|
r6965 | if tgt: | ||
self.cd(parameter_s) | ||||
dir_s.insert(0,cwd) | ||||
Bartosz Telenczuk
|
r25764 | return self.shell.run_line_magic('dirs', '') | ||
Fernando Perez
|
r6965 | |||
@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: | ||||
Nikita Kniazev
|
r27119 | ...: !wc -l $f | ||
...: | ||||
Fernando Perez
|
r6965 | 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] | ||||
Ram Rachum
|
r25827 | except IndexError as e: | ||
Fernando Perez
|
r6965 | raise UsageError( | ||
Ram Rachum
|
r25827 | "%bookmark -d: must provide a bookmark to delete") from e | ||
Fernando Perez
|
r6965 | else: | ||
try: | ||||
del bkms[todel] | ||||
Ram Rachum
|
r25827 | except KeyError as e: | ||
Fernando Perez
|
r6965 | raise UsageError( | ||
Ram Rachum
|
r25827 | "%%bookmark -d: Can't delete bookmark '%s'" % todel) from e | ||
Fernando Perez
|
r6965 | |||
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: | ||||
Srinivas Reddy Thatiparthy
|
r23045 | bkms[args[0]] = os.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, | ||
Blazej Michalik
|
r26639 | an history range (see %history) or a macro as argument. | ||
Matthias BUSSONNIER
|
r7061 | |||
Blazej Michalik
|
r26633 | If no parameter is given, prints out history of current session up to | ||
Blazej Michalik
|
r26639 | this point. :: | ||
Blazej Michalik
|
r26633 | |||
Matthias BUSSONNIER
|
r7061 | %pycat myscript.py | ||
%pycat 7-27 | ||||
%pycat myMacro | ||||
%pycat http://www.example.com/myscript.py | ||||
""" | ||||
Blazej Michalik
|
r26633 | try: | ||
Blazej Michalik
|
r26641 | 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( | ||||
Srinivas Reddy Thatiparthy
|
r23044 | 'filename', type=str, | ||
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. | ||
Matthias Bussonnier
|
r27294 | |||
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) | ||
Partha P. Mukherjee
|
r24927 | if re.match(r'^(\'.*\')|(".*")$', args.filename): | ||
hongshaoyang
|
r24663 | filename = os.path.expanduser(args.filename[1:-1]) | ||
else: | ||||
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) | ||