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