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