##// END OF EJS Templates
Implement `IPython.utils.io.Tee.isatty` (#14460)...
M Bussonnier -
r28796:c098f4c5 merge
parent child Browse files
Show More
@@ -1,151 +1,153 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 """
2 """
3 IO related utilities.
3 IO related utilities.
4 """
4 """
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
9
10
10
11 import atexit
11 import atexit
12 import os
12 import os
13 import sys
13 import sys
14 import tempfile
14 import tempfile
15 from pathlib import Path
15 from pathlib import Path
16 from warnings import warn
16 from warnings import warn
17
17
18 from IPython.utils.decorators import undoc
18 from IPython.utils.decorators import undoc
19 from .capture import CapturedIO, capture_output
19 from .capture import CapturedIO, capture_output
20
20
21 class Tee(object):
21 class Tee(object):
22 """A class to duplicate an output stream to stdout/err.
22 """A class to duplicate an output stream to stdout/err.
23
23
24 This works in a manner very similar to the Unix 'tee' command.
24 This works in a manner very similar to the Unix 'tee' command.
25
25
26 When the object is closed or deleted, it closes the original file given to
26 When the object is closed or deleted, it closes the original file given to
27 it for duplication.
27 it for duplication.
28 """
28 """
29 # Inspired by:
29 # Inspired by:
30 # http://mail.python.org/pipermail/python-list/2007-May/442737.html
30 # http://mail.python.org/pipermail/python-list/2007-May/442737.html
31
31
32 def __init__(self, file_or_name, mode="w", channel='stdout'):
32 def __init__(self, file_or_name, mode="w", channel='stdout'):
33 """Construct a new Tee object.
33 """Construct a new Tee object.
34
34
35 Parameters
35 Parameters
36 ----------
36 ----------
37 file_or_name : filename or open filehandle (writable)
37 file_or_name : filename or open filehandle (writable)
38 File that will be duplicated
38 File that will be duplicated
39 mode : optional, valid mode for open().
39 mode : optional, valid mode for open().
40 If a filename was give, open with this mode.
40 If a filename was give, open with this mode.
41 channel : str, one of ['stdout', 'stderr']
41 channel : str, one of ['stdout', 'stderr']
42 """
42 """
43 if channel not in ['stdout', 'stderr']:
43 if channel not in ['stdout', 'stderr']:
44 raise ValueError('Invalid channel spec %s' % channel)
44 raise ValueError('Invalid channel spec %s' % channel)
45
45
46 if hasattr(file_or_name, 'write') and hasattr(file_or_name, 'seek'):
46 if hasattr(file_or_name, 'write') and hasattr(file_or_name, 'seek'):
47 self.file = file_or_name
47 self.file = file_or_name
48 else:
48 else:
49 encoding = None if "b" in mode else "utf-8"
49 encoding = None if "b" in mode else "utf-8"
50 self.file = open(file_or_name, mode, encoding=encoding)
50 self.file = open(file_or_name, mode, encoding=encoding)
51 self.channel = channel
51 self.channel = channel
52 self.ostream = getattr(sys, channel)
52 self.ostream = getattr(sys, channel)
53 setattr(sys, channel, self)
53 setattr(sys, channel, self)
54 self._closed = False
54 self._closed = False
55
55
56 def close(self):
56 def close(self):
57 """Close the file and restore the channel."""
57 """Close the file and restore the channel."""
58 self.flush()
58 self.flush()
59 setattr(sys, self.channel, self.ostream)
59 setattr(sys, self.channel, self.ostream)
60 self.file.close()
60 self.file.close()
61 self._closed = True
61 self._closed = True
62
62
63 def write(self, data):
63 def write(self, data):
64 """Write data to both channels."""
64 """Write data to both channels."""
65 self.file.write(data)
65 self.file.write(data)
66 self.ostream.write(data)
66 self.ostream.write(data)
67 self.ostream.flush()
67 self.ostream.flush()
68
68
69 def flush(self):
69 def flush(self):
70 """Flush both channels."""
70 """Flush both channels."""
71 self.file.flush()
71 self.file.flush()
72 self.ostream.flush()
72 self.ostream.flush()
73
73
74 def __del__(self):
74 def __del__(self):
75 if not self._closed:
75 if not self._closed:
76 self.close()
76 self.close()
77
77
78 def isatty(self):
79 return False
78
80
79 def ask_yes_no(prompt, default=None, interrupt=None):
81 def ask_yes_no(prompt, default=None, interrupt=None):
80 """Asks a question and returns a boolean (y/n) answer.
82 """Asks a question and returns a boolean (y/n) answer.
81
83
82 If default is given (one of 'y','n'), it is used if the user input is
84 If default is given (one of 'y','n'), it is used if the user input is
83 empty. If interrupt is given (one of 'y','n'), it is used if the user
85 empty. If interrupt is given (one of 'y','n'), it is used if the user
84 presses Ctrl-C. Otherwise the question is repeated until an answer is
86 presses Ctrl-C. Otherwise the question is repeated until an answer is
85 given.
87 given.
86
88
87 An EOF is treated as the default answer. If there is no default, an
89 An EOF is treated as the default answer. If there is no default, an
88 exception is raised to prevent infinite loops.
90 exception is raised to prevent infinite loops.
89
91
90 Valid answers are: y/yes/n/no (match is not case sensitive)."""
92 Valid answers are: y/yes/n/no (match is not case sensitive)."""
91
93
92 answers = {'y':True,'n':False,'yes':True,'no':False}
94 answers = {'y':True,'n':False,'yes':True,'no':False}
93 ans = None
95 ans = None
94 while ans not in answers.keys():
96 while ans not in answers.keys():
95 try:
97 try:
96 ans = input(prompt+' ').lower()
98 ans = input(prompt+' ').lower()
97 if not ans: # response was an empty string
99 if not ans: # response was an empty string
98 ans = default
100 ans = default
99 except KeyboardInterrupt:
101 except KeyboardInterrupt:
100 if interrupt:
102 if interrupt:
101 ans = interrupt
103 ans = interrupt
102 print("\r")
104 print("\r")
103 except EOFError:
105 except EOFError:
104 if default in answers.keys():
106 if default in answers.keys():
105 ans = default
107 ans = default
106 print()
108 print()
107 else:
109 else:
108 raise
110 raise
109
111
110 return answers[ans]
112 return answers[ans]
111
113
112
114
113 def temp_pyfile(src, ext='.py'):
115 def temp_pyfile(src, ext='.py'):
114 """Make a temporary python file, return filename and filehandle.
116 """Make a temporary python file, return filename and filehandle.
115
117
116 Parameters
118 Parameters
117 ----------
119 ----------
118 src : string or list of strings (no need for ending newlines if list)
120 src : string or list of strings (no need for ending newlines if list)
119 Source code to be written to the file.
121 Source code to be written to the file.
120 ext : optional, string
122 ext : optional, string
121 Extension for the generated file.
123 Extension for the generated file.
122
124
123 Returns
125 Returns
124 -------
126 -------
125 (filename, open filehandle)
127 (filename, open filehandle)
126 It is the caller's responsibility to close the open file and unlink it.
128 It is the caller's responsibility to close the open file and unlink it.
127 """
129 """
128 fname = tempfile.mkstemp(ext)[1]
130 fname = tempfile.mkstemp(ext)[1]
129 with open(Path(fname), "w", encoding="utf-8") as f:
131 with open(Path(fname), "w", encoding="utf-8") as f:
130 f.write(src)
132 f.write(src)
131 f.flush()
133 f.flush()
132 return fname
134 return fname
133
135
134
136
135 @undoc
137 @undoc
136 def raw_print(*args, **kw):
138 def raw_print(*args, **kw):
137 """DEPRECATED: Raw print to sys.__stdout__, otherwise identical interface to print()."""
139 """DEPRECATED: Raw print to sys.__stdout__, otherwise identical interface to print()."""
138 warn("IPython.utils.io.raw_print has been deprecated since IPython 7.0", DeprecationWarning, stacklevel=2)
140 warn("IPython.utils.io.raw_print has been deprecated since IPython 7.0", DeprecationWarning, stacklevel=2)
139
141
140 print(*args, sep=kw.get('sep', ' '), end=kw.get('end', '\n'),
142 print(*args, sep=kw.get('sep', ' '), end=kw.get('end', '\n'),
141 file=sys.__stdout__)
143 file=sys.__stdout__)
142 sys.__stdout__.flush()
144 sys.__stdout__.flush()
143
145
144 @undoc
146 @undoc
145 def raw_print_err(*args, **kw):
147 def raw_print_err(*args, **kw):
146 """DEPRECATED: Raw print to sys.__stderr__, otherwise identical interface to print()."""
148 """DEPRECATED: Raw print to sys.__stderr__, otherwise identical interface to print()."""
147 warn("IPython.utils.io.raw_print_err has been deprecated since IPython 7.0", DeprecationWarning, stacklevel=2)
149 warn("IPython.utils.io.raw_print_err has been deprecated since IPython 7.0", DeprecationWarning, stacklevel=2)
148
150
149 print(*args, sep=kw.get('sep', ' '), end=kw.get('end', '\n'),
151 print(*args, sep=kw.get('sep', ' '), end=kw.get('end', '\n'),
150 file=sys.__stderr__)
152 file=sys.__stderr__)
151 sys.__stderr__.flush()
153 sys.__stderr__.flush()
General Comments 0
You need to be logged in to leave comments. Login now