##// END OF EJS Templates
Add --term-title-format flag to customize terminal title format
Tory Haavik -
Show More
@@ -0,0 +1,2 b''
1 An additional flag `--term-title-format` is introduced to allow the user to control the format of the terminal
2 title. It is specified as a python format string, and currently the only variable it will format is `{cwd}`.
@@ -1,793 +1,793 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 import py3compat
26 from IPython.utils import py3compat
27 from IPython.utils.terminal import set_term_title
27 from IPython.utils.terminal import set_term_title
28
28
29
29
30 @magics_class
30 @magics_class
31 class OSMagics(Magics):
31 class OSMagics(Magics):
32 """Magics to interact with the underlying OS (shell-type functionality).
32 """Magics to interact with the underlying OS (shell-type functionality).
33 """
33 """
34
34
35 @skip_doctest
35 @skip_doctest
36 @line_magic
36 @line_magic
37 def alias(self, parameter_s=''):
37 def alias(self, parameter_s=''):
38 """Define an alias for a system command.
38 """Define an alias for a system command.
39
39
40 '%alias alias_name cmd' defines 'alias_name' as an alias for 'cmd'
40 '%alias alias_name cmd' defines 'alias_name' as an alias for 'cmd'
41
41
42 Then, typing 'alias_name params' will execute the system command 'cmd
42 Then, typing 'alias_name params' will execute the system command 'cmd
43 params' (from your underlying operating system).
43 params' (from your underlying operating system).
44
44
45 Aliases have lower precedence than magic functions and Python normal
45 Aliases have lower precedence than magic functions and Python normal
46 variables, so if 'foo' is both a Python variable and an alias, the
46 variables, so if 'foo' is both a Python variable and an alias, the
47 alias can not be executed until 'del foo' removes the Python variable.
47 alias can not be executed until 'del foo' removes the Python variable.
48
48
49 You can use the %l specifier in an alias definition to represent the
49 You can use the %l specifier in an alias definition to represent the
50 whole line when the alias is called. For example::
50 whole line when the alias is called. For example::
51
51
52 In [2]: alias bracket echo "Input in brackets: <%l>"
52 In [2]: alias bracket echo "Input in brackets: <%l>"
53 In [3]: bracket hello world
53 In [3]: bracket hello world
54 Input in brackets: <hello world>
54 Input in brackets: <hello world>
55
55
56 You can also define aliases with parameters using %s specifiers (one
56 You can also define aliases with parameters using %s specifiers (one
57 per parameter)::
57 per parameter)::
58
58
59 In [1]: alias parts echo first %s second %s
59 In [1]: alias parts echo first %s second %s
60 In [2]: %parts A B
60 In [2]: %parts A B
61 first A second B
61 first A second B
62 In [3]: %parts A
62 In [3]: %parts A
63 Incorrect number of arguments: 2 expected.
63 Incorrect number of arguments: 2 expected.
64 parts is an alias to: 'echo first %s second %s'
64 parts is an alias to: 'echo first %s second %s'
65
65
66 Note that %l and %s are mutually exclusive. You can only use one or
66 Note that %l and %s are mutually exclusive. You can only use one or
67 the other in your aliases.
67 the other in your aliases.
68
68
69 Aliases expand Python variables just like system calls using ! or !!
69 Aliases expand Python variables just like system calls using ! or !!
70 do: all expressions prefixed with '$' get expanded. For details of
70 do: all expressions prefixed with '$' get expanded. For details of
71 the semantic rules, see PEP-215:
71 the semantic rules, see PEP-215:
72 http://www.python.org/peps/pep-0215.html. This is the library used by
72 http://www.python.org/peps/pep-0215.html. This is the library used by
73 IPython for variable expansion. If you want to access a true shell
73 IPython for variable expansion. If you want to access a true shell
74 variable, an extra $ is necessary to prevent its expansion by
74 variable, an extra $ is necessary to prevent its expansion by
75 IPython::
75 IPython::
76
76
77 In [6]: alias show echo
77 In [6]: alias show echo
78 In [7]: PATH='A Python string'
78 In [7]: PATH='A Python string'
79 In [8]: show $PATH
79 In [8]: show $PATH
80 A Python string
80 A Python string
81 In [9]: show $$PATH
81 In [9]: show $$PATH
82 /usr/local/lf9560/bin:/usr/local/intel/compiler70/ia32/bin:...
82 /usr/local/lf9560/bin:/usr/local/intel/compiler70/ia32/bin:...
83
83
84 You can use the alias facility to access all of $PATH. See the %rehashx
84 You can use the alias facility to access all of $PATH. See the %rehashx
85 function, which automatically creates aliases for the contents of your
85 function, which automatically creates aliases for the contents of your
86 $PATH.
86 $PATH.
87
87
88 If called with no parameters, %alias prints the current alias table
88 If called with no parameters, %alias prints the current alias table
89 for your system. For posix systems, the default aliases are 'cat',
89 for your system. For posix systems, the default aliases are 'cat',
90 'cp', 'mv', 'rm', 'rmdir', and 'mkdir', and other platform-specific
90 'cp', 'mv', 'rm', 'rmdir', and 'mkdir', and other platform-specific
91 aliases are added. For windows-based systems, the default aliases are
91 aliases are added. For windows-based systems, the default aliases are
92 'copy', 'ddir', 'echo', 'ls', 'ldir', 'mkdir', 'ren', and 'rmdir'.
92 'copy', 'ddir', 'echo', 'ls', 'ldir', 'mkdir', 'ren', and 'rmdir'.
93
93
94 You can see the definition of alias by adding a question mark in the
94 You can see the definition of alias by adding a question mark in the
95 end::
95 end::
96
96
97 In [1]: cat?
97 In [1]: cat?
98 Repr: <alias cat for 'cat'>"""
98 Repr: <alias cat for 'cat'>"""
99
99
100 par = parameter_s.strip()
100 par = parameter_s.strip()
101 if not par:
101 if not par:
102 aliases = sorted(self.shell.alias_manager.aliases)
102 aliases = sorted(self.shell.alias_manager.aliases)
103 # stored = self.shell.db.get('stored_aliases', {} )
103 # stored = self.shell.db.get('stored_aliases', {} )
104 # for k, v in stored:
104 # for k, v in stored:
105 # atab.append(k, v[0])
105 # atab.append(k, v[0])
106
106
107 print("Total number of aliases:", len(aliases))
107 print("Total number of aliases:", len(aliases))
108 sys.stdout.flush()
108 sys.stdout.flush()
109 return aliases
109 return aliases
110
110
111 # Now try to define a new one
111 # Now try to define a new one
112 try:
112 try:
113 alias,cmd = par.split(None, 1)
113 alias,cmd = par.split(None, 1)
114 except TypeError:
114 except TypeError:
115 print(oinspect.getdoc(self.alias))
115 print(oinspect.getdoc(self.alias))
116 return
116 return
117
117
118 try:
118 try:
119 self.shell.alias_manager.define_alias(alias, cmd)
119 self.shell.alias_manager.define_alias(alias, cmd)
120 except AliasError as e:
120 except AliasError as e:
121 print(e)
121 print(e)
122 # end magic_alias
122 # end magic_alias
123
123
124 @line_magic
124 @line_magic
125 def unalias(self, parameter_s=''):
125 def unalias(self, parameter_s=''):
126 """Remove an alias"""
126 """Remove an alias"""
127
127
128 aname = parameter_s.strip()
128 aname = parameter_s.strip()
129 try:
129 try:
130 self.shell.alias_manager.undefine_alias(aname)
130 self.shell.alias_manager.undefine_alias(aname)
131 except ValueError as e:
131 except ValueError as e:
132 print(e)
132 print(e)
133 return
133 return
134
134
135 stored = self.shell.db.get('stored_aliases', {} )
135 stored = self.shell.db.get('stored_aliases', {} )
136 if aname in stored:
136 if aname in stored:
137 print("Removing %stored alias",aname)
137 print("Removing %stored alias",aname)
138 del stored[aname]
138 del stored[aname]
139 self.shell.db['stored_aliases'] = stored
139 self.shell.db['stored_aliases'] = stored
140
140
141 @line_magic
141 @line_magic
142 def rehashx(self, parameter_s=''):
142 def rehashx(self, parameter_s=''):
143 """Update the alias table with all executable files in $PATH.
143 """Update the alias table with all executable files in $PATH.
144
144
145 rehashx explicitly checks that every entry in $PATH is a file
145 rehashx explicitly checks that every entry in $PATH is a file
146 with execute access (os.X_OK).
146 with execute access (os.X_OK).
147
147
148 Under Windows, it checks executability as a match against a
148 Under Windows, it checks executability as a match against a
149 '|'-separated string of extensions, stored in the IPython config
149 '|'-separated string of extensions, stored in the IPython config
150 variable win_exec_ext. This defaults to 'exe|com|bat'.
150 variable win_exec_ext. This defaults to 'exe|com|bat'.
151
151
152 This function also resets the root module cache of module completer,
152 This function also resets the root module cache of module completer,
153 used on slow filesystems.
153 used on slow filesystems.
154 """
154 """
155 from IPython.core.alias import InvalidAliasError
155 from IPython.core.alias import InvalidAliasError
156
156
157 # for the benefit of module completer in ipy_completers.py
157 # for the benefit of module completer in ipy_completers.py
158 del self.shell.db['rootmodules_cache']
158 del self.shell.db['rootmodules_cache']
159
159
160 path = [os.path.abspath(os.path.expanduser(p)) for p in
160 path = [os.path.abspath(os.path.expanduser(p)) for p in
161 os.environ.get('PATH','').split(os.pathsep)]
161 os.environ.get('PATH','').split(os.pathsep)]
162
162
163 syscmdlist = []
163 syscmdlist = []
164 # Now define isexec in a cross platform manner.
164 # Now define isexec in a cross platform manner.
165 if os.name == 'posix':
165 if os.name == 'posix':
166 isexec = lambda fname:os.path.isfile(fname) and \
166 isexec = lambda fname:os.path.isfile(fname) and \
167 os.access(fname,os.X_OK)
167 os.access(fname,os.X_OK)
168 else:
168 else:
169 try:
169 try:
170 winext = os.environ['pathext'].replace(';','|').replace('.','')
170 winext = os.environ['pathext'].replace(';','|').replace('.','')
171 except KeyError:
171 except KeyError:
172 winext = 'exe|com|bat|py'
172 winext = 'exe|com|bat|py'
173 if 'py' not in winext:
173 if 'py' not in winext:
174 winext += '|py'
174 winext += '|py'
175 execre = re.compile(r'(.*)\.(%s)$' % winext,re.IGNORECASE)
175 execre = re.compile(r'(.*)\.(%s)$' % winext,re.IGNORECASE)
176 isexec = lambda fname:os.path.isfile(fname) and execre.match(fname)
176 isexec = lambda fname:os.path.isfile(fname) and execre.match(fname)
177 savedir = os.getcwd()
177 savedir = os.getcwd()
178
178
179 # Now walk the paths looking for executables to alias.
179 # Now walk the paths looking for executables to alias.
180 try:
180 try:
181 # write the whole loop for posix/Windows so we don't have an if in
181 # write the whole loop for posix/Windows so we don't have an if in
182 # the innermost part
182 # the innermost part
183 if os.name == 'posix':
183 if os.name == 'posix':
184 for pdir in path:
184 for pdir in path:
185 try:
185 try:
186 os.chdir(pdir)
186 os.chdir(pdir)
187 dirlist = os.listdir(pdir)
187 dirlist = os.listdir(pdir)
188 except OSError:
188 except OSError:
189 continue
189 continue
190 for ff in dirlist:
190 for ff in dirlist:
191 if isexec(ff):
191 if isexec(ff):
192 try:
192 try:
193 # Removes dots from the name since ipython
193 # Removes dots from the name since ipython
194 # will assume names with dots to be python.
194 # will assume names with dots to be python.
195 if not self.shell.alias_manager.is_alias(ff):
195 if not self.shell.alias_manager.is_alias(ff):
196 self.shell.alias_manager.define_alias(
196 self.shell.alias_manager.define_alias(
197 ff.replace('.',''), ff)
197 ff.replace('.',''), ff)
198 except InvalidAliasError:
198 except InvalidAliasError:
199 pass
199 pass
200 else:
200 else:
201 syscmdlist.append(ff)
201 syscmdlist.append(ff)
202 else:
202 else:
203 no_alias = Alias.blacklist
203 no_alias = Alias.blacklist
204 for pdir in path:
204 for pdir in path:
205 try:
205 try:
206 os.chdir(pdir)
206 os.chdir(pdir)
207 dirlist = os.listdir(pdir)
207 dirlist = os.listdir(pdir)
208 except OSError:
208 except OSError:
209 continue
209 continue
210 for ff in dirlist:
210 for ff in dirlist:
211 base, ext = os.path.splitext(ff)
211 base, ext = os.path.splitext(ff)
212 if isexec(ff) and base.lower() not in no_alias:
212 if isexec(ff) and base.lower() not in no_alias:
213 if ext.lower() == '.exe':
213 if ext.lower() == '.exe':
214 ff = base
214 ff = base
215 try:
215 try:
216 # Removes dots from the name since ipython
216 # Removes dots from the name since ipython
217 # will assume names with dots to be python.
217 # will assume names with dots to be python.
218 self.shell.alias_manager.define_alias(
218 self.shell.alias_manager.define_alias(
219 base.lower().replace('.',''), ff)
219 base.lower().replace('.',''), ff)
220 except InvalidAliasError:
220 except InvalidAliasError:
221 pass
221 pass
222 syscmdlist.append(ff)
222 syscmdlist.append(ff)
223 self.shell.db['syscmdlist'] = syscmdlist
223 self.shell.db['syscmdlist'] = syscmdlist
224 finally:
224 finally:
225 os.chdir(savedir)
225 os.chdir(savedir)
226
226
227 @skip_doctest
227 @skip_doctest
228 @line_magic
228 @line_magic
229 def pwd(self, parameter_s=''):
229 def pwd(self, parameter_s=''):
230 """Return the current working directory path.
230 """Return the current working directory path.
231
231
232 Examples
232 Examples
233 --------
233 --------
234 ::
234 ::
235
235
236 In [9]: pwd
236 In [9]: pwd
237 Out[9]: '/home/tsuser/sprint/ipython'
237 Out[9]: '/home/tsuser/sprint/ipython'
238 """
238 """
239 try:
239 try:
240 return os.getcwd()
240 return os.getcwd()
241 except FileNotFoundError:
241 except FileNotFoundError:
242 raise UsageError("CWD no longer exists - please use %cd to change directory.")
242 raise UsageError("CWD no longer exists - please use %cd to change directory.")
243
243
244 @skip_doctest
244 @skip_doctest
245 @line_magic
245 @line_magic
246 def cd(self, parameter_s=''):
246 def cd(self, parameter_s=''):
247 """Change the current working directory.
247 """Change the current working directory.
248
248
249 This command automatically maintains an internal list of directories
249 This command automatically maintains an internal list of directories
250 you visit during your IPython session, in the variable _dh. The
250 you visit during your IPython session, in the variable _dh. The
251 command %dhist shows this history nicely formatted. You can also
251 command %dhist shows this history nicely formatted. You can also
252 do 'cd -<tab>' to see directory history conveniently.
252 do 'cd -<tab>' to see directory history conveniently.
253
253
254 Usage:
254 Usage:
255
255
256 cd 'dir': changes to directory 'dir'.
256 cd 'dir': changes to directory 'dir'.
257
257
258 cd -: changes to the last visited directory.
258 cd -: changes to the last visited directory.
259
259
260 cd -<n>: changes to the n-th directory in the directory history.
260 cd -<n>: changes to the n-th directory in the directory history.
261
261
262 cd --foo: change to directory that matches 'foo' in history
262 cd --foo: change to directory that matches 'foo' in history
263
263
264 cd -b <bookmark_name>: jump to a bookmark set by %bookmark
264 cd -b <bookmark_name>: jump to a bookmark set by %bookmark
265 (note: cd <bookmark_name> is enough if there is no
265 (note: cd <bookmark_name> is enough if there is no
266 directory <bookmark_name>, but a bookmark with the name exists.)
266 directory <bookmark_name>, but a bookmark with the name exists.)
267 'cd -b <tab>' allows you to tab-complete bookmark names.
267 'cd -b <tab>' allows you to tab-complete bookmark names.
268
268
269 Options:
269 Options:
270
270
271 -q: quiet. Do not print the working directory after the cd command is
271 -q: quiet. Do not print the working directory after the cd command is
272 executed. By default IPython's cd command does print this directory,
272 executed. By default IPython's cd command does print this directory,
273 since the default prompts do not display path information.
273 since the default prompts do not display path information.
274
274
275 Note that !cd doesn't work for this purpose because the shell where
275 Note that !cd doesn't work for this purpose because the shell where
276 !command runs is immediately discarded after executing 'command'.
276 !command runs is immediately discarded after executing 'command'.
277
277
278 Examples
278 Examples
279 --------
279 --------
280 ::
280 ::
281
281
282 In [10]: cd parent/child
282 In [10]: cd parent/child
283 /home/tsuser/parent/child
283 /home/tsuser/parent/child
284 """
284 """
285
285
286 try:
286 try:
287 oldcwd = os.getcwd()
287 oldcwd = os.getcwd()
288 except FileNotFoundError:
288 except FileNotFoundError:
289 # Happens if the CWD has been deleted.
289 # Happens if the CWD has been deleted.
290 oldcwd = None
290 oldcwd = None
291
291
292 numcd = re.match(r'(-)(\d+)$',parameter_s)
292 numcd = re.match(r'(-)(\d+)$',parameter_s)
293 # jump in directory history by number
293 # jump in directory history by number
294 if numcd:
294 if numcd:
295 nn = int(numcd.group(2))
295 nn = int(numcd.group(2))
296 try:
296 try:
297 ps = self.shell.user_ns['_dh'][nn]
297 ps = self.shell.user_ns['_dh'][nn]
298 except IndexError:
298 except IndexError:
299 print('The requested directory does not exist in history.')
299 print('The requested directory does not exist in history.')
300 return
300 return
301 else:
301 else:
302 opts = {}
302 opts = {}
303 elif parameter_s.startswith('--'):
303 elif parameter_s.startswith('--'):
304 ps = None
304 ps = None
305 fallback = None
305 fallback = None
306 pat = parameter_s[2:]
306 pat = parameter_s[2:]
307 dh = self.shell.user_ns['_dh']
307 dh = self.shell.user_ns['_dh']
308 # first search only by basename (last component)
308 # first search only by basename (last component)
309 for ent in reversed(dh):
309 for ent in reversed(dh):
310 if pat in os.path.basename(ent) and os.path.isdir(ent):
310 if pat in os.path.basename(ent) and os.path.isdir(ent):
311 ps = ent
311 ps = ent
312 break
312 break
313
313
314 if fallback is None and pat in ent and os.path.isdir(ent):
314 if fallback is None and pat in ent and os.path.isdir(ent):
315 fallback = ent
315 fallback = ent
316
316
317 # if we have no last part match, pick the first full path match
317 # if we have no last part match, pick the first full path match
318 if ps is None:
318 if ps is None:
319 ps = fallback
319 ps = fallback
320
320
321 if ps is None:
321 if ps is None:
322 print("No matching entry in directory history")
322 print("No matching entry in directory history")
323 return
323 return
324 else:
324 else:
325 opts = {}
325 opts = {}
326
326
327
327
328 else:
328 else:
329 opts, ps = self.parse_options(parameter_s, 'qb', mode='string')
329 opts, ps = self.parse_options(parameter_s, 'qb', mode='string')
330 # jump to previous
330 # jump to previous
331 if ps == '-':
331 if ps == '-':
332 try:
332 try:
333 ps = self.shell.user_ns['_dh'][-2]
333 ps = self.shell.user_ns['_dh'][-2]
334 except IndexError:
334 except IndexError:
335 raise UsageError('%cd -: No previous directory to change to.')
335 raise UsageError('%cd -: No previous directory to change to.')
336 # jump to bookmark if needed
336 # jump to bookmark if needed
337 else:
337 else:
338 if not os.path.isdir(ps) or 'b' in opts:
338 if not os.path.isdir(ps) or 'b' in opts:
339 bkms = self.shell.db.get('bookmarks', {})
339 bkms = self.shell.db.get('bookmarks', {})
340
340
341 if ps in bkms:
341 if ps in bkms:
342 target = bkms[ps]
342 target = bkms[ps]
343 print('(bookmark:%s) -> %s' % (ps, target))
343 print('(bookmark:%s) -> %s' % (ps, target))
344 ps = target
344 ps = target
345 else:
345 else:
346 if 'b' in opts:
346 if 'b' in opts:
347 raise UsageError("Bookmark '%s' not found. "
347 raise UsageError("Bookmark '%s' not found. "
348 "Use '%%bookmark -l' to see your bookmarks." % ps)
348 "Use '%%bookmark -l' to see your bookmarks." % ps)
349
349
350 # at this point ps should point to the target dir
350 # at this point ps should point to the target dir
351 if ps:
351 if ps:
352 try:
352 try:
353 os.chdir(os.path.expanduser(ps))
353 os.chdir(os.path.expanduser(ps))
354 if hasattr(self.shell, 'term_title') and self.shell.term_title:
354 if hasattr(self.shell, 'term_title') and self.shell.term_title:
355 set_term_title('IPython: ' + abbrev_cwd())
355 set_term_title(self.shell.term_title_format.format(cwd=abbrev_cwd()))
356 except OSError:
356 except OSError:
357 print(sys.exc_info()[1])
357 print(sys.exc_info()[1])
358 else:
358 else:
359 cwd = os.getcwd()
359 cwd = os.getcwd()
360 dhist = self.shell.user_ns['_dh']
360 dhist = self.shell.user_ns['_dh']
361 if oldcwd != cwd:
361 if oldcwd != cwd:
362 dhist.append(cwd)
362 dhist.append(cwd)
363 self.shell.db['dhist'] = compress_dhist(dhist)[-100:]
363 self.shell.db['dhist'] = compress_dhist(dhist)[-100:]
364
364
365 else:
365 else:
366 os.chdir(self.shell.home_dir)
366 os.chdir(self.shell.home_dir)
367 if hasattr(self.shell, 'term_title') and self.shell.term_title:
367 if hasattr(self.shell, 'term_title') and self.shell.term_title:
368 set_term_title('IPython: ' + '~')
368 set_term_title(self.shell.term_title_format.format(cwd="~"))
369 cwd = os.getcwd()
369 cwd = os.getcwd()
370 dhist = self.shell.user_ns['_dh']
370 dhist = self.shell.user_ns['_dh']
371
371
372 if oldcwd != cwd:
372 if oldcwd != cwd:
373 dhist.append(cwd)
373 dhist.append(cwd)
374 self.shell.db['dhist'] = compress_dhist(dhist)[-100:]
374 self.shell.db['dhist'] = compress_dhist(dhist)[-100:]
375 if not 'q' in opts and self.shell.user_ns['_dh']:
375 if not 'q' in opts and self.shell.user_ns['_dh']:
376 print(self.shell.user_ns['_dh'][-1])
376 print(self.shell.user_ns['_dh'][-1])
377
377
378 @line_magic
378 @line_magic
379 def env(self, parameter_s=''):
379 def env(self, parameter_s=''):
380 """Get, set, or list environment variables.
380 """Get, set, or list environment variables.
381
381
382 Usage:\\
382 Usage:\\
383
383
384 %env: lists all environment variables/values
384 %env: lists all environment variables/values
385 %env var: get value for var
385 %env var: get value for var
386 %env var val: set value for var
386 %env var val: set value for var
387 %env var=val: set value for var
387 %env var=val: set value for var
388 %env var=$val: set value for var, using python expansion if possible
388 %env var=$val: set value for var, using python expansion if possible
389 """
389 """
390 if parameter_s.strip():
390 if parameter_s.strip():
391 split = '=' if '=' in parameter_s else ' '
391 split = '=' if '=' in parameter_s else ' '
392 bits = parameter_s.split(split)
392 bits = parameter_s.split(split)
393 if len(bits) == 1:
393 if len(bits) == 1:
394 key = parameter_s.strip()
394 key = parameter_s.strip()
395 if key in os.environ:
395 if key in os.environ:
396 return os.environ[key]
396 return os.environ[key]
397 else:
397 else:
398 err = "Environment does not have key: {0}".format(key)
398 err = "Environment does not have key: {0}".format(key)
399 raise UsageError(err)
399 raise UsageError(err)
400 if len(bits) > 1:
400 if len(bits) > 1:
401 return self.set_env(parameter_s)
401 return self.set_env(parameter_s)
402 return dict(os.environ)
402 return dict(os.environ)
403
403
404 @line_magic
404 @line_magic
405 def set_env(self, parameter_s):
405 def set_env(self, parameter_s):
406 """Set environment variables. Assumptions are that either "val" is a
406 """Set environment variables. Assumptions are that either "val" is a
407 name in the user namespace, or val is something that evaluates to a
407 name in the user namespace, or val is something that evaluates to a
408 string.
408 string.
409
409
410 Usage:\\
410 Usage:\\
411 %set_env var val: set value for var
411 %set_env var val: set value for var
412 %set_env var=val: set value for var
412 %set_env var=val: set value for var
413 %set_env var=$val: set value for var, using python expansion if possible
413 %set_env var=$val: set value for var, using python expansion if possible
414 """
414 """
415 split = '=' if '=' in parameter_s else ' '
415 split = '=' if '=' in parameter_s else ' '
416 bits = parameter_s.split(split, 1)
416 bits = parameter_s.split(split, 1)
417 if not parameter_s.strip() or len(bits)<2:
417 if not parameter_s.strip() or len(bits)<2:
418 raise UsageError("usage is 'set_env var=val'")
418 raise UsageError("usage is 'set_env var=val'")
419 var = bits[0].strip()
419 var = bits[0].strip()
420 val = bits[1].strip()
420 val = bits[1].strip()
421 if re.match(r'.*\s.*', var):
421 if re.match(r'.*\s.*', var):
422 # an environment variable with whitespace is almost certainly
422 # an environment variable with whitespace is almost certainly
423 # not what the user intended. what's more likely is the wrong
423 # not what the user intended. what's more likely is the wrong
424 # split was chosen, ie for "set_env cmd_args A=B", we chose
424 # split was chosen, ie for "set_env cmd_args A=B", we chose
425 # '=' for the split and should have chosen ' '. to get around
425 # '=' for the split and should have chosen ' '. to get around
426 # this, users should just assign directly to os.environ or use
426 # this, users should just assign directly to os.environ or use
427 # standard magic {var} expansion.
427 # standard magic {var} expansion.
428 err = "refusing to set env var with whitespace: '{0}'"
428 err = "refusing to set env var with whitespace: '{0}'"
429 err = err.format(val)
429 err = err.format(val)
430 raise UsageError(err)
430 raise UsageError(err)
431 os.environ[py3compat.cast_bytes_py2(var)] = py3compat.cast_bytes_py2(val)
431 os.environ[py3compat.cast_bytes_py2(var)] = py3compat.cast_bytes_py2(val)
432 print('env: {0}={1}'.format(var,val))
432 print('env: {0}={1}'.format(var,val))
433
433
434 @line_magic
434 @line_magic
435 def pushd(self, parameter_s=''):
435 def pushd(self, parameter_s=''):
436 """Place the current dir on stack and change directory.
436 """Place the current dir on stack and change directory.
437
437
438 Usage:\\
438 Usage:\\
439 %pushd ['dirname']
439 %pushd ['dirname']
440 """
440 """
441
441
442 dir_s = self.shell.dir_stack
442 dir_s = self.shell.dir_stack
443 tgt = os.path.expanduser(parameter_s)
443 tgt = os.path.expanduser(parameter_s)
444 cwd = os.getcwd().replace(self.shell.home_dir,'~')
444 cwd = os.getcwd().replace(self.shell.home_dir,'~')
445 if tgt:
445 if tgt:
446 self.cd(parameter_s)
446 self.cd(parameter_s)
447 dir_s.insert(0,cwd)
447 dir_s.insert(0,cwd)
448 return self.shell.magic('dirs')
448 return self.shell.magic('dirs')
449
449
450 @line_magic
450 @line_magic
451 def popd(self, parameter_s=''):
451 def popd(self, parameter_s=''):
452 """Change to directory popped off the top of the stack.
452 """Change to directory popped off the top of the stack.
453 """
453 """
454 if not self.shell.dir_stack:
454 if not self.shell.dir_stack:
455 raise UsageError("%popd on empty stack")
455 raise UsageError("%popd on empty stack")
456 top = self.shell.dir_stack.pop(0)
456 top = self.shell.dir_stack.pop(0)
457 self.cd(top)
457 self.cd(top)
458 print("popd ->",top)
458 print("popd ->",top)
459
459
460 @line_magic
460 @line_magic
461 def dirs(self, parameter_s=''):
461 def dirs(self, parameter_s=''):
462 """Return the current directory stack."""
462 """Return the current directory stack."""
463
463
464 return self.shell.dir_stack
464 return self.shell.dir_stack
465
465
466 @line_magic
466 @line_magic
467 def dhist(self, parameter_s=''):
467 def dhist(self, parameter_s=''):
468 """Print your history of visited directories.
468 """Print your history of visited directories.
469
469
470 %dhist -> print full history\\
470 %dhist -> print full history\\
471 %dhist n -> print last n entries only\\
471 %dhist n -> print last n entries only\\
472 %dhist n1 n2 -> print entries between n1 and n2 (n2 not included)\\
472 %dhist n1 n2 -> print entries between n1 and n2 (n2 not included)\\
473
473
474 This history is automatically maintained by the %cd command, and
474 This history is automatically maintained by the %cd command, and
475 always available as the global list variable _dh. You can use %cd -<n>
475 always available as the global list variable _dh. You can use %cd -<n>
476 to go to directory number <n>.
476 to go to directory number <n>.
477
477
478 Note that most of time, you should view directory history by entering
478 Note that most of time, you should view directory history by entering
479 cd -<TAB>.
479 cd -<TAB>.
480
480
481 """
481 """
482
482
483 dh = self.shell.user_ns['_dh']
483 dh = self.shell.user_ns['_dh']
484 if parameter_s:
484 if parameter_s:
485 try:
485 try:
486 args = map(int,parameter_s.split())
486 args = map(int,parameter_s.split())
487 except:
487 except:
488 self.arg_err(self.dhist)
488 self.arg_err(self.dhist)
489 return
489 return
490 if len(args) == 1:
490 if len(args) == 1:
491 ini,fin = max(len(dh)-(args[0]),0),len(dh)
491 ini,fin = max(len(dh)-(args[0]),0),len(dh)
492 elif len(args) == 2:
492 elif len(args) == 2:
493 ini,fin = args
493 ini,fin = args
494 fin = min(fin, len(dh))
494 fin = min(fin, len(dh))
495 else:
495 else:
496 self.arg_err(self.dhist)
496 self.arg_err(self.dhist)
497 return
497 return
498 else:
498 else:
499 ini,fin = 0,len(dh)
499 ini,fin = 0,len(dh)
500 print('Directory history (kept in _dh)')
500 print('Directory history (kept in _dh)')
501 for i in range(ini, fin):
501 for i in range(ini, fin):
502 print("%d: %s" % (i, dh[i]))
502 print("%d: %s" % (i, dh[i]))
503
503
504 @skip_doctest
504 @skip_doctest
505 @line_magic
505 @line_magic
506 def sc(self, parameter_s=''):
506 def sc(self, parameter_s=''):
507 """Shell capture - run shell command and capture output (DEPRECATED use !).
507 """Shell capture - run shell command and capture output (DEPRECATED use !).
508
508
509 DEPRECATED. Suboptimal, retained for backwards compatibility.
509 DEPRECATED. Suboptimal, retained for backwards compatibility.
510
510
511 You should use the form 'var = !command' instead. Example:
511 You should use the form 'var = !command' instead. Example:
512
512
513 "%sc -l myfiles = ls ~" should now be written as
513 "%sc -l myfiles = ls ~" should now be written as
514
514
515 "myfiles = !ls ~"
515 "myfiles = !ls ~"
516
516
517 myfiles.s, myfiles.l and myfiles.n still apply as documented
517 myfiles.s, myfiles.l and myfiles.n still apply as documented
518 below.
518 below.
519
519
520 --
520 --
521 %sc [options] varname=command
521 %sc [options] varname=command
522
522
523 IPython will run the given command using commands.getoutput(), and
523 IPython will run the given command using commands.getoutput(), and
524 will then update the user's interactive namespace with a variable
524 will then update the user's interactive namespace with a variable
525 called varname, containing the value of the call. Your command can
525 called varname, containing the value of the call. Your command can
526 contain shell wildcards, pipes, etc.
526 contain shell wildcards, pipes, etc.
527
527
528 The '=' sign in the syntax is mandatory, and the variable name you
528 The '=' sign in the syntax is mandatory, and the variable name you
529 supply must follow Python's standard conventions for valid names.
529 supply must follow Python's standard conventions for valid names.
530
530
531 (A special format without variable name exists for internal use)
531 (A special format without variable name exists for internal use)
532
532
533 Options:
533 Options:
534
534
535 -l: list output. Split the output on newlines into a list before
535 -l: list output. Split the output on newlines into a list before
536 assigning it to the given variable. By default the output is stored
536 assigning it to the given variable. By default the output is stored
537 as a single string.
537 as a single string.
538
538
539 -v: verbose. Print the contents of the variable.
539 -v: verbose. Print the contents of the variable.
540
540
541 In most cases you should not need to split as a list, because the
541 In most cases you should not need to split as a list, because the
542 returned value is a special type of string which can automatically
542 returned value is a special type of string which can automatically
543 provide its contents either as a list (split on newlines) or as a
543 provide its contents either as a list (split on newlines) or as a
544 space-separated string. These are convenient, respectively, either
544 space-separated string. These are convenient, respectively, either
545 for sequential processing or to be passed to a shell command.
545 for sequential processing or to be passed to a shell command.
546
546
547 For example::
547 For example::
548
548
549 # Capture into variable a
549 # Capture into variable a
550 In [1]: sc a=ls *py
550 In [1]: sc a=ls *py
551
551
552 # a is a string with embedded newlines
552 # a is a string with embedded newlines
553 In [2]: a
553 In [2]: a
554 Out[2]: 'setup.py\\nwin32_manual_post_install.py'
554 Out[2]: 'setup.py\\nwin32_manual_post_install.py'
555
555
556 # which can be seen as a list:
556 # which can be seen as a list:
557 In [3]: a.l
557 In [3]: a.l
558 Out[3]: ['setup.py', 'win32_manual_post_install.py']
558 Out[3]: ['setup.py', 'win32_manual_post_install.py']
559
559
560 # or as a whitespace-separated string:
560 # or as a whitespace-separated string:
561 In [4]: a.s
561 In [4]: a.s
562 Out[4]: 'setup.py win32_manual_post_install.py'
562 Out[4]: 'setup.py win32_manual_post_install.py'
563
563
564 # a.s is useful to pass as a single command line:
564 # a.s is useful to pass as a single command line:
565 In [5]: !wc -l $a.s
565 In [5]: !wc -l $a.s
566 146 setup.py
566 146 setup.py
567 130 win32_manual_post_install.py
567 130 win32_manual_post_install.py
568 276 total
568 276 total
569
569
570 # while the list form is useful to loop over:
570 # while the list form is useful to loop over:
571 In [6]: for f in a.l:
571 In [6]: for f in a.l:
572 ...: !wc -l $f
572 ...: !wc -l $f
573 ...:
573 ...:
574 146 setup.py
574 146 setup.py
575 130 win32_manual_post_install.py
575 130 win32_manual_post_install.py
576
576
577 Similarly, the lists returned by the -l option are also special, in
577 Similarly, the lists returned by the -l option are also special, in
578 the sense that you can equally invoke the .s attribute on them to
578 the sense that you can equally invoke the .s attribute on them to
579 automatically get a whitespace-separated string from their contents::
579 automatically get a whitespace-separated string from their contents::
580
580
581 In [7]: sc -l b=ls *py
581 In [7]: sc -l b=ls *py
582
582
583 In [8]: b
583 In [8]: b
584 Out[8]: ['setup.py', 'win32_manual_post_install.py']
584 Out[8]: ['setup.py', 'win32_manual_post_install.py']
585
585
586 In [9]: b.s
586 In [9]: b.s
587 Out[9]: 'setup.py win32_manual_post_install.py'
587 Out[9]: 'setup.py win32_manual_post_install.py'
588
588
589 In summary, both the lists and strings used for output capture have
589 In summary, both the lists and strings used for output capture have
590 the following special attributes::
590 the following special attributes::
591
591
592 .l (or .list) : value as list.
592 .l (or .list) : value as list.
593 .n (or .nlstr): value as newline-separated string.
593 .n (or .nlstr): value as newline-separated string.
594 .s (or .spstr): value as space-separated string.
594 .s (or .spstr): value as space-separated string.
595 """
595 """
596
596
597 opts,args = self.parse_options(parameter_s, 'lv')
597 opts,args = self.parse_options(parameter_s, 'lv')
598 # Try to get a variable name and command to run
598 # Try to get a variable name and command to run
599 try:
599 try:
600 # the variable name must be obtained from the parse_options
600 # the variable name must be obtained from the parse_options
601 # output, which uses shlex.split to strip options out.
601 # output, which uses shlex.split to strip options out.
602 var,_ = args.split('=', 1)
602 var,_ = args.split('=', 1)
603 var = var.strip()
603 var = var.strip()
604 # But the command has to be extracted from the original input
604 # But the command has to be extracted from the original input
605 # parameter_s, not on what parse_options returns, to avoid the
605 # parameter_s, not on what parse_options returns, to avoid the
606 # quote stripping which shlex.split performs on it.
606 # quote stripping which shlex.split performs on it.
607 _,cmd = parameter_s.split('=', 1)
607 _,cmd = parameter_s.split('=', 1)
608 except ValueError:
608 except ValueError:
609 var,cmd = '',''
609 var,cmd = '',''
610 # If all looks ok, proceed
610 # If all looks ok, proceed
611 split = 'l' in opts
611 split = 'l' in opts
612 out = self.shell.getoutput(cmd, split=split)
612 out = self.shell.getoutput(cmd, split=split)
613 if 'v' in opts:
613 if 'v' in opts:
614 print('%s ==\n%s' % (var, pformat(out)))
614 print('%s ==\n%s' % (var, pformat(out)))
615 if var:
615 if var:
616 self.shell.user_ns.update({var:out})
616 self.shell.user_ns.update({var:out})
617 else:
617 else:
618 return out
618 return out
619
619
620 @line_cell_magic
620 @line_cell_magic
621 def sx(self, line='', cell=None):
621 def sx(self, line='', cell=None):
622 """Shell execute - run shell command and capture output (!! is short-hand).
622 """Shell execute - run shell command and capture output (!! is short-hand).
623
623
624 %sx command
624 %sx command
625
625
626 IPython will run the given command using commands.getoutput(), and
626 IPython will run the given command using commands.getoutput(), and
627 return the result formatted as a list (split on '\\n'). Since the
627 return the result formatted as a list (split on '\\n'). Since the
628 output is _returned_, it will be stored in ipython's regular output
628 output is _returned_, it will be stored in ipython's regular output
629 cache Out[N] and in the '_N' automatic variables.
629 cache Out[N] and in the '_N' automatic variables.
630
630
631 Notes:
631 Notes:
632
632
633 1) If an input line begins with '!!', then %sx is automatically
633 1) If an input line begins with '!!', then %sx is automatically
634 invoked. That is, while::
634 invoked. That is, while::
635
635
636 !ls
636 !ls
637
637
638 causes ipython to simply issue system('ls'), typing::
638 causes ipython to simply issue system('ls'), typing::
639
639
640 !!ls
640 !!ls
641
641
642 is a shorthand equivalent to::
642 is a shorthand equivalent to::
643
643
644 %sx ls
644 %sx ls
645
645
646 2) %sx differs from %sc in that %sx automatically splits into a list,
646 2) %sx differs from %sc in that %sx automatically splits into a list,
647 like '%sc -l'. The reason for this is to make it as easy as possible
647 like '%sc -l'. The reason for this is to make it as easy as possible
648 to process line-oriented shell output via further python commands.
648 to process line-oriented shell output via further python commands.
649 %sc is meant to provide much finer control, but requires more
649 %sc is meant to provide much finer control, but requires more
650 typing.
650 typing.
651
651
652 3) Just like %sc -l, this is a list with special attributes:
652 3) Just like %sc -l, this is a list with special attributes:
653 ::
653 ::
654
654
655 .l (or .list) : value as list.
655 .l (or .list) : value as list.
656 .n (or .nlstr): value as newline-separated string.
656 .n (or .nlstr): value as newline-separated string.
657 .s (or .spstr): value as whitespace-separated string.
657 .s (or .spstr): value as whitespace-separated string.
658
658
659 This is very useful when trying to use such lists as arguments to
659 This is very useful when trying to use such lists as arguments to
660 system commands."""
660 system commands."""
661
661
662 if cell is None:
662 if cell is None:
663 # line magic
663 # line magic
664 return self.shell.getoutput(line)
664 return self.shell.getoutput(line)
665 else:
665 else:
666 opts,args = self.parse_options(line, '', 'out=')
666 opts,args = self.parse_options(line, '', 'out=')
667 output = self.shell.getoutput(cell)
667 output = self.shell.getoutput(cell)
668 out_name = opts.get('out', opts.get('o'))
668 out_name = opts.get('out', opts.get('o'))
669 if out_name:
669 if out_name:
670 self.shell.user_ns[out_name] = output
670 self.shell.user_ns[out_name] = output
671 else:
671 else:
672 return output
672 return output
673
673
674 system = line_cell_magic('system')(sx)
674 system = line_cell_magic('system')(sx)
675 bang = cell_magic('!')(sx)
675 bang = cell_magic('!')(sx)
676
676
677 @line_magic
677 @line_magic
678 def bookmark(self, parameter_s=''):
678 def bookmark(self, parameter_s=''):
679 """Manage IPython's bookmark system.
679 """Manage IPython's bookmark system.
680
680
681 %bookmark <name> - set bookmark to current dir
681 %bookmark <name> - set bookmark to current dir
682 %bookmark <name> <dir> - set bookmark to <dir>
682 %bookmark <name> <dir> - set bookmark to <dir>
683 %bookmark -l - list all bookmarks
683 %bookmark -l - list all bookmarks
684 %bookmark -d <name> - remove bookmark
684 %bookmark -d <name> - remove bookmark
685 %bookmark -r - remove all bookmarks
685 %bookmark -r - remove all bookmarks
686
686
687 You can later on access a bookmarked folder with::
687 You can later on access a bookmarked folder with::
688
688
689 %cd -b <name>
689 %cd -b <name>
690
690
691 or simply '%cd <name>' if there is no directory called <name> AND
691 or simply '%cd <name>' if there is no directory called <name> AND
692 there is such a bookmark defined.
692 there is such a bookmark defined.
693
693
694 Your bookmarks persist through IPython sessions, but they are
694 Your bookmarks persist through IPython sessions, but they are
695 associated with each profile."""
695 associated with each profile."""
696
696
697 opts,args = self.parse_options(parameter_s,'drl',mode='list')
697 opts,args = self.parse_options(parameter_s,'drl',mode='list')
698 if len(args) > 2:
698 if len(args) > 2:
699 raise UsageError("%bookmark: too many arguments")
699 raise UsageError("%bookmark: too many arguments")
700
700
701 bkms = self.shell.db.get('bookmarks',{})
701 bkms = self.shell.db.get('bookmarks',{})
702
702
703 if 'd' in opts:
703 if 'd' in opts:
704 try:
704 try:
705 todel = args[0]
705 todel = args[0]
706 except IndexError:
706 except IndexError:
707 raise UsageError(
707 raise UsageError(
708 "%bookmark -d: must provide a bookmark to delete")
708 "%bookmark -d: must provide a bookmark to delete")
709 else:
709 else:
710 try:
710 try:
711 del bkms[todel]
711 del bkms[todel]
712 except KeyError:
712 except KeyError:
713 raise UsageError(
713 raise UsageError(
714 "%%bookmark -d: Can't delete bookmark '%s'" % todel)
714 "%%bookmark -d: Can't delete bookmark '%s'" % todel)
715
715
716 elif 'r' in opts:
716 elif 'r' in opts:
717 bkms = {}
717 bkms = {}
718 elif 'l' in opts:
718 elif 'l' in opts:
719 bks = sorted(bkms)
719 bks = sorted(bkms)
720 if bks:
720 if bks:
721 size = max(map(len, bks))
721 size = max(map(len, bks))
722 else:
722 else:
723 size = 0
723 size = 0
724 fmt = '%-'+str(size)+'s -> %s'
724 fmt = '%-'+str(size)+'s -> %s'
725 print('Current bookmarks:')
725 print('Current bookmarks:')
726 for bk in bks:
726 for bk in bks:
727 print(fmt % (bk, bkms[bk]))
727 print(fmt % (bk, bkms[bk]))
728 else:
728 else:
729 if not args:
729 if not args:
730 raise UsageError("%bookmark: You must specify the bookmark name")
730 raise UsageError("%bookmark: You must specify the bookmark name")
731 elif len(args)==1:
731 elif len(args)==1:
732 bkms[args[0]] = os.getcwd()
732 bkms[args[0]] = os.getcwd()
733 elif len(args)==2:
733 elif len(args)==2:
734 bkms[args[0]] = args[1]
734 bkms[args[0]] = args[1]
735 self.shell.db['bookmarks'] = bkms
735 self.shell.db['bookmarks'] = bkms
736
736
737 @line_magic
737 @line_magic
738 def pycat(self, parameter_s=''):
738 def pycat(self, parameter_s=''):
739 """Show a syntax-highlighted file through a pager.
739 """Show a syntax-highlighted file through a pager.
740
740
741 This magic is similar to the cat utility, but it will assume the file
741 This magic is similar to the cat utility, but it will assume the file
742 to be Python source and will show it with syntax highlighting.
742 to be Python source and will show it with syntax highlighting.
743
743
744 This magic command can either take a local filename, an url,
744 This magic command can either take a local filename, an url,
745 an history range (see %history) or a macro as argument ::
745 an history range (see %history) or a macro as argument ::
746
746
747 %pycat myscript.py
747 %pycat myscript.py
748 %pycat 7-27
748 %pycat 7-27
749 %pycat myMacro
749 %pycat myMacro
750 %pycat http://www.example.com/myscript.py
750 %pycat http://www.example.com/myscript.py
751 """
751 """
752 if not parameter_s:
752 if not parameter_s:
753 raise UsageError('Missing filename, URL, input history range, '
753 raise UsageError('Missing filename, URL, input history range, '
754 'or macro.')
754 'or macro.')
755
755
756 try :
756 try :
757 cont = self.shell.find_user_code(parameter_s, skip_encoding_cookie=False)
757 cont = self.shell.find_user_code(parameter_s, skip_encoding_cookie=False)
758 except (ValueError, IOError):
758 except (ValueError, IOError):
759 print("Error: no such file, variable, URL, history range or macro")
759 print("Error: no such file, variable, URL, history range or macro")
760 return
760 return
761
761
762 page.page(self.shell.pycolorize(source_to_unicode(cont)))
762 page.page(self.shell.pycolorize(source_to_unicode(cont)))
763
763
764 @magic_arguments.magic_arguments()
764 @magic_arguments.magic_arguments()
765 @magic_arguments.argument(
765 @magic_arguments.argument(
766 '-a', '--append', action='store_true', default=False,
766 '-a', '--append', action='store_true', default=False,
767 help='Append contents of the cell to an existing file. '
767 help='Append contents of the cell to an existing file. '
768 'The file will be created if it does not exist.'
768 'The file will be created if it does not exist.'
769 )
769 )
770 @magic_arguments.argument(
770 @magic_arguments.argument(
771 'filename', type=str,
771 'filename', type=str,
772 help='file to write'
772 help='file to write'
773 )
773 )
774 @cell_magic
774 @cell_magic
775 def writefile(self, line, cell):
775 def writefile(self, line, cell):
776 """Write the contents of the cell to a file.
776 """Write the contents of the cell to a file.
777
777
778 The file will be overwritten unless the -a (--append) flag is specified.
778 The file will be overwritten unless the -a (--append) flag is specified.
779 """
779 """
780 args = magic_arguments.parse_argstring(self.writefile, line)
780 args = magic_arguments.parse_argstring(self.writefile, line)
781 filename = os.path.expanduser(args.filename)
781 filename = os.path.expanduser(args.filename)
782
782
783 if os.path.exists(filename):
783 if os.path.exists(filename):
784 if args.append:
784 if args.append:
785 print("Appending to %s" % filename)
785 print("Appending to %s" % filename)
786 else:
786 else:
787 print("Overwriting %s" % filename)
787 print("Overwriting %s" % filename)
788 else:
788 else:
789 print("Writing %s" % filename)
789 print("Writing %s" % filename)
790
790
791 mode = 'a' if args.append else 'w'
791 mode = 'a' if args.append else 'w'
792 with io.open(filename, mode, encoding='utf-8') as f:
792 with io.open(filename, mode, encoding='utf-8') as f:
793 f.write(cell)
793 f.write(cell)
@@ -1,525 +1,530 b''
1 """IPython terminal interface using prompt_toolkit"""
1 """IPython terminal interface using prompt_toolkit"""
2
2
3 import os
3 import os
4 import sys
4 import sys
5 import warnings
5 import warnings
6 from warnings import warn
6 from warnings import warn
7
7
8 from IPython.core.interactiveshell import InteractiveShell, InteractiveShellABC
8 from IPython.core.interactiveshell import InteractiveShell, InteractiveShellABC
9 from IPython.utils import io
9 from IPython.utils import io
10 from IPython.utils.py3compat import cast_unicode_py2, input
10 from IPython.utils.py3compat import cast_unicode_py2, input
11 from IPython.utils.terminal import toggle_set_term_title, set_term_title
11 from IPython.utils.terminal import toggle_set_term_title, set_term_title
12 from IPython.utils.process import abbrev_cwd
12 from IPython.utils.process import abbrev_cwd
13 from traitlets import (
13 from traitlets import (
14 Bool, Unicode, Dict, Integer, observe, Instance, Type, default, Enum, Union,
14 Bool, Unicode, Dict, Integer, observe, Instance, Type, default, Enum, Union,
15 Any,
15 Any,
16 )
16 )
17
17
18 from prompt_toolkit.document import Document
18 from prompt_toolkit.document import Document
19 from prompt_toolkit.enums import DEFAULT_BUFFER, EditingMode
19 from prompt_toolkit.enums import DEFAULT_BUFFER, EditingMode
20 from prompt_toolkit.filters import (HasFocus, Condition, IsDone)
20 from prompt_toolkit.filters import (HasFocus, Condition, IsDone)
21 from prompt_toolkit.history import InMemoryHistory
21 from prompt_toolkit.history import InMemoryHistory
22 from prompt_toolkit.shortcuts import create_prompt_application, create_eventloop, create_prompt_layout, create_output
22 from prompt_toolkit.shortcuts import create_prompt_application, create_eventloop, create_prompt_layout, create_output
23 from prompt_toolkit.interface import CommandLineInterface
23 from prompt_toolkit.interface import CommandLineInterface
24 from prompt_toolkit.key_binding.manager import KeyBindingManager
24 from prompt_toolkit.key_binding.manager import KeyBindingManager
25 from prompt_toolkit.layout.processors import ConditionalProcessor, HighlightMatchingBracketProcessor
25 from prompt_toolkit.layout.processors import ConditionalProcessor, HighlightMatchingBracketProcessor
26 from prompt_toolkit.styles import PygmentsStyle, DynamicStyle
26 from prompt_toolkit.styles import PygmentsStyle, DynamicStyle
27
27
28 from pygments.styles import get_style_by_name, get_all_styles
28 from pygments.styles import get_style_by_name, get_all_styles
29 from pygments.style import Style
29 from pygments.style import Style
30 from pygments.token import Token
30 from pygments.token import Token
31
31
32 from .debugger import TerminalPdb, Pdb
32 from .debugger import TerminalPdb, Pdb
33 from .magics import TerminalMagics
33 from .magics import TerminalMagics
34 from .pt_inputhooks import get_inputhook_name_and_func
34 from .pt_inputhooks import get_inputhook_name_and_func
35 from .prompts import Prompts, ClassicPrompts, RichPromptDisplayHook
35 from .prompts import Prompts, ClassicPrompts, RichPromptDisplayHook
36 from .ptutils import IPythonPTCompleter, IPythonPTLexer
36 from .ptutils import IPythonPTCompleter, IPythonPTLexer
37 from .shortcuts import register_ipython_shortcuts
37 from .shortcuts import register_ipython_shortcuts
38
38
39 DISPLAY_BANNER_DEPRECATED = object()
39 DISPLAY_BANNER_DEPRECATED = object()
40
40
41
41
42 class _NoStyle(Style): pass
42 class _NoStyle(Style): pass
43
43
44
44
45
45
46 _style_overrides_light_bg = {
46 _style_overrides_light_bg = {
47 Token.Prompt: '#0000ff',
47 Token.Prompt: '#0000ff',
48 Token.PromptNum: '#0000ee bold',
48 Token.PromptNum: '#0000ee bold',
49 Token.OutPrompt: '#cc0000',
49 Token.OutPrompt: '#cc0000',
50 Token.OutPromptNum: '#bb0000 bold',
50 Token.OutPromptNum: '#bb0000 bold',
51 }
51 }
52
52
53 _style_overrides_linux = {
53 _style_overrides_linux = {
54 Token.Prompt: '#00cc00',
54 Token.Prompt: '#00cc00',
55 Token.PromptNum: '#00bb00 bold',
55 Token.PromptNum: '#00bb00 bold',
56 Token.OutPrompt: '#cc0000',
56 Token.OutPrompt: '#cc0000',
57 Token.OutPromptNum: '#bb0000 bold',
57 Token.OutPromptNum: '#bb0000 bold',
58 }
58 }
59
59
60
60
61
61
62 def get_default_editor():
62 def get_default_editor():
63 try:
63 try:
64 return os.environ['EDITOR']
64 return os.environ['EDITOR']
65 except KeyError:
65 except KeyError:
66 pass
66 pass
67 except UnicodeError:
67 except UnicodeError:
68 warn("$EDITOR environment variable is not pure ASCII. Using platform "
68 warn("$EDITOR environment variable is not pure ASCII. Using platform "
69 "default editor.")
69 "default editor.")
70
70
71 if os.name == 'posix':
71 if os.name == 'posix':
72 return 'vi' # the only one guaranteed to be there!
72 return 'vi' # the only one guaranteed to be there!
73 else:
73 else:
74 return 'notepad' # same in Windows!
74 return 'notepad' # same in Windows!
75
75
76 # conservatively check for tty
76 # conservatively check for tty
77 # overridden streams can result in things like:
77 # overridden streams can result in things like:
78 # - sys.stdin = None
78 # - sys.stdin = None
79 # - no isatty method
79 # - no isatty method
80 for _name in ('stdin', 'stdout', 'stderr'):
80 for _name in ('stdin', 'stdout', 'stderr'):
81 _stream = getattr(sys, _name)
81 _stream = getattr(sys, _name)
82 if not _stream or not hasattr(_stream, 'isatty') or not _stream.isatty():
82 if not _stream or not hasattr(_stream, 'isatty') or not _stream.isatty():
83 _is_tty = False
83 _is_tty = False
84 break
84 break
85 else:
85 else:
86 _is_tty = True
86 _is_tty = True
87
87
88
88
89 _use_simple_prompt = ('IPY_TEST_SIMPLE_PROMPT' in os.environ) or (not _is_tty)
89 _use_simple_prompt = ('IPY_TEST_SIMPLE_PROMPT' in os.environ) or (not _is_tty)
90
90
91 class TerminalInteractiveShell(InteractiveShell):
91 class TerminalInteractiveShell(InteractiveShell):
92 space_for_menu = Integer(6, help='Number of line at the bottom of the screen '
92 space_for_menu = Integer(6, help='Number of line at the bottom of the screen '
93 'to reserve for the completion menu'
93 'to reserve for the completion menu'
94 ).tag(config=True)
94 ).tag(config=True)
95
95
96 def _space_for_menu_changed(self, old, new):
96 def _space_for_menu_changed(self, old, new):
97 self._update_layout()
97 self._update_layout()
98
98
99 pt_cli = None
99 pt_cli = None
100 debugger_history = None
100 debugger_history = None
101 _pt_app = None
101 _pt_app = None
102
102
103 simple_prompt = Bool(_use_simple_prompt,
103 simple_prompt = Bool(_use_simple_prompt,
104 help="""Use `raw_input` for the REPL, without completion, multiline input, and prompt colors.
104 help="""Use `raw_input` for the REPL, without completion, multiline input, and prompt colors.
105
105
106 Useful when controlling IPython as a subprocess, and piping STDIN/OUT/ERR. Known usage are:
106 Useful when controlling IPython as a subprocess, and piping STDIN/OUT/ERR. Known usage are:
107 IPython own testing machinery, and emacs inferior-shell integration through elpy.
107 IPython own testing machinery, and emacs inferior-shell integration through elpy.
108
108
109 This mode default to `True` if the `IPY_TEST_SIMPLE_PROMPT`
109 This mode default to `True` if the `IPY_TEST_SIMPLE_PROMPT`
110 environment variable is set, or the current terminal is not a tty."""
110 environment variable is set, or the current terminal is not a tty."""
111 ).tag(config=True)
111 ).tag(config=True)
112
112
113 @property
113 @property
114 def debugger_cls(self):
114 def debugger_cls(self):
115 return Pdb if self.simple_prompt else TerminalPdb
115 return Pdb if self.simple_prompt else TerminalPdb
116
116
117 confirm_exit = Bool(True,
117 confirm_exit = Bool(True,
118 help="""
118 help="""
119 Set to confirm when you try to exit IPython with an EOF (Control-D
119 Set to confirm when you try to exit IPython with an EOF (Control-D
120 in Unix, Control-Z/Enter in Windows). By typing 'exit' or 'quit',
120 in Unix, Control-Z/Enter in Windows). By typing 'exit' or 'quit',
121 you can force a direct exit without any confirmation.""",
121 you can force a direct exit without any confirmation.""",
122 ).tag(config=True)
122 ).tag(config=True)
123
123
124 editing_mode = Unicode('emacs',
124 editing_mode = Unicode('emacs',
125 help="Shortcut style to use at the prompt. 'vi' or 'emacs'.",
125 help="Shortcut style to use at the prompt. 'vi' or 'emacs'.",
126 ).tag(config=True)
126 ).tag(config=True)
127
127
128 mouse_support = Bool(False,
128 mouse_support = Bool(False,
129 help="Enable mouse support in the prompt"
129 help="Enable mouse support in the prompt"
130 ).tag(config=True)
130 ).tag(config=True)
131
131
132 highlighting_style = Union([Unicode('legacy'), Type(klass=Style)],
132 highlighting_style = Union([Unicode('legacy'), Type(klass=Style)],
133 help="""The name or class of a Pygments style to use for syntax
133 help="""The name or class of a Pygments style to use for syntax
134 highlighting: \n %s""" % ', '.join(get_all_styles())
134 highlighting: \n %s""" % ', '.join(get_all_styles())
135 ).tag(config=True)
135 ).tag(config=True)
136
136
137
137
138 @observe('highlighting_style')
138 @observe('highlighting_style')
139 @observe('colors')
139 @observe('colors')
140 def _highlighting_style_changed(self, change):
140 def _highlighting_style_changed(self, change):
141 self.refresh_style()
141 self.refresh_style()
142
142
143 def refresh_style(self):
143 def refresh_style(self):
144 self._style = self._make_style_from_name_or_cls(self.highlighting_style)
144 self._style = self._make_style_from_name_or_cls(self.highlighting_style)
145
145
146
146
147 highlighting_style_overrides = Dict(
147 highlighting_style_overrides = Dict(
148 help="Override highlighting format for specific tokens"
148 help="Override highlighting format for specific tokens"
149 ).tag(config=True)
149 ).tag(config=True)
150
150
151 true_color = Bool(False,
151 true_color = Bool(False,
152 help=("Use 24bit colors instead of 256 colors in prompt highlighting. "
152 help=("Use 24bit colors instead of 256 colors in prompt highlighting. "
153 "If your terminal supports true color, the following command "
153 "If your terminal supports true color, the following command "
154 "should print 'TRUECOLOR' in orange: "
154 "should print 'TRUECOLOR' in orange: "
155 "printf \"\\x1b[38;2;255;100;0mTRUECOLOR\\x1b[0m\\n\"")
155 "printf \"\\x1b[38;2;255;100;0mTRUECOLOR\\x1b[0m\\n\"")
156 ).tag(config=True)
156 ).tag(config=True)
157
157
158 editor = Unicode(get_default_editor(),
158 editor = Unicode(get_default_editor(),
159 help="Set the editor used by IPython (default to $EDITOR/vi/notepad)."
159 help="Set the editor used by IPython (default to $EDITOR/vi/notepad)."
160 ).tag(config=True)
160 ).tag(config=True)
161
161
162 prompts_class = Type(Prompts, help='Class used to generate Prompt token for prompt_toolkit').tag(config=True)
162 prompts_class = Type(Prompts, help='Class used to generate Prompt token for prompt_toolkit').tag(config=True)
163
163
164 prompts = Instance(Prompts)
164 prompts = Instance(Prompts)
165
165
166 @default('prompts')
166 @default('prompts')
167 def _prompts_default(self):
167 def _prompts_default(self):
168 return self.prompts_class(self)
168 return self.prompts_class(self)
169
169
170 @observe('prompts')
170 @observe('prompts')
171 def _(self, change):
171 def _(self, change):
172 self._update_layout()
172 self._update_layout()
173
173
174 @default('displayhook_class')
174 @default('displayhook_class')
175 def _displayhook_class_default(self):
175 def _displayhook_class_default(self):
176 return RichPromptDisplayHook
176 return RichPromptDisplayHook
177
177
178 term_title = Bool(True,
178 term_title = Bool(True,
179 help="Automatically set the terminal title"
179 help="Automatically set the terminal title"
180 ).tag(config=True)
180 ).tag(config=True)
181
181
182 term_title_format = Unicode("IPython: {cwd}",
183 help="Customize the terminal title format. This is a python format string. " +
184 "Available substitutions are: {cwd}."
185 ).tag(config=True)
186
182 display_completions = Enum(('column', 'multicolumn','readlinelike'),
187 display_completions = Enum(('column', 'multicolumn','readlinelike'),
183 help= ( "Options for displaying tab completions, 'column', 'multicolumn', and "
188 help= ( "Options for displaying tab completions, 'column', 'multicolumn', and "
184 "'readlinelike'. These options are for `prompt_toolkit`, see "
189 "'readlinelike'. These options are for `prompt_toolkit`, see "
185 "`prompt_toolkit` documentation for more information."
190 "`prompt_toolkit` documentation for more information."
186 ),
191 ),
187 default_value='multicolumn').tag(config=True)
192 default_value='multicolumn').tag(config=True)
188
193
189 highlight_matching_brackets = Bool(True,
194 highlight_matching_brackets = Bool(True,
190 help="Highlight matching brackets.",
195 help="Highlight matching brackets.",
191 ).tag(config=True)
196 ).tag(config=True)
192
197
193 extra_open_editor_shortcuts = Bool(False,
198 extra_open_editor_shortcuts = Bool(False,
194 help="Enable vi (v) or Emacs (C-X C-E) shortcuts to open an external editor. "
199 help="Enable vi (v) or Emacs (C-X C-E) shortcuts to open an external editor. "
195 "This is in addition to the F2 binding, which is always enabled."
200 "This is in addition to the F2 binding, which is always enabled."
196 ).tag(config=True)
201 ).tag(config=True)
197
202
198 handle_return = Any(None,
203 handle_return = Any(None,
199 help="Provide an alternative handler to be called when the user presses "
204 help="Provide an alternative handler to be called when the user presses "
200 "Return. This is an advanced option intended for debugging, which "
205 "Return. This is an advanced option intended for debugging, which "
201 "may be changed or removed in later releases."
206 "may be changed or removed in later releases."
202 ).tag(config=True)
207 ).tag(config=True)
203
208
204 @observe('term_title')
209 @observe('term_title')
205 def init_term_title(self, change=None):
210 def init_term_title(self, change=None):
206 # Enable or disable the terminal title.
211 # Enable or disable the terminal title.
207 if self.term_title:
212 if self.term_title:
208 toggle_set_term_title(True)
213 toggle_set_term_title(True)
209 set_term_title('IPython: ' + abbrev_cwd())
214 set_term_title(self.term_title_format.format(cwd=abbrev_cwd()))
210 else:
215 else:
211 toggle_set_term_title(False)
216 toggle_set_term_title(False)
212
217
213 def init_display_formatter(self):
218 def init_display_formatter(self):
214 super(TerminalInteractiveShell, self).init_display_formatter()
219 super(TerminalInteractiveShell, self).init_display_formatter()
215 # terminal only supports plain text
220 # terminal only supports plain text
216 self.display_formatter.active_types = ['text/plain']
221 self.display_formatter.active_types = ['text/plain']
217 # disable `_ipython_display_`
222 # disable `_ipython_display_`
218 self.display_formatter.ipython_display_formatter.enabled = False
223 self.display_formatter.ipython_display_formatter.enabled = False
219
224
220 def init_prompt_toolkit_cli(self):
225 def init_prompt_toolkit_cli(self):
221 if self.simple_prompt:
226 if self.simple_prompt:
222 # Fall back to plain non-interactive output for tests.
227 # Fall back to plain non-interactive output for tests.
223 # This is very limited, and only accepts a single line.
228 # This is very limited, and only accepts a single line.
224 def prompt():
229 def prompt():
225 return cast_unicode_py2(input('In [%d]: ' % self.execution_count))
230 return cast_unicode_py2(input('In [%d]: ' % self.execution_count))
226 self.prompt_for_code = prompt
231 self.prompt_for_code = prompt
227 return
232 return
228
233
229 # Set up keyboard shortcuts
234 # Set up keyboard shortcuts
230 kbmanager = KeyBindingManager.for_prompt(
235 kbmanager = KeyBindingManager.for_prompt(
231 enable_open_in_editor=self.extra_open_editor_shortcuts,
236 enable_open_in_editor=self.extra_open_editor_shortcuts,
232 )
237 )
233 register_ipython_shortcuts(kbmanager.registry, self)
238 register_ipython_shortcuts(kbmanager.registry, self)
234
239
235 # Pre-populate history from IPython's history database
240 # Pre-populate history from IPython's history database
236 history = InMemoryHistory()
241 history = InMemoryHistory()
237 last_cell = u""
242 last_cell = u""
238 for __, ___, cell in self.history_manager.get_tail(self.history_load_length,
243 for __, ___, cell in self.history_manager.get_tail(self.history_load_length,
239 include_latest=True):
244 include_latest=True):
240 # Ignore blank lines and consecutive duplicates
245 # Ignore blank lines and consecutive duplicates
241 cell = cell.rstrip()
246 cell = cell.rstrip()
242 if cell and (cell != last_cell):
247 if cell and (cell != last_cell):
243 history.append(cell)
248 history.append(cell)
244 last_cell = cell
249 last_cell = cell
245
250
246 self._style = self._make_style_from_name_or_cls(self.highlighting_style)
251 self._style = self._make_style_from_name_or_cls(self.highlighting_style)
247 self.style = DynamicStyle(lambda: self._style)
252 self.style = DynamicStyle(lambda: self._style)
248
253
249 editing_mode = getattr(EditingMode, self.editing_mode.upper())
254 editing_mode = getattr(EditingMode, self.editing_mode.upper())
250
255
251 def patch_stdout(**kwargs):
256 def patch_stdout(**kwargs):
252 return self.pt_cli.patch_stdout_context(**kwargs)
257 return self.pt_cli.patch_stdout_context(**kwargs)
253
258
254 self._pt_app = create_prompt_application(
259 self._pt_app = create_prompt_application(
255 editing_mode=editing_mode,
260 editing_mode=editing_mode,
256 key_bindings_registry=kbmanager.registry,
261 key_bindings_registry=kbmanager.registry,
257 history=history,
262 history=history,
258 completer=IPythonPTCompleter(shell=self,
263 completer=IPythonPTCompleter(shell=self,
259 patch_stdout=patch_stdout),
264 patch_stdout=patch_stdout),
260 enable_history_search=True,
265 enable_history_search=True,
261 style=self.style,
266 style=self.style,
262 mouse_support=self.mouse_support,
267 mouse_support=self.mouse_support,
263 **self._layout_options()
268 **self._layout_options()
264 )
269 )
265 self._eventloop = create_eventloop(self.inputhook)
270 self._eventloop = create_eventloop(self.inputhook)
266 self.pt_cli = CommandLineInterface(
271 self.pt_cli = CommandLineInterface(
267 self._pt_app, eventloop=self._eventloop,
272 self._pt_app, eventloop=self._eventloop,
268 output=create_output(true_color=self.true_color))
273 output=create_output(true_color=self.true_color))
269
274
270 def _make_style_from_name_or_cls(self, name_or_cls):
275 def _make_style_from_name_or_cls(self, name_or_cls):
271 """
276 """
272 Small wrapper that make an IPython compatible style from a style name
277 Small wrapper that make an IPython compatible style from a style name
273
278
274 We need that to add style for prompt ... etc.
279 We need that to add style for prompt ... etc.
275 """
280 """
276 style_overrides = {}
281 style_overrides = {}
277 if name_or_cls == 'legacy':
282 if name_or_cls == 'legacy':
278 legacy = self.colors.lower()
283 legacy = self.colors.lower()
279 if legacy == 'linux':
284 if legacy == 'linux':
280 style_cls = get_style_by_name('monokai')
285 style_cls = get_style_by_name('monokai')
281 style_overrides = _style_overrides_linux
286 style_overrides = _style_overrides_linux
282 elif legacy == 'lightbg':
287 elif legacy == 'lightbg':
283 style_overrides = _style_overrides_light_bg
288 style_overrides = _style_overrides_light_bg
284 style_cls = get_style_by_name('pastie')
289 style_cls = get_style_by_name('pastie')
285 elif legacy == 'neutral':
290 elif legacy == 'neutral':
286 # The default theme needs to be visible on both a dark background
291 # The default theme needs to be visible on both a dark background
287 # and a light background, because we can't tell what the terminal
292 # and a light background, because we can't tell what the terminal
288 # looks like. These tweaks to the default theme help with that.
293 # looks like. These tweaks to the default theme help with that.
289 style_cls = get_style_by_name('default')
294 style_cls = get_style_by_name('default')
290 style_overrides.update({
295 style_overrides.update({
291 Token.Number: '#007700',
296 Token.Number: '#007700',
292 Token.Operator: 'noinherit',
297 Token.Operator: 'noinherit',
293 Token.String: '#BB6622',
298 Token.String: '#BB6622',
294 Token.Name.Function: '#2080D0',
299 Token.Name.Function: '#2080D0',
295 Token.Name.Class: 'bold #2080D0',
300 Token.Name.Class: 'bold #2080D0',
296 Token.Name.Namespace: 'bold #2080D0',
301 Token.Name.Namespace: 'bold #2080D0',
297 Token.Prompt: '#009900',
302 Token.Prompt: '#009900',
298 Token.PromptNum: '#00ff00 bold',
303 Token.PromptNum: '#00ff00 bold',
299 Token.OutPrompt: '#990000',
304 Token.OutPrompt: '#990000',
300 Token.OutPromptNum: '#ff0000 bold',
305 Token.OutPromptNum: '#ff0000 bold',
301 })
306 })
302
307
303 # Hack: Due to limited color support on the Windows console
308 # Hack: Due to limited color support on the Windows console
304 # the prompt colors will be wrong without this
309 # the prompt colors will be wrong without this
305 if os.name == 'nt':
310 if os.name == 'nt':
306 style_overrides.update({
311 style_overrides.update({
307 Token.Prompt: '#ansidarkgreen',
312 Token.Prompt: '#ansidarkgreen',
308 Token.PromptNum: '#ansigreen bold',
313 Token.PromptNum: '#ansigreen bold',
309 Token.OutPrompt: '#ansidarkred',
314 Token.OutPrompt: '#ansidarkred',
310 Token.OutPromptNum: '#ansired bold',
315 Token.OutPromptNum: '#ansired bold',
311 })
316 })
312 elif legacy =='nocolor':
317 elif legacy =='nocolor':
313 style_cls=_NoStyle
318 style_cls=_NoStyle
314 style_overrides = {}
319 style_overrides = {}
315 else :
320 else :
316 raise ValueError('Got unknown colors: ', legacy)
321 raise ValueError('Got unknown colors: ', legacy)
317 else :
322 else :
318 if isinstance(name_or_cls, str):
323 if isinstance(name_or_cls, str):
319 style_cls = get_style_by_name(name_or_cls)
324 style_cls = get_style_by_name(name_or_cls)
320 else:
325 else:
321 style_cls = name_or_cls
326 style_cls = name_or_cls
322 style_overrides = {
327 style_overrides = {
323 Token.Prompt: '#009900',
328 Token.Prompt: '#009900',
324 Token.PromptNum: '#00ff00 bold',
329 Token.PromptNum: '#00ff00 bold',
325 Token.OutPrompt: '#990000',
330 Token.OutPrompt: '#990000',
326 Token.OutPromptNum: '#ff0000 bold',
331 Token.OutPromptNum: '#ff0000 bold',
327 }
332 }
328 style_overrides.update(self.highlighting_style_overrides)
333 style_overrides.update(self.highlighting_style_overrides)
329 style = PygmentsStyle.from_defaults(pygments_style_cls=style_cls,
334 style = PygmentsStyle.from_defaults(pygments_style_cls=style_cls,
330 style_dict=style_overrides)
335 style_dict=style_overrides)
331
336
332 return style
337 return style
333
338
334 def _layout_options(self):
339 def _layout_options(self):
335 """
340 """
336 Return the current layout option for the current Terminal InteractiveShell
341 Return the current layout option for the current Terminal InteractiveShell
337 """
342 """
338 return {
343 return {
339 'lexer':IPythonPTLexer(),
344 'lexer':IPythonPTLexer(),
340 'reserve_space_for_menu':self.space_for_menu,
345 'reserve_space_for_menu':self.space_for_menu,
341 'get_prompt_tokens':self.prompts.in_prompt_tokens,
346 'get_prompt_tokens':self.prompts.in_prompt_tokens,
342 'get_continuation_tokens':self.prompts.continuation_prompt_tokens,
347 'get_continuation_tokens':self.prompts.continuation_prompt_tokens,
343 'multiline':True,
348 'multiline':True,
344 'display_completions_in_columns': (self.display_completions == 'multicolumn'),
349 'display_completions_in_columns': (self.display_completions == 'multicolumn'),
345
350
346 # Highlight matching brackets, but only when this setting is
351 # Highlight matching brackets, but only when this setting is
347 # enabled, and only when the DEFAULT_BUFFER has the focus.
352 # enabled, and only when the DEFAULT_BUFFER has the focus.
348 'extra_input_processors': [ConditionalProcessor(
353 'extra_input_processors': [ConditionalProcessor(
349 processor=HighlightMatchingBracketProcessor(chars='[](){}'),
354 processor=HighlightMatchingBracketProcessor(chars='[](){}'),
350 filter=HasFocus(DEFAULT_BUFFER) & ~IsDone() &
355 filter=HasFocus(DEFAULT_BUFFER) & ~IsDone() &
351 Condition(lambda cli: self.highlight_matching_brackets))],
356 Condition(lambda cli: self.highlight_matching_brackets))],
352 }
357 }
353
358
354 def _update_layout(self):
359 def _update_layout(self):
355 """
360 """
356 Ask for a re computation of the application layout, if for example ,
361 Ask for a re computation of the application layout, if for example ,
357 some configuration options have changed.
362 some configuration options have changed.
358 """
363 """
359 if self._pt_app:
364 if self._pt_app:
360 self._pt_app.layout = create_prompt_layout(**self._layout_options())
365 self._pt_app.layout = create_prompt_layout(**self._layout_options())
361
366
362 def prompt_for_code(self):
367 def prompt_for_code(self):
363 document = self.pt_cli.run(
368 document = self.pt_cli.run(
364 pre_run=self.pre_prompt, reset_current_buffer=True)
369 pre_run=self.pre_prompt, reset_current_buffer=True)
365 return document.text
370 return document.text
366
371
367 def enable_win_unicode_console(self):
372 def enable_win_unicode_console(self):
368 if sys.version_info >= (3, 6):
373 if sys.version_info >= (3, 6):
369 # Since PEP 528, Python uses the unicode APIs for the Windows
374 # Since PEP 528, Python uses the unicode APIs for the Windows
370 # console by default, so WUC shouldn't be needed.
375 # console by default, so WUC shouldn't be needed.
371 return
376 return
372
377
373 import win_unicode_console
378 import win_unicode_console
374 win_unicode_console.enable()
379 win_unicode_console.enable()
375
380
376 def init_io(self):
381 def init_io(self):
377 if sys.platform not in {'win32', 'cli'}:
382 if sys.platform not in {'win32', 'cli'}:
378 return
383 return
379
384
380 self.enable_win_unicode_console()
385 self.enable_win_unicode_console()
381
386
382 import colorama
387 import colorama
383 colorama.init()
388 colorama.init()
384
389
385 # For some reason we make these wrappers around stdout/stderr.
390 # For some reason we make these wrappers around stdout/stderr.
386 # For now, we need to reset them so all output gets coloured.
391 # For now, we need to reset them so all output gets coloured.
387 # https://github.com/ipython/ipython/issues/8669
392 # https://github.com/ipython/ipython/issues/8669
388 # io.std* are deprecated, but don't show our own deprecation warnings
393 # io.std* are deprecated, but don't show our own deprecation warnings
389 # during initialization of the deprecated API.
394 # during initialization of the deprecated API.
390 with warnings.catch_warnings():
395 with warnings.catch_warnings():
391 warnings.simplefilter('ignore', DeprecationWarning)
396 warnings.simplefilter('ignore', DeprecationWarning)
392 io.stdout = io.IOStream(sys.stdout)
397 io.stdout = io.IOStream(sys.stdout)
393 io.stderr = io.IOStream(sys.stderr)
398 io.stderr = io.IOStream(sys.stderr)
394
399
395 def init_magics(self):
400 def init_magics(self):
396 super(TerminalInteractiveShell, self).init_magics()
401 super(TerminalInteractiveShell, self).init_magics()
397 self.register_magics(TerminalMagics)
402 self.register_magics(TerminalMagics)
398
403
399 def init_alias(self):
404 def init_alias(self):
400 # The parent class defines aliases that can be safely used with any
405 # The parent class defines aliases that can be safely used with any
401 # frontend.
406 # frontend.
402 super(TerminalInteractiveShell, self).init_alias()
407 super(TerminalInteractiveShell, self).init_alias()
403
408
404 # Now define aliases that only make sense on the terminal, because they
409 # Now define aliases that only make sense on the terminal, because they
405 # need direct access to the console in a way that we can't emulate in
410 # need direct access to the console in a way that we can't emulate in
406 # GUI or web frontend
411 # GUI or web frontend
407 if os.name == 'posix':
412 if os.name == 'posix':
408 for cmd in ['clear', 'more', 'less', 'man']:
413 for cmd in ['clear', 'more', 'less', 'man']:
409 self.alias_manager.soft_define_alias(cmd, cmd)
414 self.alias_manager.soft_define_alias(cmd, cmd)
410
415
411
416
412 def __init__(self, *args, **kwargs):
417 def __init__(self, *args, **kwargs):
413 super(TerminalInteractiveShell, self).__init__(*args, **kwargs)
418 super(TerminalInteractiveShell, self).__init__(*args, **kwargs)
414 self.init_prompt_toolkit_cli()
419 self.init_prompt_toolkit_cli()
415 self.init_term_title()
420 self.init_term_title()
416 self.keep_running = True
421 self.keep_running = True
417
422
418 self.debugger_history = InMemoryHistory()
423 self.debugger_history = InMemoryHistory()
419
424
420 def ask_exit(self):
425 def ask_exit(self):
421 self.keep_running = False
426 self.keep_running = False
422
427
423 rl_next_input = None
428 rl_next_input = None
424
429
425 def pre_prompt(self):
430 def pre_prompt(self):
426 if self.rl_next_input:
431 if self.rl_next_input:
427 # We can't set the buffer here, because it will be reset just after
432 # We can't set the buffer here, because it will be reset just after
428 # this. Adding a callable to pre_run_callables does what we need
433 # this. Adding a callable to pre_run_callables does what we need
429 # after the buffer is reset.
434 # after the buffer is reset.
430 s = cast_unicode_py2(self.rl_next_input)
435 s = cast_unicode_py2(self.rl_next_input)
431 def set_doc():
436 def set_doc():
432 self.pt_cli.application.buffer.document = Document(s)
437 self.pt_cli.application.buffer.document = Document(s)
433 if hasattr(self.pt_cli, 'pre_run_callables'):
438 if hasattr(self.pt_cli, 'pre_run_callables'):
434 self.pt_cli.pre_run_callables.append(set_doc)
439 self.pt_cli.pre_run_callables.append(set_doc)
435 else:
440 else:
436 # Older version of prompt_toolkit; it's OK to set the document
441 # Older version of prompt_toolkit; it's OK to set the document
437 # directly here.
442 # directly here.
438 set_doc()
443 set_doc()
439 self.rl_next_input = None
444 self.rl_next_input = None
440
445
441 def interact(self, display_banner=DISPLAY_BANNER_DEPRECATED):
446 def interact(self, display_banner=DISPLAY_BANNER_DEPRECATED):
442
447
443 if display_banner is not DISPLAY_BANNER_DEPRECATED:
448 if display_banner is not DISPLAY_BANNER_DEPRECATED:
444 warn('interact `display_banner` argument is deprecated since IPython 5.0. Call `show_banner()` if needed.', DeprecationWarning, stacklevel=2)
449 warn('interact `display_banner` argument is deprecated since IPython 5.0. Call `show_banner()` if needed.', DeprecationWarning, stacklevel=2)
445
450
446 self.keep_running = True
451 self.keep_running = True
447 while self.keep_running:
452 while self.keep_running:
448 print(self.separate_in, end='')
453 print(self.separate_in, end='')
449
454
450 try:
455 try:
451 code = self.prompt_for_code()
456 code = self.prompt_for_code()
452 except EOFError:
457 except EOFError:
453 if (not self.confirm_exit) \
458 if (not self.confirm_exit) \
454 or self.ask_yes_no('Do you really want to exit ([y]/n)?','y','n'):
459 or self.ask_yes_no('Do you really want to exit ([y]/n)?','y','n'):
455 self.ask_exit()
460 self.ask_exit()
456
461
457 else:
462 else:
458 if code:
463 if code:
459 self.run_cell(code, store_history=True)
464 self.run_cell(code, store_history=True)
460
465
461 def mainloop(self, display_banner=DISPLAY_BANNER_DEPRECATED):
466 def mainloop(self, display_banner=DISPLAY_BANNER_DEPRECATED):
462 # An extra layer of protection in case someone mashing Ctrl-C breaks
467 # An extra layer of protection in case someone mashing Ctrl-C breaks
463 # out of our internal code.
468 # out of our internal code.
464 if display_banner is not DISPLAY_BANNER_DEPRECATED:
469 if display_banner is not DISPLAY_BANNER_DEPRECATED:
465 warn('mainloop `display_banner` argument is deprecated since IPython 5.0. Call `show_banner()` if needed.', DeprecationWarning, stacklevel=2)
470 warn('mainloop `display_banner` argument is deprecated since IPython 5.0. Call `show_banner()` if needed.', DeprecationWarning, stacklevel=2)
466 while True:
471 while True:
467 try:
472 try:
468 self.interact()
473 self.interact()
469 break
474 break
470 except KeyboardInterrupt as e:
475 except KeyboardInterrupt as e:
471 print("\n%s escaped interact()\n" % type(e).__name__)
476 print("\n%s escaped interact()\n" % type(e).__name__)
472 finally:
477 finally:
473 # An interrupt during the eventloop will mess up the
478 # An interrupt during the eventloop will mess up the
474 # internal state of the prompt_toolkit library.
479 # internal state of the prompt_toolkit library.
475 # Stopping the eventloop fixes this, see
480 # Stopping the eventloop fixes this, see
476 # https://github.com/ipython/ipython/pull/9867
481 # https://github.com/ipython/ipython/pull/9867
477 if hasattr(self, '_eventloop'):
482 if hasattr(self, '_eventloop'):
478 self._eventloop.stop()
483 self._eventloop.stop()
479
484
480 _inputhook = None
485 _inputhook = None
481 def inputhook(self, context):
486 def inputhook(self, context):
482 if self._inputhook is not None:
487 if self._inputhook is not None:
483 self._inputhook(context)
488 self._inputhook(context)
484
489
485 active_eventloop = None
490 active_eventloop = None
486 def enable_gui(self, gui=None):
491 def enable_gui(self, gui=None):
487 if gui:
492 if gui:
488 self.active_eventloop, self._inputhook =\
493 self.active_eventloop, self._inputhook =\
489 get_inputhook_name_and_func(gui)
494 get_inputhook_name_and_func(gui)
490 else:
495 else:
491 self.active_eventloop = self._inputhook = None
496 self.active_eventloop = self._inputhook = None
492
497
493 # Run !system commands directly, not through pipes, so terminal programs
498 # Run !system commands directly, not through pipes, so terminal programs
494 # work correctly.
499 # work correctly.
495 system = InteractiveShell.system_raw
500 system = InteractiveShell.system_raw
496
501
497 def auto_rewrite_input(self, cmd):
502 def auto_rewrite_input(self, cmd):
498 """Overridden from the parent class to use fancy rewriting prompt"""
503 """Overridden from the parent class to use fancy rewriting prompt"""
499 if not self.show_rewritten_input:
504 if not self.show_rewritten_input:
500 return
505 return
501
506
502 tokens = self.prompts.rewrite_prompt_tokens()
507 tokens = self.prompts.rewrite_prompt_tokens()
503 if self.pt_cli:
508 if self.pt_cli:
504 self.pt_cli.print_tokens(tokens)
509 self.pt_cli.print_tokens(tokens)
505 print(cmd)
510 print(cmd)
506 else:
511 else:
507 prompt = ''.join(s for t, s in tokens)
512 prompt = ''.join(s for t, s in tokens)
508 print(prompt, cmd, sep='')
513 print(prompt, cmd, sep='')
509
514
510 _prompts_before = None
515 _prompts_before = None
511 def switch_doctest_mode(self, mode):
516 def switch_doctest_mode(self, mode):
512 """Switch prompts to classic for %doctest_mode"""
517 """Switch prompts to classic for %doctest_mode"""
513 if mode:
518 if mode:
514 self._prompts_before = self.prompts
519 self._prompts_before = self.prompts
515 self.prompts = ClassicPrompts(self)
520 self.prompts = ClassicPrompts(self)
516 elif self._prompts_before:
521 elif self._prompts_before:
517 self.prompts = self._prompts_before
522 self.prompts = self._prompts_before
518 self._prompts_before = None
523 self._prompts_before = None
519 self._update_layout()
524 self._update_layout()
520
525
521
526
522 InteractiveShellABC.register(TerminalInteractiveShell)
527 InteractiveShellABC.register(TerminalInteractiveShell)
523
528
524 if __name__ == '__main__':
529 if __name__ == '__main__':
525 TerminalInteractiveShell.instance().interact()
530 TerminalInteractiveShell.instance().interact()
General Comments 0
You need to be logged in to leave comments. Login now