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