##// END OF EJS Templates
Merge pull request #4297 from abhinav-upadhyay/unreachable-code...
Min RK -
r12827:7c6ccedb merge
parent child Browse files
Show More
@@ -1,647 +1,646 b''
1 1 """Implementation of basic magic functions.
2 2 """
3 3 #-----------------------------------------------------------------------------
4 4 # Copyright (c) 2012 The IPython Development Team.
5 5 #
6 6 # Distributed under the terms of the Modified BSD License.
7 7 #
8 8 # The full license is in the file COPYING.txt, distributed with this software.
9 9 #-----------------------------------------------------------------------------
10 10
11 11 #-----------------------------------------------------------------------------
12 12 # Imports
13 13 #-----------------------------------------------------------------------------
14 14 from __future__ import print_function
15 15
16 16 # Stdlib
17 17 import io
18 18 import json
19 19 import sys
20 20 from pprint import pformat
21 21
22 22 # Our own packages
23 23 from IPython.core import magic_arguments, page
24 24 from IPython.core.error import UsageError
25 25 from IPython.core.magic import Magics, magics_class, line_magic, magic_escapes
26 26 from IPython.utils.text import format_screen, dedent, indent
27 27 from IPython.testing.skipdoctest import skip_doctest
28 28 from IPython.utils.ipstruct import Struct
29 29 from IPython.utils.path import unquote_filename
30 30 from IPython.utils.warn import warn, error
31 31
32 32 #-----------------------------------------------------------------------------
33 33 # Magics class implementation
34 34 #-----------------------------------------------------------------------------
35 35
36 36 class MagicsDisplay(object):
37 37 def __init__(self, magics_manager):
38 38 self.magics_manager = magics_manager
39 39
40 40 def _lsmagic(self):
41 41 """The main implementation of the %lsmagic"""
42 42 mesc = magic_escapes['line']
43 43 cesc = magic_escapes['cell']
44 44 mman = self.magics_manager
45 45 magics = mman.lsmagic()
46 46 out = ['Available line magics:',
47 47 mesc + (' '+mesc).join(sorted(magics['line'])),
48 48 '',
49 49 'Available cell magics:',
50 50 cesc + (' '+cesc).join(sorted(magics['cell'])),
51 51 '',
52 52 mman.auto_status()]
53 53 return '\n'.join(out)
54 54
55 55 def _repr_pretty_(self, p, cycle):
56 56 p.text(self._lsmagic())
57 57
58 58 def __str__(self):
59 59 return self._lsmagic()
60 60
61 61 def _jsonable(self):
62 62 """turn magics dict into jsonable dict of the same structure
63 63
64 64 replaces object instances with their class names as strings
65 65 """
66 66 magic_dict = {}
67 67 mman = self.magics_manager
68 68 magics = mman.lsmagic()
69 69 for key, subdict in magics.items():
70 70 d = {}
71 71 magic_dict[key] = d
72 72 for name, obj in subdict.items():
73 73 try:
74 74 classname = obj.im_class.__name__
75 75 except AttributeError:
76 76 classname = 'Other'
77 77
78 78 d[name] = classname
79 79 return magic_dict
80 80
81 81 def _repr_json_(self):
82 82 return json.dumps(self._jsonable())
83 83
84 84
85 85 @magics_class
86 86 class BasicMagics(Magics):
87 87 """Magics that provide central IPython functionality.
88 88
89 89 These are various magics that don't fit into specific categories but that
90 90 are all part of the base 'IPython experience'."""
91 91
92 92 @magic_arguments.magic_arguments()
93 93 @magic_arguments.argument(
94 94 '-l', '--line', action='store_true',
95 95 help="""Create a line magic alias."""
96 96 )
97 97 @magic_arguments.argument(
98 98 '-c', '--cell', action='store_true',
99 99 help="""Create a cell magic alias."""
100 100 )
101 101 @magic_arguments.argument(
102 102 'name',
103 103 help="""Name of the magic to be created."""
104 104 )
105 105 @magic_arguments.argument(
106 106 'target',
107 107 help="""Name of the existing line or cell magic."""
108 108 )
109 109 @line_magic
110 110 def alias_magic(self, line=''):
111 111 """Create an alias for an existing line or cell magic.
112 112
113 113 Examples
114 114 --------
115 115 ::
116 116 In [1]: %alias_magic t timeit
117 117 Created `%t` as an alias for `%timeit`.
118 118 Created `%%t` as an alias for `%%timeit`.
119 119
120 120 In [2]: %t -n1 pass
121 121 1 loops, best of 3: 954 ns per loop
122 122
123 123 In [3]: %%t -n1
124 124 ...: pass
125 125 ...:
126 126 1 loops, best of 3: 954 ns per loop
127 127
128 128 In [4]: %alias_magic --cell whereami pwd
129 129 UsageError: Cell magic function `%%pwd` not found.
130 130 In [5]: %alias_magic --line whereami pwd
131 131 Created `%whereami` as an alias for `%pwd`.
132 132
133 133 In [6]: %whereami
134 134 Out[6]: u'/home/testuser'
135 135 """
136 136 args = magic_arguments.parse_argstring(self.alias_magic, line)
137 137 shell = self.shell
138 138 mman = self.shell.magics_manager
139 139 escs = ''.join(magic_escapes.values())
140 140
141 141 target = args.target.lstrip(escs)
142 142 name = args.name.lstrip(escs)
143 143
144 144 # Find the requested magics.
145 145 m_line = shell.find_magic(target, 'line')
146 146 m_cell = shell.find_magic(target, 'cell')
147 147 if args.line and m_line is None:
148 148 raise UsageError('Line magic function `%s%s` not found.' %
149 149 (magic_escapes['line'], target))
150 150 if args.cell and m_cell is None:
151 151 raise UsageError('Cell magic function `%s%s` not found.' %
152 152 (magic_escapes['cell'], target))
153 153
154 154 # If --line and --cell are not specified, default to the ones
155 155 # that are available.
156 156 if not args.line and not args.cell:
157 157 if not m_line and not m_cell:
158 158 raise UsageError(
159 159 'No line or cell magic with name `%s` found.' % target
160 160 )
161 161 args.line = bool(m_line)
162 162 args.cell = bool(m_cell)
163 163
164 164 if args.line:
165 165 mman.register_alias(name, target, 'line')
166 166 print('Created `%s%s` as an alias for `%s%s`.' % (
167 167 magic_escapes['line'], name,
168 168 magic_escapes['line'], target))
169 169
170 170 if args.cell:
171 171 mman.register_alias(name, target, 'cell')
172 172 print('Created `%s%s` as an alias for `%s%s`.' % (
173 173 magic_escapes['cell'], name,
174 174 magic_escapes['cell'], target))
175 175
176 176 @line_magic
177 177 def lsmagic(self, parameter_s=''):
178 178 """List currently available magic functions."""
179 179 return MagicsDisplay(self.shell.magics_manager)
180 180
181 181 def _magic_docs(self, brief=False, rest=False):
182 182 """Return docstrings from magic functions."""
183 183 mman = self.shell.magics_manager
184 184 docs = mman.lsmagic_docs(brief, missing='No documentation')
185 185
186 186 if rest:
187 187 format_string = '**%s%s**::\n\n%s\n\n'
188 188 else:
189 189 format_string = '%s%s:\n%s\n'
190 190
191 191 return ''.join(
192 192 [format_string % (magic_escapes['line'], fname,
193 193 indent(dedent(fndoc)))
194 194 for fname, fndoc in sorted(docs['line'].items())]
195 195 +
196 196 [format_string % (magic_escapes['cell'], fname,
197 197 indent(dedent(fndoc)))
198 198 for fname, fndoc in sorted(docs['cell'].items())]
199 199 )
200 200
201 201 @line_magic
202 202 def magic(self, parameter_s=''):
203 203 """Print information about the magic function system.
204 204
205 205 Supported formats: -latex, -brief, -rest
206 206 """
207 207
208 208 mode = ''
209 209 try:
210 210 mode = parameter_s.split()[0][1:]
211 211 if mode == 'rest':
212 212 rest_docs = []
213 213 except IndexError:
214 214 pass
215 215
216 216 brief = (mode == 'brief')
217 217 rest = (mode == 'rest')
218 218 magic_docs = self._magic_docs(brief, rest)
219 219
220 220 if mode == 'latex':
221 221 print(self.format_latex(magic_docs))
222 222 return
223 223 else:
224 224 magic_docs = format_screen(magic_docs)
225 225
226 226 out = ["""
227 227 IPython's 'magic' functions
228 228 ===========================
229 229
230 230 The magic function system provides a series of functions which allow you to
231 231 control the behavior of IPython itself, plus a lot of system-type
232 232 features. There are two kinds of magics, line-oriented and cell-oriented.
233 233
234 234 Line magics are prefixed with the % character and work much like OS
235 235 command-line calls: they get as an argument the rest of the line, where
236 236 arguments are passed without parentheses or quotes. For example, this will
237 237 time the given statement::
238 238
239 239 %timeit range(1000)
240 240
241 241 Cell magics are prefixed with a double %%, and they are functions that get as
242 242 an argument not only the rest of the line, but also the lines below it in a
243 243 separate argument. These magics are called with two arguments: the rest of the
244 244 call line and the body of the cell, consisting of the lines below the first.
245 245 For example::
246 246
247 247 %%timeit x = numpy.random.randn((100, 100))
248 248 numpy.linalg.svd(x)
249 249
250 250 will time the execution of the numpy svd routine, running the assignment of x
251 251 as part of the setup phase, which is not timed.
252 252
253 253 In a line-oriented client (the terminal or Qt console IPython), starting a new
254 254 input with %% will automatically enter cell mode, and IPython will continue
255 255 reading input until a blank line is given. In the notebook, simply type the
256 256 whole cell as one entity, but keep in mind that the %% escape can only be at
257 257 the very start of the cell.
258 258
259 259 NOTE: If you have 'automagic' enabled (via the command line option or with the
260 260 %automagic function), you don't need to type in the % explicitly for line
261 261 magics; cell magics always require an explicit '%%' escape. By default,
262 262 IPython ships with automagic on, so you should only rarely need the % escape.
263 263
264 264 Example: typing '%cd mydir' (without the quotes) changes you working directory
265 265 to 'mydir', if it exists.
266 266
267 267 For a list of the available magic functions, use %lsmagic. For a description
268 268 of any of them, type %magic_name?, e.g. '%cd?'.
269 269
270 270 Currently the magic system has the following functions:""",
271 271 magic_docs,
272 272 "Summary of magic functions (from %slsmagic):" % magic_escapes['line'],
273 273 str(self.lsmagic()),
274 274 ]
275 275 page.page('\n'.join(out))
276 276
277 277
278 278 @line_magic
279 279 def page(self, parameter_s=''):
280 280 """Pretty print the object and display it through a pager.
281 281
282 282 %page [options] OBJECT
283 283
284 284 If no object is given, use _ (last output).
285 285
286 286 Options:
287 287
288 288 -r: page str(object), don't pretty-print it."""
289 289
290 290 # After a function contributed by Olivier Aubert, slightly modified.
291 291
292 292 # Process options/args
293 293 opts, args = self.parse_options(parameter_s, 'r')
294 294 raw = 'r' in opts
295 295
296 296 oname = args and args or '_'
297 297 info = self.shell._ofind(oname)
298 298 if info['found']:
299 299 txt = (raw and str or pformat)( info['obj'] )
300 300 page.page(txt)
301 301 else:
302 302 print('Object `%s` not found' % oname)
303 303
304 304 @line_magic
305 305 def profile(self, parameter_s=''):
306 306 """Print your currently active IPython profile."""
307 307 from IPython.core.application import BaseIPythonApplication
308 308 if BaseIPythonApplication.initialized():
309 309 print(BaseIPythonApplication.instance().profile)
310 310 else:
311 311 error("profile is an application-level value, but you don't appear to be in an IPython application")
312 312
313 313 @line_magic
314 314 def pprint(self, parameter_s=''):
315 315 """Toggle pretty printing on/off."""
316 316 ptformatter = self.shell.display_formatter.formatters['text/plain']
317 317 ptformatter.pprint = bool(1 - ptformatter.pprint)
318 318 print('Pretty printing has been turned',
319 319 ['OFF','ON'][ptformatter.pprint])
320 320
321 321 @line_magic
322 322 def colors(self, parameter_s=''):
323 323 """Switch color scheme for prompts, info system and exception handlers.
324 324
325 325 Currently implemented schemes: NoColor, Linux, LightBG.
326 326
327 327 Color scheme names are not case-sensitive.
328 328
329 329 Examples
330 330 --------
331 331 To get a plain black and white terminal::
332 332
333 333 %colors nocolor
334 334 """
335 335 def color_switch_err(name):
336 336 warn('Error changing %s color schemes.\n%s' %
337 337 (name, sys.exc_info()[1]))
338 338
339 339
340 340 new_scheme = parameter_s.strip()
341 341 if not new_scheme:
342 342 raise UsageError(
343 343 "%colors: you must specify a color scheme. See '%colors?'")
344 return
345 344 # local shortcut
346 345 shell = self.shell
347 346
348 347 import IPython.utils.rlineimpl as readline
349 348
350 349 if not shell.colors_force and \
351 350 not readline.have_readline and \
352 351 (sys.platform == "win32" or sys.platform == "cli"):
353 352 msg = """\
354 353 Proper color support under MS Windows requires the pyreadline library.
355 354 You can find it at:
356 355 http://ipython.org/pyreadline.html
357 356 Gary's readline needs the ctypes module, from:
358 357 http://starship.python.net/crew/theller/ctypes
359 358 (Note that ctypes is already part of Python versions 2.5 and newer).
360 359
361 360 Defaulting color scheme to 'NoColor'"""
362 361 new_scheme = 'NoColor'
363 362 warn(msg)
364 363
365 364 # readline option is 0
366 365 if not shell.colors_force and not shell.has_readline:
367 366 new_scheme = 'NoColor'
368 367
369 368 # Set prompt colors
370 369 try:
371 370 shell.prompt_manager.color_scheme = new_scheme
372 371 except:
373 372 color_switch_err('prompt')
374 373 else:
375 374 shell.colors = \
376 375 shell.prompt_manager.color_scheme_table.active_scheme_name
377 376 # Set exception colors
378 377 try:
379 378 shell.InteractiveTB.set_colors(scheme = new_scheme)
380 379 shell.SyntaxTB.set_colors(scheme = new_scheme)
381 380 except:
382 381 color_switch_err('exception')
383 382
384 383 # Set info (for 'object?') colors
385 384 if shell.color_info:
386 385 try:
387 386 shell.inspector.set_active_scheme(new_scheme)
388 387 except:
389 388 color_switch_err('object inspector')
390 389 else:
391 390 shell.inspector.set_active_scheme('NoColor')
392 391
393 392 @line_magic
394 393 def xmode(self, parameter_s=''):
395 394 """Switch modes for the exception handlers.
396 395
397 396 Valid modes: Plain, Context and Verbose.
398 397
399 398 If called without arguments, acts as a toggle."""
400 399
401 400 def xmode_switch_err(name):
402 401 warn('Error changing %s exception modes.\n%s' %
403 402 (name,sys.exc_info()[1]))
404 403
405 404 shell = self.shell
406 405 new_mode = parameter_s.strip().capitalize()
407 406 try:
408 407 shell.InteractiveTB.set_mode(mode=new_mode)
409 408 print('Exception reporting mode:',shell.InteractiveTB.mode)
410 409 except:
411 410 xmode_switch_err('user')
412 411
413 412 @line_magic
414 413 def quickref(self,arg):
415 414 """ Show a quick reference sheet """
416 415 from IPython.core.usage import quick_reference
417 416 qr = quick_reference + self._magic_docs(brief=True)
418 417 page.page(qr)
419 418
420 419 @line_magic
421 420 def doctest_mode(self, parameter_s=''):
422 421 """Toggle doctest mode on and off.
423 422
424 423 This mode is intended to make IPython behave as much as possible like a
425 424 plain Python shell, from the perspective of how its prompts, exceptions
426 425 and output look. This makes it easy to copy and paste parts of a
427 426 session into doctests. It does so by:
428 427
429 428 - Changing the prompts to the classic ``>>>`` ones.
430 429 - Changing the exception reporting mode to 'Plain'.
431 430 - Disabling pretty-printing of output.
432 431
433 432 Note that IPython also supports the pasting of code snippets that have
434 433 leading '>>>' and '...' prompts in them. This means that you can paste
435 434 doctests from files or docstrings (even if they have leading
436 435 whitespace), and the code will execute correctly. You can then use
437 436 '%history -t' to see the translated history; this will give you the
438 437 input after removal of all the leading prompts and whitespace, which
439 438 can be pasted back into an editor.
440 439
441 440 With these features, you can switch into this mode easily whenever you
442 441 need to do testing and changes to doctests, without having to leave
443 442 your existing IPython session.
444 443 """
445 444
446 445 # Shorthands
447 446 shell = self.shell
448 447 pm = shell.prompt_manager
449 448 meta = shell.meta
450 449 disp_formatter = self.shell.display_formatter
451 450 ptformatter = disp_formatter.formatters['text/plain']
452 451 # dstore is a data store kept in the instance metadata bag to track any
453 452 # changes we make, so we can undo them later.
454 453 dstore = meta.setdefault('doctest_mode',Struct())
455 454 save_dstore = dstore.setdefault
456 455
457 456 # save a few values we'll need to recover later
458 457 mode = save_dstore('mode',False)
459 458 save_dstore('rc_pprint',ptformatter.pprint)
460 459 save_dstore('xmode',shell.InteractiveTB.mode)
461 460 save_dstore('rc_separate_out',shell.separate_out)
462 461 save_dstore('rc_separate_out2',shell.separate_out2)
463 462 save_dstore('rc_prompts_pad_left',pm.justify)
464 463 save_dstore('rc_separate_in',shell.separate_in)
465 464 save_dstore('rc_active_types',disp_formatter.active_types)
466 465 save_dstore('prompt_templates',(pm.in_template, pm.in2_template, pm.out_template))
467 466
468 467 if mode == False:
469 468 # turn on
470 469 pm.in_template = '>>> '
471 470 pm.in2_template = '... '
472 471 pm.out_template = ''
473 472
474 473 # Prompt separators like plain python
475 474 shell.separate_in = ''
476 475 shell.separate_out = ''
477 476 shell.separate_out2 = ''
478 477
479 478 pm.justify = False
480 479
481 480 ptformatter.pprint = False
482 481 disp_formatter.active_types = ['text/plain']
483 482
484 483 shell.magic('xmode Plain')
485 484 else:
486 485 # turn off
487 486 pm.in_template, pm.in2_template, pm.out_template = dstore.prompt_templates
488 487
489 488 shell.separate_in = dstore.rc_separate_in
490 489
491 490 shell.separate_out = dstore.rc_separate_out
492 491 shell.separate_out2 = dstore.rc_separate_out2
493 492
494 493 pm.justify = dstore.rc_prompts_pad_left
495 494
496 495 ptformatter.pprint = dstore.rc_pprint
497 496 disp_formatter.active_types = dstore.rc_active_types
498 497
499 498 shell.magic('xmode ' + dstore.xmode)
500 499
501 500 # Store new mode and inform
502 501 dstore.mode = bool(1-int(mode))
503 502 mode_label = ['OFF','ON'][dstore.mode]
504 503 print('Doctest mode is:', mode_label)
505 504
506 505 @line_magic
507 506 def gui(self, parameter_s=''):
508 507 """Enable or disable IPython GUI event loop integration.
509 508
510 509 %gui [GUINAME]
511 510
512 511 This magic replaces IPython's threaded shells that were activated
513 512 using the (pylab/wthread/etc.) command line flags. GUI toolkits
514 513 can now be enabled at runtime and keyboard
515 514 interrupts should work without any problems. The following toolkits
516 515 are supported: wxPython, PyQt4, PyGTK, Tk and Cocoa (OSX)::
517 516
518 517 %gui wx # enable wxPython event loop integration
519 518 %gui qt4|qt # enable PyQt4 event loop integration
520 519 %gui gtk # enable PyGTK event loop integration
521 520 %gui gtk3 # enable Gtk3 event loop integration
522 521 %gui tk # enable Tk event loop integration
523 522 %gui osx # enable Cocoa event loop integration
524 523 # (requires %matplotlib 1.1)
525 524 %gui # disable all event loop integration
526 525
527 526 WARNING: after any of these has been called you can simply create
528 527 an application object, but DO NOT start the event loop yourself, as
529 528 we have already handled that.
530 529 """
531 530 opts, arg = self.parse_options(parameter_s, '')
532 531 if arg=='': arg = None
533 532 try:
534 533 return self.shell.enable_gui(arg)
535 534 except Exception as e:
536 535 # print simple error message, rather than traceback if we can't
537 536 # hook up the GUI
538 537 error(str(e))
539 538
540 539 @skip_doctest
541 540 @line_magic
542 541 def precision(self, s=''):
543 542 """Set floating point precision for pretty printing.
544 543
545 544 Can set either integer precision or a format string.
546 545
547 546 If numpy has been imported and precision is an int,
548 547 numpy display precision will also be set, via ``numpy.set_printoptions``.
549 548
550 549 If no argument is given, defaults will be restored.
551 550
552 551 Examples
553 552 --------
554 553 ::
555 554
556 555 In [1]: from math import pi
557 556
558 557 In [2]: %precision 3
559 558 Out[2]: u'%.3f'
560 559
561 560 In [3]: pi
562 561 Out[3]: 3.142
563 562
564 563 In [4]: %precision %i
565 564 Out[4]: u'%i'
566 565
567 566 In [5]: pi
568 567 Out[5]: 3
569 568
570 569 In [6]: %precision %e
571 570 Out[6]: u'%e'
572 571
573 572 In [7]: pi**10
574 573 Out[7]: 9.364805e+04
575 574
576 575 In [8]: %precision
577 576 Out[8]: u'%r'
578 577
579 578 In [9]: pi**10
580 579 Out[9]: 93648.047476082982
581 580 """
582 581 ptformatter = self.shell.display_formatter.formatters['text/plain']
583 582 ptformatter.float_precision = s
584 583 return ptformatter.float_format
585 584
586 585 @magic_arguments.magic_arguments()
587 586 @magic_arguments.argument(
588 587 '-e', '--export', action='store_true', default=False,
589 588 help='Export IPython history as a notebook. The filename argument '
590 589 'is used to specify the notebook name and format. For example '
591 590 'a filename of notebook.ipynb will result in a notebook name '
592 591 'of "notebook" and a format of "json". Likewise using a ".py" '
593 592 'file extension will write the notebook as a Python script'
594 593 )
595 594 @magic_arguments.argument(
596 595 '-f', '--format',
597 596 help='Convert an existing IPython notebook to a new format. This option '
598 597 'specifies the new format and can have the values: json, py. '
599 598 'The target filename is chosen automatically based on the new '
600 599 'format. The filename argument gives the name of the source file.'
601 600 )
602 601 @magic_arguments.argument(
603 602 'filename', type=unicode,
604 603 help='Notebook name or filename'
605 604 )
606 605 @line_magic
607 606 def notebook(self, s):
608 607 """Export and convert IPython notebooks.
609 608
610 609 This function can export the current IPython history to a notebook file
611 610 or can convert an existing notebook file into a different format. For
612 611 example, to export the history to "foo.ipynb" do "%notebook -e foo.ipynb".
613 612 To export the history to "foo.py" do "%notebook -e foo.py". To convert
614 613 "foo.ipynb" to "foo.json" do "%notebook -f json foo.ipynb". Possible
615 614 formats include (json/ipynb, py).
616 615 """
617 616 args = magic_arguments.parse_argstring(self.notebook, s)
618 617
619 618 from IPython.nbformat import current
620 619 args.filename = unquote_filename(args.filename)
621 620 if args.export:
622 621 fname, name, format = current.parse_filename(args.filename)
623 622 cells = []
624 623 hist = list(self.shell.history_manager.get_range())
625 624 for session, prompt_number, input in hist[:-1]:
626 625 cells.append(current.new_code_cell(prompt_number=prompt_number,
627 626 input=input))
628 627 worksheet = current.new_worksheet(cells=cells)
629 628 nb = current.new_notebook(name=name,worksheets=[worksheet])
630 629 with io.open(fname, 'w', encoding='utf-8') as f:
631 630 current.write(nb, f, format);
632 631 elif args.format is not None:
633 632 old_fname, old_name, old_format = current.parse_filename(args.filename)
634 633 new_format = args.format
635 634 if new_format == u'xml':
636 635 raise ValueError('Notebooks cannot be written as xml.')
637 636 elif new_format == u'ipynb' or new_format == u'json':
638 637 new_fname = old_name + u'.ipynb'
639 638 new_format = u'json'
640 639 elif new_format == u'py':
641 640 new_fname = old_name + u'.py'
642 641 else:
643 642 raise ValueError('Invalid notebook format: %s' % new_format)
644 643 with io.open(old_fname, 'r', encoding='utf-8') as f:
645 644 nb = current.read(f, old_format)
646 645 with io.open(new_fname, 'w', encoding='utf-8') as f:
647 646 current.write(nb, f, new_format)
General Comments 0
You need to be logged in to leave comments. Login now