io.py 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. # encoding: utf-8
  2. """
  3. IO related utilities.
  4. """
  5. # Copyright (c) IPython Development Team.
  6. # Distributed under the terms of the Modified BSD License.
  7. from __future__ import print_function
  8. from __future__ import absolute_import
  9. import atexit
  10. import os
  11. import sys
  12. import tempfile
  13. import warnings
  14. from warnings import warn
  15. from IPython.utils.decorators import undoc
  16. from .capture import CapturedIO, capture_output
  17. from .py3compat import string_types, input, PY3
  18. @undoc
  19. class IOStream:
  20. def __init__(self, stream, fallback=None):
  21. warn('IOStream is deprecated since IPython 5.0, use sys.{stdin,stdout,stderr} instead',
  22. DeprecationWarning, stacklevel=2)
  23. if not hasattr(stream,'write') or not hasattr(stream,'flush'):
  24. if fallback is not None:
  25. stream = fallback
  26. else:
  27. raise ValueError("fallback required, but not specified")
  28. self.stream = stream
  29. self._swrite = stream.write
  30. # clone all methods not overridden:
  31. def clone(meth):
  32. return not hasattr(self, meth) and not meth.startswith('_')
  33. for meth in filter(clone, dir(stream)):
  34. try:
  35. val = getattr(stream, meth)
  36. except AttributeError:
  37. pass
  38. else:
  39. setattr(self, meth, val)
  40. def __repr__(self):
  41. cls = self.__class__
  42. tpl = '{mod}.{cls}({args})'
  43. return tpl.format(mod=cls.__module__, cls=cls.__name__, args=self.stream)
  44. def write(self,data):
  45. warn('IOStream is deprecated since IPython 5.0, use sys.{stdin,stdout,stderr} instead',
  46. DeprecationWarning, stacklevel=2)
  47. try:
  48. self._swrite(data)
  49. except:
  50. try:
  51. # print handles some unicode issues which may trip a plain
  52. # write() call. Emulate write() by using an empty end
  53. # argument.
  54. print(data, end='', file=self.stream)
  55. except:
  56. # if we get here, something is seriously broken.
  57. print('ERROR - failed to write data to stream:', self.stream,
  58. file=sys.stderr)
  59. def writelines(self, lines):
  60. warn('IOStream is deprecated since IPython 5.0, use sys.{stdin,stdout,stderr} instead',
  61. DeprecationWarning, stacklevel=2)
  62. if isinstance(lines, string_types):
  63. lines = [lines]
  64. for line in lines:
  65. self.write(line)
  66. # This class used to have a writeln method, but regular files and streams
  67. # in Python don't have this method. We need to keep this completely
  68. # compatible so we removed it.
  69. @property
  70. def closed(self):
  71. return self.stream.closed
  72. def close(self):
  73. pass
  74. # setup stdin/stdout/stderr to sys.stdin/sys.stdout/sys.stderr
  75. devnull = open(os.devnull, 'w')
  76. atexit.register(devnull.close)
  77. # io.std* are deprecated, but don't show our own deprecation warnings
  78. # during initialization of the deprecated API.
  79. with warnings.catch_warnings():
  80. warnings.simplefilter('ignore', DeprecationWarning)
  81. stdin = IOStream(sys.stdin, fallback=devnull)
  82. stdout = IOStream(sys.stdout, fallback=devnull)
  83. stderr = IOStream(sys.stderr, fallback=devnull)
  84. class Tee(object):
  85. """A class to duplicate an output stream to stdout/err.
  86. This works in a manner very similar to the Unix 'tee' command.
  87. When the object is closed or deleted, it closes the original file given to
  88. it for duplication.
  89. """
  90. # Inspired by:
  91. # http://mail.python.org/pipermail/python-list/2007-May/442737.html
  92. def __init__(self, file_or_name, mode="w", channel='stdout'):
  93. """Construct a new Tee object.
  94. Parameters
  95. ----------
  96. file_or_name : filename or open filehandle (writable)
  97. File that will be duplicated
  98. mode : optional, valid mode for open().
  99. If a filename was give, open with this mode.
  100. channel : str, one of ['stdout', 'stderr']
  101. """
  102. if channel not in ['stdout', 'stderr']:
  103. raise ValueError('Invalid channel spec %s' % channel)
  104. if hasattr(file_or_name, 'write') and hasattr(file_or_name, 'seek'):
  105. self.file = file_or_name
  106. else:
  107. self.file = open(file_or_name, mode)
  108. self.channel = channel
  109. self.ostream = getattr(sys, channel)
  110. setattr(sys, channel, self)
  111. self._closed = False
  112. def close(self):
  113. """Close the file and restore the channel."""
  114. self.flush()
  115. setattr(sys, self.channel, self.ostream)
  116. self.file.close()
  117. self._closed = True
  118. def write(self, data):
  119. """Write data to both channels."""
  120. self.file.write(data)
  121. self.ostream.write(data)
  122. self.ostream.flush()
  123. def flush(self):
  124. """Flush both channels."""
  125. self.file.flush()
  126. self.ostream.flush()
  127. def __del__(self):
  128. if not self._closed:
  129. self.close()
  130. def ask_yes_no(prompt, default=None, interrupt=None):
  131. """Asks a question and returns a boolean (y/n) answer.
  132. If default is given (one of 'y','n'), it is used if the user input is
  133. empty. If interrupt is given (one of 'y','n'), it is used if the user
  134. presses Ctrl-C. Otherwise the question is repeated until an answer is
  135. given.
  136. An EOF is treated as the default answer. If there is no default, an
  137. exception is raised to prevent infinite loops.
  138. Valid answers are: y/yes/n/no (match is not case sensitive)."""
  139. answers = {'y':True,'n':False,'yes':True,'no':False}
  140. ans = None
  141. while ans not in answers.keys():
  142. try:
  143. ans = input(prompt+' ').lower()
  144. if not ans: # response was an empty string
  145. ans = default
  146. except KeyboardInterrupt:
  147. if interrupt:
  148. ans = interrupt
  149. except EOFError:
  150. if default in answers.keys():
  151. ans = default
  152. print()
  153. else:
  154. raise
  155. return answers[ans]
  156. def temp_pyfile(src, ext='.py'):
  157. """Make a temporary python file, return filename and filehandle.
  158. Parameters
  159. ----------
  160. src : string or list of strings (no need for ending newlines if list)
  161. Source code to be written to the file.
  162. ext : optional, string
  163. Extension for the generated file.
  164. Returns
  165. -------
  166. (filename, open filehandle)
  167. It is the caller's responsibility to close the open file and unlink it.
  168. """
  169. fname = tempfile.mkstemp(ext)[1]
  170. f = open(fname,'w')
  171. f.write(src)
  172. f.flush()
  173. return fname, f
  174. def atomic_writing(*args, **kwargs):
  175. """DEPRECATED: moved to notebook.services.contents.fileio"""
  176. warn("IPython.utils.io.atomic_writing has moved to notebook.services.contents.fileio")
  177. from notebook.services.contents.fileio import atomic_writing
  178. return atomic_writing(*args, **kwargs)
  179. def raw_print(*args, **kw):
  180. """Raw print to sys.__stdout__, otherwise identical interface to print()."""
  181. print(*args, sep=kw.get('sep', ' '), end=kw.get('end', '\n'),
  182. file=sys.__stdout__)
  183. sys.__stdout__.flush()
  184. def raw_print_err(*args, **kw):
  185. """Raw print to sys.__stderr__, otherwise identical interface to print()."""
  186. print(*args, sep=kw.get('sep', ' '), end=kw.get('end', '\n'),
  187. file=sys.__stderr__)
  188. sys.__stderr__.flush()
  189. # Short aliases for quick debugging, do NOT use these in production code.
  190. rprint = raw_print
  191. rprinte = raw_print_err
  192. def unicode_std_stream(stream='stdout'):
  193. """DEPRECATED, moved to nbconvert.utils.io"""
  194. warn("IPython.utils.io.unicode_std_stream has moved to nbconvert.utils.io")
  195. from nbconvert.utils.io import unicode_std_stream
  196. return unicode_std_stream(stream)