Merge lp:~vila/bzr/376582-auth-prompt into lp:~bzr/bzr/trunk-old
- 376582-auth-prompt
- Merge into trunk-old
Proposed by
Vincent Ladeuil
Status: | Merged | ||||
---|---|---|---|---|---|
Approved by: | Ian Clatworthy | ||||
Approved revision: | no longer in the source branch. | ||||
Merged at revision: | not available | ||||
Proposed branch: | lp:~vila/bzr/376582-auth-prompt | ||||
Merge into: | lp:~bzr/bzr/trunk-old | ||||
Diff against target: | 277 lines | ||||
To merge this branch: | bzr merge lp:~vila/bzr/376582-auth-prompt | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Ian Clatworthy | Approve | ||
Review via email: mp+6609@code.launchpad.net |
Commit message
Description of the change
To post a comment you must log in.
Revision history for this message
Vincent Ladeuil (vila) wrote : | # |
Revision history for this message
Ian Clatworthy (ian-clatworthy) wrote : | # |
I suspect this change ought to be mentioned as a Compatibility break, not just a bug fix, in NEWS. When landing, make sure you tweak NEWS to put this in 1.16 (not 1.15) as well. :-)
review:
Approve
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'NEWS' | |||
2 | --- NEWS 2009-06-02 01:37:30 +0000 | |||
3 | +++ NEWS 2009-06-02 14:35:20 +0000 | |||
4 | @@ -12,6 +12,11 @@ | |||
5 | 12 | Compatibility Breaks | 12 | Compatibility Breaks |
6 | 13 | ******************** | 13 | ******************** |
7 | 14 | 14 | ||
8 | 15 | * Display prompt on stderr (instead of stdout) when querying users so | ||
9 | 16 | that the output of commands can be safely redirected. | ||
10 | 17 | (Vincent Ladeuil, #376582) | ||
11 | 18 | |||
12 | 19 | |||
13 | 15 | New Features | 20 | New Features |
14 | 16 | ************ | 21 | ************ |
15 | 17 | 22 | ||
16 | 18 | 23 | ||
17 | === modified file 'bzrlib/plugins/launchpad/test_register.py' | |||
18 | --- bzrlib/plugins/launchpad/test_register.py 2009-03-23 14:59:43 +0000 | |||
19 | +++ bzrlib/plugins/launchpad/test_register.py 2009-06-02 14:35:20 +0000 | |||
20 | @@ -335,12 +335,14 @@ | |||
21 | 335 | g_conf = config.GlobalConfig() | 335 | g_conf = config.GlobalConfig() |
22 | 336 | g_conf.set_user_option('email', 'Test User <test@user.com>') | 336 | g_conf.set_user_option('email', 'Test User <test@user.com>') |
23 | 337 | stdout = tests.StringIOWrapper() | 337 | stdout = tests.StringIOWrapper() |
24 | 338 | stderr = tests.StringIOWrapper() | ||
25 | 338 | ui.ui_factory = tests.TestUIFactory(stdin='userpass\n', | 339 | ui.ui_factory = tests.TestUIFactory(stdin='userpass\n', |
27 | 339 | stdout=stdout) | 340 | stdout=stdout, stderr=stderr) |
28 | 340 | self.assertIs(None, service.registrant_password) | 341 | self.assertIs(None, service.registrant_password) |
29 | 341 | service.gather_user_credentials() | 342 | service.gather_user_credentials() |
30 | 342 | self.assertEqual('test@user.com', service.registrant_email) | 343 | self.assertEqual('test@user.com', service.registrant_email) |
31 | 343 | self.assertEqual('userpass', service.registrant_password) | 344 | self.assertEqual('userpass', service.registrant_password) |
33 | 344 | self.assertContainsRe(stdout.getvalue(), | 345 | self.assertEquals('', stdout.getvalue()) |
34 | 346 | self.assertContainsRe(stderr.getvalue(), | ||
35 | 345 | 'launchpad.net password for test@user\\.com') | 347 | 'launchpad.net password for test@user\\.com') |
36 | 346 | 348 | ||
37 | 347 | 349 | ||
38 | === modified file 'bzrlib/tests/test_config.py' | |||
39 | --- bzrlib/tests/test_config.py 2009-04-27 16:10:10 +0000 | |||
40 | +++ bzrlib/tests/test_config.py 2009-06-02 14:35:20 +0000 | |||
41 | @@ -1461,7 +1461,8 @@ | |||
42 | 1461 | """Test AuthenticationConfig behaviour""" | 1461 | """Test AuthenticationConfig behaviour""" |
43 | 1462 | 1462 | ||
44 | 1463 | def _check_default_password_prompt(self, expected_prompt_format, scheme, | 1463 | def _check_default_password_prompt(self, expected_prompt_format, scheme, |
46 | 1464 | host=None, port=None, realm=None, path=None): | 1464 | host=None, port=None, realm=None, |
47 | 1465 | path=None): | ||
48 | 1465 | if host is None: | 1466 | if host is None: |
49 | 1466 | host = 'bar.org' | 1467 | host = 'bar.org' |
50 | 1467 | user, password = 'jim', 'precious' | 1468 | user, password = 'jim', 'precious' |
51 | @@ -1470,17 +1471,20 @@ | |||
52 | 1470 | 'user': user, 'realm': realm} | 1471 | 'user': user, 'realm': realm} |
53 | 1471 | 1472 | ||
54 | 1472 | stdout = tests.StringIOWrapper() | 1473 | stdout = tests.StringIOWrapper() |
55 | 1474 | stderr = tests.StringIOWrapper() | ||
56 | 1473 | ui.ui_factory = tests.TestUIFactory(stdin=password + '\n', | 1475 | ui.ui_factory = tests.TestUIFactory(stdin=password + '\n', |
58 | 1474 | stdout=stdout) | 1476 | stdout=stdout, stderr=stderr) |
59 | 1475 | # We use an empty conf so that the user is always prompted | 1477 | # We use an empty conf so that the user is always prompted |
60 | 1476 | conf = config.AuthenticationConfig() | 1478 | conf = config.AuthenticationConfig() |
61 | 1477 | self.assertEquals(password, | 1479 | self.assertEquals(password, |
62 | 1478 | conf.get_password(scheme, host, user, port=port, | 1480 | conf.get_password(scheme, host, user, port=port, |
63 | 1479 | realm=realm, path=path)) | 1481 | realm=realm, path=path)) |
65 | 1480 | self.assertEquals(stdout.getvalue(), expected_prompt) | 1482 | self.assertEquals(expected_prompt, stderr.getvalue()) |
66 | 1483 | self.assertEquals('', stdout.getvalue()) | ||
67 | 1481 | 1484 | ||
68 | 1482 | def _check_default_username_prompt(self, expected_prompt_format, scheme, | 1485 | def _check_default_username_prompt(self, expected_prompt_format, scheme, |
70 | 1483 | host=None, port=None, realm=None, path=None): | 1486 | host=None, port=None, realm=None, |
71 | 1487 | path=None): | ||
72 | 1484 | if host is None: | 1488 | if host is None: |
73 | 1485 | host = 'bar.org' | 1489 | host = 'bar.org' |
74 | 1486 | username = 'jim' | 1490 | username = 'jim' |
75 | @@ -1488,13 +1492,15 @@ | |||
76 | 1488 | 'scheme': scheme, 'host': host, 'port': port, | 1492 | 'scheme': scheme, 'host': host, 'port': port, |
77 | 1489 | 'realm': realm} | 1493 | 'realm': realm} |
78 | 1490 | stdout = tests.StringIOWrapper() | 1494 | stdout = tests.StringIOWrapper() |
79 | 1495 | stderr = tests.StringIOWrapper() | ||
80 | 1491 | ui.ui_factory = tests.TestUIFactory(stdin=username+ '\n', | 1496 | ui.ui_factory = tests.TestUIFactory(stdin=username+ '\n', |
82 | 1492 | stdout=stdout) | 1497 | stdout=stdout, stderr=stderr) |
83 | 1493 | # We use an empty conf so that the user is always prompted | 1498 | # We use an empty conf so that the user is always prompted |
84 | 1494 | conf = config.AuthenticationConfig() | 1499 | conf = config.AuthenticationConfig() |
85 | 1495 | self.assertEquals(username, conf.get_user(scheme, host, port=port, | 1500 | self.assertEquals(username, conf.get_user(scheme, host, port=port, |
86 | 1496 | realm=realm, path=path, ask=True)) | 1501 | realm=realm, path=path, ask=True)) |
88 | 1497 | self.assertEquals(stdout.getvalue(), expected_prompt) | 1502 | self.assertEquals(expected_prompt, stderr.getvalue()) |
89 | 1503 | self.assertEquals('', stdout.getvalue()) | ||
90 | 1498 | 1504 | ||
91 | 1499 | def test_username_defaults_prompts(self): | 1505 | def test_username_defaults_prompts(self): |
92 | 1500 | # HTTP prompts can't be tested here, see test_http.py | 1506 | # HTTP prompts can't be tested here, see test_http.py |
93 | 1501 | 1507 | ||
94 | === modified file 'bzrlib/tests/test_http.py' | |||
95 | --- bzrlib/tests/test_http.py 2009-05-27 13:59:47 +0000 | |||
96 | +++ bzrlib/tests/test_http.py 2009-06-02 14:35:20 +0000 | |||
97 | @@ -1569,15 +1569,18 @@ | |||
98 | 1569 | self.server.add_user('joe', 'foo') | 1569 | self.server.add_user('joe', 'foo') |
99 | 1570 | t = self.get_user_transport(None, None) | 1570 | t = self.get_user_transport(None, None) |
100 | 1571 | stdout = tests.StringIOWrapper() | 1571 | stdout = tests.StringIOWrapper() |
102 | 1572 | ui.ui_factory = tests.TestUIFactory(stdin='joe\nfoo\n', stdout=stdout) | 1572 | stderr = tests.StringIOWrapper() |
103 | 1573 | ui.ui_factory = tests.TestUIFactory(stdin='joe\nfoo\n', | ||
104 | 1574 | stdout=stdout, stderr=stderr) | ||
105 | 1573 | self.assertEqual('contents of a\n',t.get('a').read()) | 1575 | self.assertEqual('contents of a\n',t.get('a').read()) |
106 | 1574 | # stdin should be empty | 1576 | # stdin should be empty |
107 | 1575 | self.assertEqual('', ui.ui_factory.stdin.readline()) | 1577 | self.assertEqual('', ui.ui_factory.stdin.readline()) |
109 | 1576 | stdout.seek(0) | 1578 | stderr.seek(0) |
110 | 1577 | expected_prompt = self._expected_username_prompt(t._unqualified_scheme) | 1579 | expected_prompt = self._expected_username_prompt(t._unqualified_scheme) |
112 | 1578 | self.assertEquals(expected_prompt, stdout.read(len(expected_prompt))) | 1580 | self.assertEquals(expected_prompt, stderr.read(len(expected_prompt))) |
113 | 1581 | self.assertEquals('', stdout.getvalue()) | ||
114 | 1579 | self._check_password_prompt(t._unqualified_scheme, 'joe', | 1582 | self._check_password_prompt(t._unqualified_scheme, 'joe', |
116 | 1580 | stdout.readline()) | 1583 | stderr.readline()) |
117 | 1581 | 1584 | ||
118 | 1582 | def test_prompt_for_password(self): | 1585 | def test_prompt_for_password(self): |
119 | 1583 | if self._testing_pycurl(): | 1586 | if self._testing_pycurl(): |
120 | @@ -1588,12 +1591,15 @@ | |||
121 | 1588 | self.server.add_user('joe', 'foo') | 1591 | self.server.add_user('joe', 'foo') |
122 | 1589 | t = self.get_user_transport('joe', None) | 1592 | t = self.get_user_transport('joe', None) |
123 | 1590 | stdout = tests.StringIOWrapper() | 1593 | stdout = tests.StringIOWrapper() |
126 | 1591 | ui.ui_factory = tests.TestUIFactory(stdin='foo\n', stdout=stdout) | 1594 | stderr = tests.StringIOWrapper() |
127 | 1592 | self.assertEqual('contents of a\n',t.get('a').read()) | 1595 | ui.ui_factory = tests.TestUIFactory(stdin='foo\n', |
128 | 1596 | stdout=stdout, stderr=stderr) | ||
129 | 1597 | self.assertEqual('contents of a\n', t.get('a').read()) | ||
130 | 1593 | # stdin should be empty | 1598 | # stdin should be empty |
131 | 1594 | self.assertEqual('', ui.ui_factory.stdin.readline()) | 1599 | self.assertEqual('', ui.ui_factory.stdin.readline()) |
132 | 1595 | self._check_password_prompt(t._unqualified_scheme, 'joe', | 1600 | self._check_password_prompt(t._unqualified_scheme, 'joe', |
134 | 1596 | stdout.getvalue()) | 1601 | stderr.getvalue()) |
135 | 1602 | self.assertEquals('', stdout.getvalue()) | ||
136 | 1597 | # And we shouldn't prompt again for a different request | 1603 | # And we shouldn't prompt again for a different request |
137 | 1598 | # against the same transport. | 1604 | # against the same transport. |
138 | 1599 | self.assertEqual('contents of b\n',t.get('b').read()) | 1605 | self.assertEqual('contents of b\n',t.get('b').read()) |
139 | 1600 | 1606 | ||
140 | === modified file 'bzrlib/tests/test_ui.py' | |||
141 | --- bzrlib/tests/test_ui.py 2009-04-24 13:30:48 +0000 | |||
142 | +++ bzrlib/tests/test_ui.py 2009-06-02 14:35:20 +0000 | |||
143 | @@ -67,15 +67,17 @@ | |||
144 | 67 | self.assertEqual('', stdout.getvalue()) | 67 | self.assertEqual('', stdout.getvalue()) |
145 | 68 | 68 | ||
146 | 69 | def test_text_factory_ascii_password(self): | 69 | def test_text_factory_ascii_password(self): |
148 | 70 | ui = TestUIFactory(stdin='secret\n', stdout=StringIOWrapper()) | 70 | ui = TestUIFactory(stdin='secret\n', stdout=StringIOWrapper(), |
149 | 71 | stderr=StringIOWrapper()) | ||
150 | 71 | pb = ui.nested_progress_bar() | 72 | pb = ui.nested_progress_bar() |
151 | 72 | try: | 73 | try: |
152 | 73 | self.assertEqual('secret', | 74 | self.assertEqual('secret', |
153 | 74 | self.apply_redirected(ui.stdin, ui.stdout, | 75 | self.apply_redirected(ui.stdin, ui.stdout, |
155 | 75 | ui.stdout, | 76 | ui.stderr, |
156 | 76 | ui.get_password)) | 77 | ui.get_password)) |
157 | 77 | # ': ' is appended to prompt | 78 | # ': ' is appended to prompt |
159 | 78 | self.assertEqual(': ', ui.stdout.getvalue()) | 79 | self.assertEqual(': ', ui.stderr.getvalue()) |
160 | 80 | self.assertEqual('', ui.stdout.readline()) | ||
161 | 79 | # stdin should be empty | 81 | # stdin should be empty |
162 | 80 | self.assertEqual('', ui.stdin.readline()) | 82 | self.assertEqual('', ui.stdin.readline()) |
163 | 81 | finally: | 83 | finally: |
164 | @@ -88,21 +90,22 @@ | |||
165 | 88 | it to utf8 to test that we transport the password correctly. | 90 | it to utf8 to test that we transport the password correctly. |
166 | 89 | """ | 91 | """ |
167 | 90 | ui = TestUIFactory(stdin=u'baz\u1234'.encode('utf8'), | 92 | ui = TestUIFactory(stdin=u'baz\u1234'.encode('utf8'), |
171 | 91 | stdout=StringIOWrapper()) | 93 | stdout=StringIOWrapper(), |
172 | 92 | ui.stdin.encoding = 'utf8' | 94 | stderr=StringIOWrapper()) |
173 | 93 | ui.stdout.encoding = ui.stdin.encoding | 95 | ui.stderr.encoding = ui.stdout.encoding = ui.stdin.encoding = 'utf8' |
174 | 94 | pb = ui.nested_progress_bar() | 96 | pb = ui.nested_progress_bar() |
175 | 95 | try: | 97 | try: |
177 | 96 | password = self.apply_redirected(ui.stdin, ui.stdout, ui.stdout, | 98 | password = self.apply_redirected(ui.stdin, ui.stdout, ui.stderr, |
178 | 97 | ui.get_password, | 99 | ui.get_password, |
179 | 98 | u'Hello \u1234 %(user)s', | 100 | u'Hello \u1234 %(user)s', |
180 | 99 | user=u'some\u1234') | 101 | user=u'some\u1234') |
181 | 100 | # We use StringIO objects, we need to decode them | 102 | # We use StringIO objects, we need to decode them |
182 | 101 | self.assertEqual(u'baz\u1234', password.decode('utf8')) | 103 | self.assertEqual(u'baz\u1234', password.decode('utf8')) |
183 | 102 | self.assertEqual(u'Hello \u1234 some\u1234: ', | 104 | self.assertEqual(u'Hello \u1234 some\u1234: ', |
186 | 103 | ui.stdout.getvalue().decode('utf8')) | 105 | ui.stderr.getvalue().decode('utf8')) |
187 | 104 | # stdin should be empty | 106 | # stdin and stdout should be empty |
188 | 105 | self.assertEqual('', ui.stdin.readline()) | 107 | self.assertEqual('', ui.stdin.readline()) |
189 | 108 | self.assertEqual('', ui.stdout.readline()) | ||
190 | 106 | finally: | 109 | finally: |
191 | 107 | pb.finished() | 110 | pb.finished() |
192 | 108 | 111 | ||
193 | @@ -193,6 +196,7 @@ | |||
194 | 193 | "yes\nn\nnot an answer\n" | 196 | "yes\nn\nnot an answer\n" |
195 | 194 | "no\nfoo\n") | 197 | "no\nfoo\n") |
196 | 195 | factory.stdout = StringIO() | 198 | factory.stdout = StringIO() |
197 | 199 | factory.stderr = StringIO() | ||
198 | 196 | # there is no output from the base factory | 200 | # there is no output from the base factory |
199 | 197 | self.assertEqual(True, factory.get_boolean("")) | 201 | self.assertEqual(True, factory.get_boolean("")) |
200 | 198 | self.assertEqual(True, factory.get_boolean("")) | 202 | self.assertEqual(True, factory.get_boolean("")) |
201 | @@ -223,8 +227,10 @@ | |||
202 | 223 | 227 | ||
203 | 224 | def test_text_factory_prompt(self): | 228 | def test_text_factory_prompt(self): |
204 | 225 | # see <https://launchpad.net/bugs/365891> | 229 | # see <https://launchpad.net/bugs/365891> |
206 | 226 | factory = TextUIFactory(None, StringIO(), StringIO()) | 230 | factory = TextUIFactory(None, StringIO(), StringIO(), StringIO()) |
207 | 227 | factory.prompt('foo %2e') | 231 | factory.prompt('foo %2e') |
208 | 232 | self.assertEqual('', factory.stdout.getvalue()) | ||
209 | 233 | self.assertEqual('foo %2e', factory.stderr.getvalue()) | ||
210 | 228 | 234 | ||
211 | 229 | def test_text_factory_prompts_and_clears(self): | 235 | def test_text_factory_prompts_and_clears(self): |
212 | 230 | # a get_boolean call should clear the pb before prompting | 236 | # a get_boolean call should clear the pb before prompting |
213 | @@ -263,37 +269,41 @@ | |||
214 | 263 | factory = SilentUIFactory() | 269 | factory = SilentUIFactory() |
215 | 264 | factory.stdin = StringIO("someuser\n\n") | 270 | factory.stdin = StringIO("someuser\n\n") |
216 | 265 | factory.stdout = StringIO() | 271 | factory.stdout = StringIO() |
218 | 266 | self.assertEquals(None, | 272 | factory.stderr = StringIO() |
219 | 273 | self.assertEquals(None, | ||
220 | 267 | factory.get_username(u'Hello\u1234 %(host)s', host=u'some\u1234')) | 274 | factory.get_username(u'Hello\u1234 %(host)s', host=u'some\u1234')) |
221 | 268 | self.assertEquals("", factory.stdout.getvalue()) | 275 | self.assertEquals("", factory.stdout.getvalue()) |
222 | 276 | self.assertEquals("", factory.stderr.getvalue()) | ||
223 | 269 | self.assertEquals("someuser\n\n", factory.stdin.getvalue()) | 277 | self.assertEquals("someuser\n\n", factory.stdin.getvalue()) |
224 | 270 | 278 | ||
225 | 271 | def test_text_ui_getusername(self): | 279 | def test_text_ui_getusername(self): |
226 | 272 | factory = TextUIFactory(None, None, None) | 280 | factory = TextUIFactory(None, None, None) |
227 | 273 | factory.stdin = StringIO("someuser\n\n") | 281 | factory.stdin = StringIO("someuser\n\n") |
228 | 274 | factory.stdout = StringIO() | 282 | factory.stdout = StringIO() |
229 | 283 | factory.stderr = StringIO() | ||
230 | 275 | factory.stdout.encoding = "utf8" | 284 | factory.stdout.encoding = "utf8" |
231 | 276 | # there is no output from the base factory | 285 | # there is no output from the base factory |
235 | 277 | self.assertEqual("someuser", | 286 | self.assertEqual("someuser", |
236 | 278 | factory.get_username('Hello %(host)s', host='some')) | 287 | factory.get_username('Hello %(host)s', host='some')) |
237 | 279 | self.assertEquals("Hello some: ", factory.stdout.getvalue()) | 288 | self.assertEquals("Hello some: ", factory.stderr.getvalue()) |
238 | 289 | self.assertEquals('', factory.stdout.getvalue()) | ||
239 | 280 | self.assertEqual("", factory.get_username("Gebruiker")) | 290 | self.assertEqual("", factory.get_username("Gebruiker")) |
240 | 281 | # stdin should be empty | 291 | # stdin should be empty |
241 | 282 | self.assertEqual('', factory.stdin.readline()) | 292 | self.assertEqual('', factory.stdin.readline()) |
242 | 283 | 293 | ||
243 | 284 | def test_text_ui_getusername_utf8(self): | 294 | def test_text_ui_getusername_utf8(self): |
244 | 285 | ui = TestUIFactory(stdin=u'someuser\u1234'.encode('utf8'), | 295 | ui = TestUIFactory(stdin=u'someuser\u1234'.encode('utf8'), |
248 | 286 | stdout=StringIOWrapper()) | 296 | stdout=StringIOWrapper(), stderr=StringIOWrapper()) |
249 | 287 | ui.stdin.encoding = "utf8" | 297 | ui.stderr.encoding = ui.stdout.encoding = ui.stdin.encoding = "utf8" |
247 | 288 | ui.stdout.encoding = ui.stdin.encoding | ||
250 | 289 | pb = ui.nested_progress_bar() | 298 | pb = ui.nested_progress_bar() |
251 | 290 | try: | 299 | try: |
252 | 291 | # there is no output from the base factory | 300 | # there is no output from the base factory |
254 | 292 | username = self.apply_redirected(ui.stdin, ui.stdout, ui.stdout, | 301 | username = self.apply_redirected(ui.stdin, ui.stdout, ui.stderr, |
255 | 293 | ui.get_username, u'Hello\u1234 %(host)s', host=u'some\u1234') | 302 | ui.get_username, u'Hello\u1234 %(host)s', host=u'some\u1234') |
256 | 294 | self.assertEquals(u"someuser\u1234", username.decode('utf8')) | 303 | self.assertEquals(u"someuser\u1234", username.decode('utf8')) |
259 | 295 | self.assertEquals(u"Hello\u1234 some\u1234: ", | 304 | self.assertEquals(u"Hello\u1234 some\u1234: ", |
260 | 296 | ui.stdout.getvalue().decode("utf8")) | 305 | ui.stderr.getvalue().decode("utf8")) |
261 | 306 | self.assertEquals('', ui.stdout.getvalue()) | ||
262 | 297 | finally: | 307 | finally: |
263 | 298 | pb.finished() | 308 | pb.finished() |
264 | 299 | 309 | ||
265 | 300 | 310 | ||
266 | === modified file 'bzrlib/ui/__init__.py' | |||
267 | --- bzrlib/ui/__init__.py 2009-04-24 15:49:33 +0000 | |||
268 | +++ bzrlib/ui/__init__.py 2009-06-02 14:35:20 +0000 | |||
269 | @@ -227,7 +227,7 @@ | |||
270 | 227 | prompt = prompt % kwargs | 227 | prompt = prompt % kwargs |
271 | 228 | prompt = prompt.encode(osutils.get_terminal_encoding(), 'replace') | 228 | prompt = prompt.encode(osutils.get_terminal_encoding(), 'replace') |
272 | 229 | self.clear_term() | 229 | self.clear_term() |
274 | 230 | self.stdout.write(prompt) | 230 | self.stderr.write(prompt) |
275 | 231 | 231 | ||
276 | 232 | def note(self, msg): | 232 | def note(self, msg): |
277 | 233 | """Write an already-formatted message.""" | 233 | """Write an already-formatted message.""" |
There is no good reason to use stdout for prompting the user. Bug #376582 raised the issue that doing that forbid the redirection of command outputs.
This patch use stderr instead and should address the issue.