io.py
239 lines
| 7.5 KiB
| text/x-python
|
PythonLexer
Brian Granger
|
r2498 | # encoding: utf-8 | ||
""" | ||||
IO related utilities. | ||||
""" | ||||
Min RK
|
r21111 | # Copyright (c) IPython Development Team. | ||
# Distributed under the terms of the Modified BSD License. | ||||
Brian Granger
|
r2498 | |||
Min RK
|
r21111 | |||
John Kirkham
|
r21762 | import atexit | ||
Brandon Parsons
|
r6653 | import os | ||
Brian Granger
|
r2498 | import sys | ||
import tempfile | ||||
Min RK
|
r22742 | import warnings | ||
Matthias Bussonnier
|
r21298 | from warnings import warn | ||
Thomas Kluyver
|
r22192 | |||
from IPython.utils.decorators import undoc | ||||
MinRK
|
r12223 | from .capture import CapturedIO, capture_output | ||
Thomas Kluyver
|
r13690 | from .py3compat import string_types, input, PY3 | ||
Brian Granger
|
r2498 | |||
Thomas Kluyver
|
r22192 | @undoc | ||
Brian Granger
|
r2498 | class IOStream: | ||
Matthias Bussonnier
|
r22408 | def __init__(self, stream, fallback=None): | ||
warn('IOStream is deprecated since IPython 5.0, use sys.{stdin,stdout,stderr} instead', | ||||
DeprecationWarning, stacklevel=2) | ||||
Brian Granger
|
r2498 | if not hasattr(stream,'write') or not hasattr(stream,'flush'): | ||
MinRK
|
r3800 | if fallback is not None: | ||
stream = fallback | ||||
else: | ||||
raise ValueError("fallback required, but not specified") | ||||
Brian Granger
|
r2498 | self.stream = stream | ||
self._swrite = stream.write | ||||
Bernardo B. Marques
|
r4872 | |||
MinRK
|
r3800 | # clone all methods not overridden: | ||
def clone(meth): | ||||
return not hasattr(self, meth) and not meth.startswith('_') | ||||
for meth in filter(clone, dir(stream)): | ||||
setattr(self, meth, getattr(stream, meth)) | ||||
Bernardo B. Marques
|
r4872 | |||
Thomas Kluyver
|
r13528 | def __repr__(self): | ||
cls = self.__class__ | ||||
tpl = '{mod}.{cls}({args})' | ||||
return tpl.format(mod=cls.__module__, cls=cls.__name__, args=self.stream) | ||||
Brian Granger
|
r2498 | def write(self,data): | ||
Matthias Bussonnier
|
r22408 | warn('IOStream is deprecated since IPython 5.0, use sys.{stdin,stdout,stderr} instead', | ||
Thomas Kluyver
|
r22192 | DeprecationWarning, stacklevel=2) | ||
Brian Granger
|
r2498 | try: | ||
self._swrite(data) | ||||
except: | ||||
try: | ||||
# print handles some unicode issues which may trip a plain | ||||
Fernando Perez
|
r2856 | # write() call. Emulate write() by using an empty end | ||
# argument. | ||||
print(data, end='', file=self.stream) | ||||
Brian Granger
|
r2498 | except: | ||
# if we get here, something is seriously broken. | ||||
Fernando Perez
|
r2856 | print('ERROR - failed to write data to stream:', self.stream, | ||
file=sys.stderr) | ||||
Bernardo B. Marques
|
r4872 | |||
MinRK
|
r3800 | def writelines(self, lines): | ||
Matthias Bussonnier
|
r22408 | warn('IOStream is deprecated since IPython 5.0, use sys.{stdin,stdout,stderr} instead', | ||
Thomas Kluyver
|
r22192 | DeprecationWarning, stacklevel=2) | ||
Thomas Kluyver
|
r13353 | if isinstance(lines, string_types): | ||
MinRK
|
r3800 | lines = [lines] | ||
for line in lines: | ||||
self.write(line) | ||||
Brian Granger
|
r2498 | |||
Brian Granger
|
r2504 | # This class used to have a writeln method, but regular files and streams | ||
# in Python don't have this method. We need to keep this completely | ||||
# compatible so we removed it. | ||||
MinRK
|
r3800 | @property | ||
def closed(self): | ||||
return self.stream.closed | ||||
Bernardo B. Marques
|
r4872 | |||
Brian Granger
|
r2498 | def close(self): | ||
pass | ||||
Brandon Parsons
|
r6652 | # setup stdin/stdout/stderr to sys.stdin/sys.stdout/sys.stderr | ||
Min RK
|
r22742 | devnull = open(os.devnull, 'w') | ||
John Kirkham
|
r21762 | atexit.register(devnull.close) | ||
Min RK
|
r22742 | |||
# io.std* are deprecated, but don't show our own deprecation warnings | ||||
# during initialization of the deprecated API. | ||||
with warnings.catch_warnings(): | ||||
warnings.simplefilter('ignore', DeprecationWarning) | ||||
stdin = IOStream(sys.stdin, fallback=devnull) | ||||
stdout = IOStream(sys.stdout, fallback=devnull) | ||||
stderr = IOStream(sys.stderr, fallback=devnull) | ||||
Brian Granger
|
r2498 | |||
class Tee(object): | ||||
"""A class to duplicate an output stream to stdout/err. | ||||
This works in a manner very similar to the Unix 'tee' command. | ||||
When the object is closed or deleted, it closes the original file given to | ||||
it for duplication. | ||||
""" | ||||
# Inspired by: | ||||
# http://mail.python.org/pipermail/python-list/2007-May/442737.html | ||||
Thomas Kluyver
|
r4763 | def __init__(self, file_or_name, mode="w", channel='stdout'): | ||
Brian Granger
|
r2498 | """Construct a new Tee object. | ||
Parameters | ||||
---------- | ||||
file_or_name : filename or open filehandle (writable) | ||||
File that will be duplicated | ||||
mode : optional, valid mode for open(). | ||||
If a filename was give, open with this mode. | ||||
Bernardo B. Marques
|
r4872 | channel : str, one of ['stdout', 'stderr'] | ||
Brian Granger
|
r2498 | """ | ||
if channel not in ['stdout', 'stderr']: | ||||
raise ValueError('Invalid channel spec %s' % channel) | ||||
Bernardo B. Marques
|
r4872 | |||
Thomas Kluyver
|
r4763 | if hasattr(file_or_name, 'write') and hasattr(file_or_name, 'seek'): | ||
Brian Granger
|
r2498 | self.file = file_or_name | ||
else: | ||||
self.file = open(file_or_name, mode) | ||||
self.channel = channel | ||||
self.ostream = getattr(sys, channel) | ||||
setattr(sys, channel, self) | ||||
self._closed = False | ||||
def close(self): | ||||
"""Close the file and restore the channel.""" | ||||
self.flush() | ||||
setattr(sys, self.channel, self.ostream) | ||||
self.file.close() | ||||
self._closed = True | ||||
def write(self, data): | ||||
"""Write data to both channels.""" | ||||
self.file.write(data) | ||||
self.ostream.write(data) | ||||
self.ostream.flush() | ||||
def flush(self): | ||||
"""Flush both channels.""" | ||||
self.file.flush() | ||||
self.ostream.flush() | ||||
def __del__(self): | ||||
if not self._closed: | ||||
self.close() | ||||
Paul Ivanov
|
r13609 | def ask_yes_no(prompt, default=None, interrupt=None): | ||
Brian Granger
|
r2498 | """Asks a question and returns a boolean (y/n) answer. | ||
If default is given (one of 'y','n'), it is used if the user input is | ||||
Paul Ivanov
|
r13609 | empty. If interrupt is given (one of 'y','n'), it is used if the user | ||
presses Ctrl-C. Otherwise the question is repeated until an answer is | ||||
given. | ||||
Brian Granger
|
r2498 | |||
An EOF is treated as the default answer. If there is no default, an | ||||
exception is raised to prevent infinite loops. | ||||
Valid answers are: y/yes/n/no (match is not case sensitive).""" | ||||
answers = {'y':True,'n':False,'yes':True,'no':False} | ||||
ans = None | ||||
while ans not in answers.keys(): | ||||
try: | ||||
Thomas Kluyver
|
r13355 | ans = input(prompt+' ').lower() | ||
Brian Granger
|
r2498 | if not ans: # response was an empty string | ||
ans = default | ||||
except KeyboardInterrupt: | ||||
Paul Ivanov
|
r13609 | if interrupt: | ||
ans = interrupt | ||||
Brian Granger
|
r2498 | except EOFError: | ||
if default in answers.keys(): | ||||
ans = default | ||||
Thomas Spura
|
r4430 | print() | ||
Brian Granger
|
r2498 | else: | ||
raise | ||||
return answers[ans] | ||||
def temp_pyfile(src, ext='.py'): | ||||
"""Make a temporary python file, return filename and filehandle. | ||||
Parameters | ||||
---------- | ||||
src : string or list of strings (no need for ending newlines if list) | ||||
Source code to be written to the file. | ||||
ext : optional, string | ||||
Extension for the generated file. | ||||
Returns | ||||
------- | ||||
(filename, open filehandle) | ||||
It is the caller's responsibility to close the open file and unlink it. | ||||
""" | ||||
fname = tempfile.mkstemp(ext)[1] | ||||
f = open(fname,'w') | ||||
f.write(src) | ||||
f.flush() | ||||
return fname, f | ||||
Min RK
|
r21113 | def atomic_writing(*args, **kwargs): | ||
Thomas Kluyver
|
r21360 | """DEPRECATED: moved to notebook.services.contents.fileio""" | ||
warn("IPython.utils.io.atomic_writing has moved to notebook.services.contents.fileio") | ||||
from notebook.services.contents.fileio import atomic_writing | ||||
Min RK
|
r21113 | return atomic_writing(*args, **kwargs) | ||
Brian Granger
|
r2498 | |||
Fernando Perez
|
r2874 | def raw_print(*args, **kw): | ||
"""Raw print to sys.__stdout__, otherwise identical interface to print().""" | ||||
Bernardo B. Marques
|
r4872 | |||
Fernando Perez
|
r2856 | print(*args, sep=kw.get('sep', ' '), end=kw.get('end', '\n'), | ||
file=sys.__stdout__) | ||||
sys.__stdout__.flush() | ||||
Fernando Perez
|
r2874 | def raw_print_err(*args, **kw): | ||
"""Raw print to sys.__stderr__, otherwise identical interface to print().""" | ||||
Bernardo B. Marques
|
r4872 | |||
Fernando Perez
|
r2856 | print(*args, sep=kw.get('sep', ' '), end=kw.get('end', '\n'), | ||
file=sys.__stderr__) | ||||
Fernando Perez
|
r2838 | sys.__stderr__.flush() | ||
Fernando Perez
|
r2874 | |||
# Short aliases for quick debugging, do NOT use these in production code. | ||||
rprint = raw_print | ||||
rprinte = raw_print_err | ||||
Thomas Kluyver
|
r13690 | |||
Min RK
|
r21111 | def unicode_std_stream(stream='stdout'): | ||
Min RK
|
r21339 | """DEPRECATED, moved to nbconvert.utils.io""" | ||
warn("IPython.utils.io.unicode_std_stream has moved to nbconvert.utils.io") | ||||
from nbconvert.utils.io import unicode_std_stream | ||||
Min RK
|
r21111 | return unicode_std_stream(stream) | ||