Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

gh-110944: Move pty helper to test.support and add basic pdb completion test #111826

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Nov 13, 2023
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 60 additions & 0 deletions 60 Lib/test/support/pty_helper.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
"""
Helper to run a script in a pseudo-terminal.
"""
import os
import selectors
import subprocess
import sys
from contextlib import ExitStack
from errno import EIO

from test.support.import_helper import import_module

def run_pty(script, input=b"dummy input\r", env=None):
pty = import_module('pty')
output = bytearray()
[master, slave] = pty.openpty()
args = (sys.executable, '-c', script)
proc = subprocess.Popen(args, stdin=slave, stdout=slave, stderr=slave, env=env)
os.close(slave)
with ExitStack() as cleanup:
cleanup.enter_context(proc)
def terminate(proc):
try:
proc.terminate()
except ProcessLookupError:
# Workaround for Open/Net BSD bug (Issue 16762)
pass
cleanup.callback(terminate, proc)
cleanup.callback(os.close, master)
# Avoid using DefaultSelector and PollSelector. Kqueue() does not
# work with pseudo-terminals on OS X < 10.9 (Issue 20365) and Open
# BSD (Issue 20667). Poll() does not work with OS X 10.6 or 10.4
# either (Issue 20472). Hopefully the file descriptor is low enough
# to use with select().
sel = cleanup.enter_context(selectors.SelectSelector())
sel.register(master, selectors.EVENT_READ | selectors.EVENT_WRITE)
os.set_blocking(master, False)
while True:
for [_, events] in sel.select():
if events & selectors.EVENT_READ:
try:
chunk = os.read(master, 0x10000)
except OSError as err:
# Linux raises EIO when slave is closed (Issue 5380)
if err.errno != EIO:
raise
chunk = b""
if not chunk:
return output
output.extend(chunk)
if events & selectors.EVENT_WRITE:
try:
input = input[os.write(master, input):]
except OSError as err:
# Apparently EIO means the slave was closed
if err.errno != EIO:
raise
input = b"" # Stop writing
if not input:
sel.modify(master, selectors.EVENT_READ)
28 changes: 28 additions & 0 deletions 28 Lib/test/test_pdb.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
from io import StringIO
from test import support
from test.support import os_helper
from test.support.import_helper import import_module
from test.support.pty_helper import run_pty
# This little helper class is essential for testing pdb under doctest.
from test.test_doctest import _FakeInput
from unittest.mock import patch
Expand Down Expand Up @@ -3235,6 +3237,32 @@ def test_checkline_is_not_executable(self):
self.assertFalse(db.checkline(os_helper.TESTFN, lineno))


@support.requires_subprocess()
class PdbTestReadline(unittest.TestCase):
def setUpClass():
# Ensure that the readline module is loaded
# If this fails, the test is skipped because SkipTest will be raised
readline = import_module('readline')
if readline.__doc__ and "libedit" in readline.__doc__:
raise unittest.SkipTest("libedit readline is not supported for pdb")

def test_basic_completion(self):
script = textwrap.dedent("""
import pdb; pdb.Pdb().set_trace()
print('hello')
gaogaotiantian marked this conversation as resolved.
Show resolved Hide resolved
""")

# List everything starting with 'co', there should be multiple matches
# then add ntin and complete 'contin' to 'continue'
input = b"co\t\tntin\t\n"

output = run_pty(script, input)

self.assertIn(b'cont', output)
self.assertIn(b'condition', output)
self.assertIn(b'continue', output)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The cont assert is useless since continue is checked later. How about checking that it appears as a separate word?

Also, IMO it would be nice to assert that thecontinue command caused the print to run (outputting a string that doesn't appear in the source).

Suggested change
self.assertIn(b'cont', output)
self.assertIn(b'condition', output)
self.assertIn(b'continue', output)
self.assertIn(b'cont', output.split())
self.assertIn(b'condition', output)
self.assertIn(b'continue', output)
self.assertIn(b'hello!', output)



def load_tests(loader, tests, pattern):
from test import test_pdb
tests.addTest(doctest.DocTestSuite(test_pdb))
Expand Down
55 changes: 1 addition & 54 deletions 55 Lib/test/test_readline.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,15 @@
"""
Very minimal unittests for parts of the readline module.
"""
from contextlib import ExitStack
from errno import EIO
import locale
import os
import selectors
import subprocess
import sys
import tempfile
import unittest
from test.support import verbose
from test.support.import_helper import import_module
from test.support.os_helper import unlink, temp_dir, TESTFN
from test.support.pty_helper import run_pty
from test.support.script_helper import assert_python_ok

# Skip tests if there is no readline module
Expand Down Expand Up @@ -304,55 +301,5 @@ def test_history_size(self):
self.assertEqual(lines[-1].strip(), b"last input")


def run_pty(script, input=b"dummy input\r", env=None):
pty = import_module('pty')
output = bytearray()
[master, slave] = pty.openpty()
args = (sys.executable, '-c', script)
proc = subprocess.Popen(args, stdin=slave, stdout=slave, stderr=slave, env=env)
os.close(slave)
with ExitStack() as cleanup:
cleanup.enter_context(proc)
def terminate(proc):
try:
proc.terminate()
except ProcessLookupError:
# Workaround for Open/Net BSD bug (Issue 16762)
pass
cleanup.callback(terminate, proc)
cleanup.callback(os.close, master)
# Avoid using DefaultSelector and PollSelector. Kqueue() does not
# work with pseudo-terminals on OS X < 10.9 (Issue 20365) and Open
# BSD (Issue 20667). Poll() does not work with OS X 10.6 or 10.4
# either (Issue 20472). Hopefully the file descriptor is low enough
# to use with select().
sel = cleanup.enter_context(selectors.SelectSelector())
sel.register(master, selectors.EVENT_READ | selectors.EVENT_WRITE)
os.set_blocking(master, False)
while True:
for [_, events] in sel.select():
if events & selectors.EVENT_READ:
try:
chunk = os.read(master, 0x10000)
except OSError as err:
# Linux raises EIO when slave is closed (Issue 5380)
if err.errno != EIO:
raise
chunk = b""
if not chunk:
return output
output.extend(chunk)
if events & selectors.EVENT_WRITE:
try:
input = input[os.write(master, input):]
except OSError as err:
# Apparently EIO means the slave was closed
if err.errno != EIO:
raise
input = b"" # Stop writing
if not input:
sel.modify(master, selectors.EVENT_READ)


if __name__ == "__main__":
unittest.main()
Morty Proxy This is a proxified and sanitized view of the page, visit original site.