From 89001c1c5fec347eb4dca72a07f2e840748d972f Mon Sep 17 00:00:00 2001 From: Benedek Racz Date: Thu, 19 Sep 2019 15:26:01 +0200 Subject: [PATCH] [FIX] fix issue #6. Debug print remained in the write process --- .travis.yml | 9 ++++ README.md | 43 ++++++++++++++++ tests/foo.py | 19 +++++++ tests/test_echo.py | 46 +++++++++++++++++ wexpect.py | 124 +++++++++++++++++++++++++-------------------- 5 files changed, 187 insertions(+), 54 deletions(-) create mode 100644 tests/foo.py create mode 100644 tests/test_echo.py diff --git a/.travis.yml b/.travis.yml index 7ac133a..3dbc33a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,3 +1,12 @@ +# +# WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING +# +# Travis-ci doesn't supports python runs on windows platform right now (2019.09.09). This medium story +# https://medium.com/@dirk.avery/travis-ci-python-and-windows-2f9a1b6dd096 can didn't helped either. +# (wexpect is a compicated case) +# +# So travis build paused! See appveyor builds. (https://ci.appveyor.com/project/raczben/wexpect) + language: python dist: xenial # required for Python >= 3.7 matrix: diff --git a/README.md b/README.md index 02b0459..29443e5 100644 --- a/README.md +++ b/README.md @@ -103,6 +103,49 @@ The wexpect uses [pbr](https://docs.openstack.org/pbr/latest/) for managing rele - Install the test build `python -m pip install wexpect` - run `python -c "import wexpect;print(wexpect.__version__)"` +## Basic behaviour +Let's go through the example code: +```python +import wexpect +child = wexpect.spawn('cmd.exe') +child.expect('>') +child.sendline('ls') +child.expect('>') +print(child.before) +child.sendline('exit') +``` + +### spawn() + +`child = wexpect.spawn('cmd.exe')` + +Call trace: + + - ::spawn (line 289) + - spawn_windows::__init__() (line 1639) + - spawn_unix::__init__() (line 313) + - spawn_windows::_spawn() (line 1660) + - Wtty::spawn() (line 1932) + - Wtty::startChild() (line 1978) + - win32process.CreateProcess() (line 2024) + + + +### expect() + +`child.expect('>')` + +Call trace: + + - spawn_linux::expect() (line 1285) + - spawn_linux::expect_list() (line 1365) + - spawn_linux::expect_loop() (line 1397) + - spawn_windows::read_nonblocking() (line 1635) + - Wtty::read_nonblocking() + - Wtty::readConsoleToCursor() + - Wtty::readConsole() (line: 2153) + - __consout.ReadConsoleOutputCharacter() (line: 2176) + diff --git a/tests/foo.py b/tests/foo.py new file mode 100644 index 0000000..44862b0 --- /dev/null +++ b/tests/foo.py @@ -0,0 +1,19 @@ +''' +This is is a very basic stdio handler script. This is used by python.py example. +''' + +import time + +# Read an integer from the user: +print('Give a small integer: ', end='') +num = input() + +# Wait the given time +for i in range(int(num)): + print('waiter ' + str(i)) + time.sleep(0.2) + +# Ask the name of the user to say hello. +print('Give your name: ', end='') +name = input() +print('Hello ' + str(name), end='') diff --git a/tests/test_echo.py b/tests/test_echo.py new file mode 100644 index 0000000..46f2bba --- /dev/null +++ b/tests/test_echo.py @@ -0,0 +1,46 @@ +#!/usr/bin/env python +''' +PEXPECT LICENSE + + This license is approved by the OSI and FSF as GPL-compatible. + http://opensource.org/licenses/isc-license.txt + + Copyright (c) 2012, Noah Spurrier + PERMISSION TO USE, COPY, MODIFY, AND/OR DISTRIBUTE THIS SOFTWARE FOR ANY + PURPOSE WITH OR WITHOUT FEE IS HEREBY GRANTED, PROVIDED THAT THE ABOVE + COPYRIGHT NOTICE AND THIS PERMISSION NOTICE APPEAR IN ALL COPIES. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +''' +import wexpect +import unittest +from . import PexpectTestCase + +class EchoTestCase(PexpectTestCase.PexpectTestCase): + def testPath(self): + # Path of cmd executable: + cmd_exe = 'cmd' + cmdPrompt = '>' + + # Start the child process + p = wexpect.spawn(cmd_exe) + + # Wait for prompt + p.expect(cmdPrompt) + + # Send a command + p.sendline('echo hello') + p.expect(cmdPrompt) + + self.assertEqual('hello', p.before.splitlines()[1]) + +if __name__ == '__main__': + unittest.main() + +suite = unittest.makeSuite(EchoTestCase,'test') diff --git a/wexpect.py b/wexpect.py index 7fe20c4..d6691c2 100644 --- a/wexpect.py +++ b/wexpect.py @@ -63,59 +63,85 @@ http://pexpect.sourceforge.net/ $Id: pexpect.py 507 2007-12-27 02:40:52Z noah $ """ -try: - import os, sys, time - import select - import shutil - import re - import struct - import types - import errno - import traceback - import signal - import pkg_resources - - if sys.platform != 'win32': - import pty - import tty - import termios - import resource - import fcntl - else: - from io import StringIO - try: - from ctypes import windll - import pywintypes - from win32com.shell.shellcon import CSIDL_APPDATA - from win32com.shell.shell import SHGetSpecialFolderPath - import win32console - import win32process - import win32con - import win32gui - import win32api - import win32file - import winerror - except ImportError as e: - raise ImportError(str(e) + "\nThis package requires the win32 python packages.") - screenbufferfillchar = '\4' - maxconsoleY = 8000 -except ImportError as e: +# +# wexpect is windows only. Use pexpect on linux like systems. +# +import sys +if sys.platform != 'win32': raise ImportError (str(e) + """ +sys.platform != 'win32': Wexpect supports only Windows. +Pexpect is intended for UNIX-like operating systems.""") -A critical module was not found. Probably this operating system does not -support it. Pexpect is intended for UNIX-like operating systems.""") +# +# Import built in modules +# +import logging +import os +import time +import re +import select +import shutil +import struct +import types +import errno +import traceback +import signal +import pkg_resources +from io import StringIO +try: + from ctypes import windll + import pywintypes + from win32com.shell.shellcon import CSIDL_APPDATA + from win32com.shell.shell import SHGetSpecialFolderPath + import win32console + import win32process + import win32con + import win32gui + import win32api + import win32file + import winerror +except ImportError as e: + raise ImportError(str(e) + "\nThis package requires the win32 python packages.\r\nInstall with pip install pywin32") + +# +# System-wide constants +# +screenbufferfillchar = '\4' +maxconsoleY = 8000 + +# +# Create logger: We write logs only to file. Printing out logs are dangerous, because of the deep +# console manipulation. +# +logger = logging.getLogger('wexpect') +logger.setLevel(logging.DEBUG) +fh = logging.FileHandler('wexpect.log') +formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') +fh.setFormatter(formatter) +logger.addHandler(fh) + +# Test the logger +logger.info('wexpect imported; logger working') + +# The version is handled by the package: pbr, which derives the version from the git tags. try: __version__ = pkg_resources.require("wexpect")[0].version except: __version__ = '0.0.1.unkown0' __revision__ = '$Revision: 399 $' + __all__ = ['ExceptionPexpect', 'EOF', 'TIMEOUT', 'spawn', 'run', 'which', 'split_command_line', '__version__', '__revision__'] -# Exception classes used by this module. -class ExceptionPexpect(Exception): +#################################################################################################### +# +# Exceptions +# +#################################################################################################### + +class ExceptionPexpect(Exception): """Base class for all exceptions raised by this module. """ @@ -148,22 +174,14 @@ class ExceptionPexpect(Exception): else: return False -class EOF(ExceptionPexpect): - """Raised when EOF is read from a child. This usually means the child has exited.""" +class EOF(ExceptionPexpect): + """Raised when EOF is read from a child. This usually means the child has exited. + The user can wait to EOF, which means he waits the end of the execution of the child process.""" class TIMEOUT(ExceptionPexpect): - """Raised when a read time exceeds the timeout. """ -##class TIMEOUT_PATTERN(TIMEOUT): -## """Raised when the pattern match time exceeds the timeout. -## This is different than a read TIMEOUT because the child process may -## give output, thus never give a TIMEOUT, but the output -## may never match a pattern. -## """ -##class MAXBUFFER(ExceptionPexpect): -## """Raised when a scan buffer fills before matching an expected pattern.""" def run (command, timeout=-1, withexitstatus=False, events=None, extra_args=None, logfile=None, cwd=None, env=None): @@ -2107,8 +2125,6 @@ class Wtty: return "" consinfo = self.__consout.GetConsoleScreenBufferInfo() startCo = consinfo['CursorPosition'] - print(records) - print(consinfo) wrote = self.__consin.WriteConsoleInput(records) ts = time.time() while self.__consin and self.__consin.PeekConsoleInput(8) != ():