summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeff Quast <contact@jeffquast.com>2015-10-06 10:17:50 -0700
committerJeff Quast <contact@jeffquast.com>2015-10-06 10:17:50 -0700
commitae932d179adc4c602f9a4298076cdc5a82f9351a (patch)
tree389f0a2ee8ba6722e600f5a8235c1e3b6026db5b
parent0b7fee3c974d89b7f7f51fef9a1893e25ed980da (diff)
downloadpexpect-git-ae932d179adc4c602f9a4298076cdc5a82f9351a.tar.gz
2 new tools: display-{fpathconf.maxcanon}.py
tests/test_maxcanon.py has been deleted and turned into an "autodetection" tool of sorts, no longer attempting to assert exacting values, but determine it programmatically.
-rw-r--r--pexpect/pty_spawn.py14
-rw-r--r--tests/test_maxcanon.py179
-rw-r--r--tools/display-fpathconf.py41
-rw-r--r--tools/display-maxcanon.py80
4 files changed, 128 insertions, 186 deletions
diff --git a/pexpect/pty_spawn.py b/pexpect/pty_spawn.py
index 7fc27fe..299016c 100644
--- a/pexpect/pty_spawn.py
+++ b/pexpect/pty_spawn.py
@@ -492,9 +492,9 @@ class spawn(SpawnBase):
This value may be discovered using fpathconf(3)::
- >>> from os import fpathconf
- >>> print(fpathconf(0, 'PC_MAX_CANON'))
- 256
+ >>> from os import fpathconf
+ >>> print(fpathconf(0, 'PC_MAX_CANON'))
+ 256
On such a system, only 256 bytes may be received per line. Any
subsequent bytes received will be discarded. BEL (``'\a'``) is then
@@ -505,10 +505,10 @@ class spawn(SpawnBase):
Canonical input processing may be disabled altogether by executing
a shell, then stty(1), before executing the final program::
- >>> bash = pexpect.spawn('/bin/bash', echo=False)
- >>> bash.sendline('stty -icanon')
- >>> bash.sendline('base64')
- >>> bash.sendline('x' * 5000)
+ >>> bash = pexpect.spawn('/bin/bash', echo=False)
+ >>> bash.sendline('stty -icanon')
+ >>> bash.sendline('base64')
+ >>> bash.sendline('x' * 5000)
'''
time.sleep(self.delaybeforesend)
diff --git a/tests/test_maxcanon.py b/tests/test_maxcanon.py
deleted file mode 100644
index cd48cbc..0000000
--- a/tests/test_maxcanon.py
+++ /dev/null
@@ -1,179 +0,0 @@
-""" Module for canonical-mode tests. """
-# std imports
-import sys
-import os
-
-# local
-import pexpect
-from . import PexpectTestCase
-
-# 3rd-party
-import pytest
-
-
-class TestCaseCanon(PexpectTestCase.PexpectTestCase):
- """
- Test expected Canonical mode behavior (limited input line length).
-
- All systems use the value of MAX_CANON which can be found using
- fpathconf(3) value PC_MAX_CANON -- with the exception of Linux
- and FreeBSD.
-
- Linux, though defining a value of 255, actually honors the value
- of 4096 from linux kernel include file tty.h definition
- N_TTY_BUF_SIZE.
-
- Linux also does not honor IMAXBEL. termios(3) states, "Linux does not
- implement this bit, and acts as if it is always set." Although these
- tests ensure it is enabled, this is a non-op for Linux.
-
- More unsettling in regards to Linux, Fedora and Debian have different
- behaviours. For this reason, **these test has been disabled entirely**.
-
- FreeBSD supports neither, and instead uses a fraction (1/5) of the tty
- speed which is always 9600. Therefor, the maximum limited input line
- length is 9600 / 5 = 1920.
-
- These tests only ensure the correctness of the behavior described by
- the sendline() docstring. pexpect is not particularly involved in
- these scenarios, though if we wish to expose some kind of interface
- to tty.setraw, for example, these tests may be re-purposed as such.
-
- Lastly, portions of these tests are skipped on Travis-CI. It produces
- unexpected behavior not reproduced on Debian/GNU Linux.
- """
-
- def setUp(self):
- super(TestCaseCanon, self).setUp()
-
- self.echo = False
- if sys.platform.lower().startswith('linux'):
- # linux is 4096, N_TTY_BUF_SIZE.
- self.max_input = 4096
- self.echo = True
- elif sys.platform.lower().startswith('sunos'):
- # SunOS allows PC_MAX_CANON + 1; see
- # https://bitbucket.org/illumos/illumos-gate/src/d07a59219ab7fd2a7f39eb47c46cf083c88e932f/usr/src/uts/common/io/ldterm.c?at=default#cl-1888
- self.max_input = os.fpathconf(0, 'PC_MAX_CANON') + 1
- elif sys.platform.lower().startswith('freebsd'):
- # http://lists.freebsd.org/pipermail/freebsd-stable/2009-October/052318.html
- self.max_input = 9600 / 5
- else:
- # All others (probably) limit exactly at PC_MAX_CANON
- self.max_input = os.fpathconf(0, 'PC_MAX_CANON')
-
- @pytest.mark.skipif(
- sys.platform.lower().startswith('freebsd'),
- reason='os.write to BLOCK indefinitely on FreeBSD in this case'
- )
- def disabled_under_max_canon(self):
- " BEL is not sent by terminal driver at maximum bytes - 1. "
- # given,
- child = pexpect.spawn('bash', echo=self.echo, timeout=5)
- child.sendline('echo READY')
- child.sendline('stty icanon imaxbel')
- child.sendline('echo BEGIN; cat')
-
- # some systems BEL on (maximum - 1), not able to receive CR,
- # even though all characters up until then were received, they
- # simply cannot be transmitted, as CR is part of the transmission.
- send_bytes = self.max_input - 1
-
- # exercise,
- child.sendline('_' * send_bytes)
-
- # fast forward beyond 'cat' command, as ^G can be found as part of
- # set-xterm-title sequence of $PROMPT_COMMAND or $PS1.
- child.expect_exact('BEGIN')
-
- # verify, all input is found in echo output,
- child.expect_exact('_' * send_bytes)
-
- # BEL is not found,
- with self.assertRaises(pexpect.TIMEOUT):
- child.expect_exact('\a', timeout=1)
-
- # cleanup,
- child.sendeof() # exit cat(1)
- child.sendline('exit 0') # exit bash(1)
- child.expect(pexpect.EOF)
- assert not child.isalive()
- assert child.exitstatus == 0
-
- @pytest.mark.skipif(
- sys.platform.lower().startswith('freebsd'),
- reason='os.write to BLOCK indefinitely on FreeBSD in this case'
- )
- def disabled_beyond_max_icanon(self):
- " a single BEL is sent when maximum bytes is reached. "
- # given,
- child = pexpect.spawn('bash', echo=self.echo, timeout=5)
- child.sendline('stty icanon imaxbel erase ^H')
- child.sendline('cat')
- send_bytes = self.max_input
-
- # exercise,
- child.sendline('_' * send_bytes)
- child.expect_exact('\a')
-
- # exercise, we must now backspace to send CR.
- child.sendcontrol('h')
- child.sendline()
-
- if os.environ.get('TRAVIS', None) == 'true':
- # Travis-CI has intermittent behavior here, possibly
- # because the master process is itself, a PTY?
- return
-
- # verify the length of (maximum - 1) received by cat(1),
- # which has written it back out,
- child.expect_exact('_' * (send_bytes - 1))
- # and not a byte more.
- with self.assertRaises(pexpect.TIMEOUT):
- child.expect_exact('_', timeout=1)
-
- # cleanup,
- child.sendeof() # exit cat(1)
- child.sendline('exit 0') # exit bash(1)
- child.expect_exact(pexpect.EOF)
- assert not child.isalive()
- assert child.exitstatus == 0
-
- @pytest.mark.skipif(
- sys.platform.lower().startswith('freebsd'),
- reason='os.write to BLOCK indefinitely on FreeBSD in this case'
- )
- def disabled_max_no_icanon(self):
- " may exceed maximum input bytes if canonical mode is disabled. "
- # given,
- child = pexpect.spawn('bash', echo=self.echo, timeout=5)
- child.sendline('stty -icanon imaxbel')
- child.sendline('echo BEGIN; cat')
- send_bytes = self.max_input + 11
-
- # exercise,
- child.sendline('_' * send_bytes)
-
- # fast forward beyond 'cat' command, as ^G can be found as part of
- # set-xterm-title sequence of $PROMPT_COMMAND or $PS1.
- child.expect_exact('BEGIN')
-
- if os.environ.get('TRAVIS', None) == 'true':
- # Travis-CI has intermittent behavior here, possibly
- # because the master process is itself, a PTY?
- return
-
- # BEL is *not* found,
- with self.assertRaises(pexpect.TIMEOUT):
- child.expect_exact('\a', timeout=1)
-
- # verify, all input is found in output,
- child.expect_exact('_' * send_bytes)
-
- # cleanup,
- child.sendcontrol('c') # exit cat(1) (eof wont work in -icanon)
- child.sendcontrol('c')
- child.sendline('exit 0') # exit bash(1)
- child.expect(pexpect.EOF)
- assert not child.isalive()
- assert child.exitstatus == 0
diff --git a/tools/display-fpathconf.py b/tools/display-fpathconf.py
new file mode 100644
index 0000000..d40cbae
--- /dev/null
+++ b/tools/display-fpathconf.py
@@ -0,0 +1,41 @@
+#!/usr/bin/env python
+"""Displays os.fpathconf values related to terminals. """
+from __future__ import print_function
+import sys
+import os
+
+
+def display_fpathconf():
+ DISP_VALUES = (
+ ('PC_MAX_CANON', ('Max no. of bytes in a '
+ 'terminal canonical input line.')),
+ ('PC_MAX_INPUT', ('Max no. of bytes for which '
+ 'space is available in a terminal input queue.')),
+ ('PC_PIPE_BUF', ('Max no. of bytes which will '
+ 'be written atomically to a pipe.')),
+ ('PC_VDISABLE', 'Terminal character disabling value.')
+ )
+ FMT = '{name:<13} {value:<5} {description}'
+
+ # column header
+ print(FMT.format(name='name', value='value', description='description'))
+ print(FMT.format(name=('-' * 13), value=('-' * 5), description=('-' * 11)))
+
+ fd = sys.stdin.fileno()
+ for name, description in DISP_VALUES:
+ key = os.pathconf_names.get(name, None)
+ if key is None:
+ value = 'UNDEF'
+ else:
+ try:
+ value = os.fpathconf(fd, name)
+ except OSError as err:
+ value = 'OSErrno {0.errno}'.format(err)
+ if name == 'PC_VDISABLE':
+ value = hex(value)
+ print(FMT.format(name=name, value=value, description=description))
+ print()
+
+
+if __name__ == '__main__':
+ display_fpathconf()
diff --git a/tools/display-maxcanon.py b/tools/display-maxcanon.py
new file mode 100644
index 0000000..cbd664f
--- /dev/null
+++ b/tools/display-maxcanon.py
@@ -0,0 +1,80 @@
+#!/usr/bin/env python
+"""
+This tool uses pexpect to test expected Canonical mode length.
+
+All systems use the value of MAX_CANON which can be found using
+fpathconf(3) value PC_MAX_CANON -- with the exception of Linux
+and FreeBSD.
+
+Linux, though defining a value of 255, actually honors the value
+of 4096 from linux kernel include file tty.h definition
+N_TTY_BUF_SIZE.
+
+Linux also does not honor IMAXBEL. termios(3) states, "Linux does not
+implement this bit, and acts as if it is always set." Although these
+tests ensure it is enabled, this is a non-op for Linux.
+
+FreeBSD supports neither, and instead uses a fraction (1/5) of the tty
+speed which is always 9600. Therefor, the maximum limited input line
+length is 9600 / 5 = 1920.
+
+These tests only ensure the correctness of the behavior described by
+the sendline() docstring -- the values listed there, and above should
+be equal to the output of the given OS described, but no promises!
+"""
+# std import
+from __future__ import print_function
+import sys
+import os
+
+
+def detect_maxcanon():
+ import pexpect
+ bashrc = os.path.join(
+ # re-use pexpect/replwrap.py's bashrc file,
+ os.path.dirname(__file__), os.path.pardir, 'pexpect', 'bashrc.sh')
+
+ child = pexpect.spawn('bash', ['--rcfile', bashrc],
+ echo=True, encoding='utf8', timeout=3)
+
+ child.sendline(u'echo -n READY_; echo GO')
+ child.expect_exact(u'READY_GO')
+
+ child.sendline(u'stty icanon imaxbel erase ^H; echo -n retval: $?')
+ child.expect_exact(u'retval: 0')
+
+ child.sendline(u'echo -n GO_; echo AGAIN')
+ child.expect_exact(u'GO_AGAIN')
+ child.sendline(u'cat')
+
+ child.delaybeforesend = 0
+
+ column, blocksize = 0, 64
+ ch_marker = u'_'
+
+ print('auto-detecting MAX_CANON: ', end='')
+ sys.stdout.flush()
+
+ while True:
+ child.send(ch_marker * blocksize)
+ result = child.expect([ch_marker * blocksize, u'\a'])
+ if result == 0:
+ # entire block fit without emitting bel
+ column += blocksize
+ elif result == 1:
+ # an '\a' was emitted, count the number of ch_markers
+ # found since last blocksize, determining our MAX_CANON
+ column += child.before.count(ch_marker)
+ break
+ print(column)
+
+if __name__ == '__main__':
+ try:
+ detect_maxcanon()
+ except ImportError:
+ # we'd like to use this with CI -- but until we integrate
+ # with tox, we can't determine a period in testing when
+ # the pexpect module has been installed
+ print('warning: pexpect not in module path, MAX_CANON '
+ 'could not be determined by systems test.',
+ file=sys.stderr)