|
4 | 4 | import asyncio
|
5 | 5 | import builtins
|
6 | 6 | import collections
|
| 7 | +import contextlib |
7 | 8 | import decimal
|
8 | 9 | import fractions
|
9 | 10 | import gc
|
10 | 11 | import io
|
| 12 | +import importlib |
11 | 13 | import locale
|
12 | 14 | import math
|
13 | 15 | import os
|
@@ -2400,49 +2402,63 @@ def child(wpipe):
|
2400 | 2402 | expected = terminal_input.decode(sys.stdin.encoding) # what else?
|
2401 | 2403 | self.assertEqual(input_result, expected)
|
2402 | 2404 |
|
2403 |
| - def test_input_tty(self): |
2404 |
| - # Test input() functionality when wired to a tty (the code path |
2405 |
| - # is different and invokes GNU readline if available). |
2406 |
| - self.check_input_tty("prompt", b"quux") |
2407 |
| - |
2408 |
| - def skip_if_readline(self): |
| 2405 | + @contextlib.contextmanager |
| 2406 | + def detach_readline(self): |
2409 | 2407 | # bpo-13886: When the readline module is loaded, PyOS_Readline() uses
|
2410 | 2408 | # the readline implementation. In some cases, the Python readline
|
2411 | 2409 | # callback rlhandler() is called by readline with a string without
|
2412 |
| - # non-ASCII characters. Skip tests on non-ASCII characters if the |
2413 |
| - # readline module is loaded, since test_builtin is not intended to test |
| 2410 | + # non-ASCII characters. |
| 2411 | + # Unlink readline temporarily from PyOS_Readline() for those tests, |
| 2412 | + # since test_builtin is not intended to test |
2414 | 2413 | # the readline module, but the builtins module.
|
2415 |
| - if 'readline' in sys.modules: |
2416 |
| - self.skipTest("the readline module is loaded") |
| 2414 | + if "readline" in sys.modules: |
| 2415 | + try: |
| 2416 | + c = importlib.import_module("ctypes") |
| 2417 | + except ImportError: |
| 2418 | + self.skipTest("the readline module is loaded") |
| 2419 | + |
| 2420 | + fp_api = "PyOS_ReadlineFunctionPointer" |
| 2421 | + prev_value = c.c_void_p.in_dll(c.pythonapi, fp_api).value |
| 2422 | + c.c_void_p.in_dll(c.pythonapi, fp_api).value = None |
| 2423 | + yield |
| 2424 | + c.c_void_p.in_dll(c.pythonapi, fp_api).value = prev_value |
| 2425 | + else: |
| 2426 | + yield |
| 2427 | + |
| 2428 | + def test_input_tty(self): |
| 2429 | + # Test input() functionality when wired to a tty |
| 2430 | + with self.detach_readline(): |
| 2431 | + self.check_input_tty("prompt", b"quux") |
2417 | 2432 |
|
2418 | 2433 | def test_input_tty_non_ascii(self):
|
2419 |
| - self.skip_if_readline() |
2420 | 2434 | # Check stdin/stdout encoding is used when invoking PyOS_Readline()
|
2421 |
| - self.check_input_tty("prompté", b"quux\xc3\xa9", "utf-8") |
| 2435 | + with self.detach_readline(): |
| 2436 | + self.check_input_tty("prompté", b"quux\xc3\xa9", "utf-8") |
2422 | 2437 |
|
2423 | 2438 | def test_input_tty_non_ascii_unicode_errors(self):
|
2424 |
| - self.skip_if_readline() |
2425 | 2439 | # Check stdin/stdout error handler is used when invoking PyOS_Readline()
|
2426 |
| - self.check_input_tty("prompté", b"quux\xe9", "ascii") |
| 2440 | + with self.detach_readline(): |
| 2441 | + self.check_input_tty("prompté", b"quux\xe9", "ascii") |
2427 | 2442 |
|
2428 | 2443 | def test_input_tty_null_in_prompt(self):
|
2429 |
| - self.check_input_tty("prompt\0", b"", |
2430 |
| - expected='ValueError: input: prompt string cannot contain ' |
2431 |
| - 'null characters') |
| 2444 | + with self.detach_readline(): |
| 2445 | + self.check_input_tty("prompt\0", b"", |
| 2446 | + expected='ValueError: input: prompt string cannot contain ' |
| 2447 | + 'null characters') |
2432 | 2448 |
|
2433 | 2449 | def test_input_tty_nonencodable_prompt(self):
|
2434 |
| - self.skip_if_readline() |
2435 |
| - self.check_input_tty("prompté", b"quux", "ascii", stdout_errors='strict', |
2436 |
| - expected="UnicodeEncodeError: 'ascii' codec can't encode " |
2437 |
| - "character '\\xe9' in position 6: ordinal not in " |
2438 |
| - "range(128)") |
| 2450 | + with self.detach_readline(): |
| 2451 | + self.check_input_tty("prompté", b"quux", "ascii", stdout_errors='strict', |
| 2452 | + expected="UnicodeEncodeError: 'ascii' codec can't encode " |
| 2453 | + "character '\\xe9' in position 6: ordinal not in " |
| 2454 | + "range(128)") |
2439 | 2455 |
|
2440 | 2456 | def test_input_tty_nondecodable_input(self):
|
2441 |
| - self.skip_if_readline() |
2442 |
| - self.check_input_tty("prompt", b"quux\xe9", "ascii", stdin_errors='strict', |
2443 |
| - expected="UnicodeDecodeError: 'ascii' codec can't decode " |
2444 |
| - "byte 0xe9 in position 4: ordinal not in " |
2445 |
| - "range(128)") |
| 2457 | + with self.detach_readline(): |
| 2458 | + self.check_input_tty("prompt", b"quux\xe9", "ascii", stdin_errors='strict', |
| 2459 | + expected="UnicodeDecodeError: 'ascii' codec can't decode " |
| 2460 | + "byte 0xe9 in position 4: ordinal not in " |
| 2461 | + "range(128)") |
2446 | 2462 |
|
2447 | 2463 | def test_input_no_stdout_fileno(self):
|
2448 | 2464 | # Issue #24402: If stdin is the original terminal but stdout.fileno()
|
|
0 commit comments