├── .github
└── ISSUE_TEMPLATE
│ ├── bug_report.md
│ └── feature_request.md
├── .gitignore
├── .travis.yml
├── CODE_OF_CONDUCT.md
├── LICENSE
├── README.md
├── appveyor.yml
├── codecov.yml
├── doc
├── Makefile
├── make.bat
├── requirements.txt
└── source
│ ├── api
│ ├── console_reader.rst
│ ├── host.rst
│ ├── index.rst
│ └── wexpect_util.rst
│ ├── conf.py
│ ├── examples.rst
│ ├── history.rst
│ └── index.rst
├── examples
├── README.md
├── cmd_wrapper.py
├── foo.py
├── hello_wexpect.py
├── python.py
└── termination.py
├── issues
├── README.md
├── bla.py
├── bla2.py
├── i09_cmd_error.py
├── i09_confused_string.py
├── i09_py_sim_no_error.py
├── i09_py_sim_no_error2.py
├── i10_parent.py
├── i11_greek_parent.py
├── i11_greek_printer.py
├── i11_unicode_parent.py
├── i11_unicode_printer.py
├── i18_setecho_error.py
└── long_printer.py
├── requirements.txt
├── setup.cfg
├── setup.py
├── tests
├── PexpectTestCase.py
├── __init__.py
├── echo_w_prompt.py
├── exit1.py
├── foo.py
├── lines_printer.py
├── list100.py
├── long_printer.py
├── needs_kill.py
├── parametric_printer.py
├── pyinstaller_test.py
├── test_command_list_split.py
├── test_console_reader.py
├── test_constructor.py
├── test_delay.py
├── test_destructor.py
├── test_echo.py
├── test_expect.py
├── test_interact.py
├── test_isalive.py
├── test_long.py
├── test_misc.py
├── test_misc_console_coverage.py
├── test_parametric_printer.py
├── test_parametric_printer_coverage.py
├── test_readline.py
├── test_run.py
├── test_timeout_pattern.py
└── utils.py
├── tox.ini
├── wexpect.spec
└── wexpect
├── __init__.py
├── __main__.py
├── console_reader.py
├── host.py
├── legacy_wexpect.py
└── wexpect_util.py
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Describe the bug**
11 | A clear and concise description of what the bug is.
12 |
13 | **To Reproduce**
14 | Steps to reproduce the behavior:
15 |
16 | **Expected behavior**
17 | A clear and concise description of what you expected to happen.
18 |
19 | **Screenshots**
20 | If applicable, add screenshots to help explain your problem.
21 |
22 | **Environment:**
23 | - [windows version]
24 | - [Python version]
25 | - [wexpect version]
26 |
27 | **Additional context**
28 | Add any other context about the problem here.
29 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Is your feature request related to a problem? Please describe.**
11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12 |
13 | **Describe the solution you'd like**
14 | A clear and concise description of what you want to happen.
15 |
16 | **Describe alternatives you've considered**
17 | A clear and concise description of any alternative solutions or features you've considered.
18 |
19 | **Additional context**
20 | Add any other context or screenshots about the feature request here.
21 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | test_01_installed/*
2 |
3 | # Byte-compiled / optimized / DLL files
4 | __pycache__/
5 | *.py[cod]
6 |
7 | *coverage.xml
8 |
9 | # C extensions
10 | *.so
11 |
12 | .eggs/
13 | AUTHORS
14 | ChangeLog
15 |
16 | # Distribution / packaging
17 | .Python
18 | env/
19 | build/
20 | develop-eggs/
21 | dist/
22 | downloads/
23 | eggs/
24 | lib/
25 | lib64/
26 | parts/
27 | sdist/
28 | var/
29 | *.egg-info/
30 | .installed.cfg
31 | *.egg
32 |
33 | # PyInstaller
34 | # Usually these files are written by a python script from a template
35 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
36 | *.manifest
37 | *.spec
38 | !wexpect.spec
39 |
40 | # Installer logs
41 | pip-log.txt
42 | pip-delete-this-directory.txt
43 |
44 | # Unit test / coverage reports
45 | htmlcov/
46 | .tox/
47 | .coverage*
48 | .cache
49 | nosetests.xml
50 | coverage.xml
51 |
52 | # Translations
53 | *.mo
54 | *.pot
55 |
56 | # Django stuff:
57 | *.log
58 |
59 | # Sphinx documentation
60 | docs/_build/
61 |
62 | # PyBuilder
63 | target/
64 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | #
2 | # WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
3 | #
4 | # Travis-ci doesn't supports python runs on windows platform right now (2019.09.09). This medium story
5 | # https://medium.com/@dirk.avery/travis-ci-python-and-windows-2f9a1b6dd096 can didn't helped either.
6 | # (wexpect is a compicated case)
7 | #
8 | # So travis build paused! See appveyor builds. (https://ci.appveyor.com/project/raczben/wexpect)
9 |
10 | language: python
11 | dist: xenial # required for Python >= 3.7
12 | matrix:
13 | include:
14 | - stage: test
15 | - os: windows
16 | language: sh
17 | python: "3.7"
18 | before_install:
19 | - choco install python3
20 | - export PATH="/c/Python37:/c/Python37/Scripts:$PATH"
21 | - python -m pip install virtualenv
22 | - ls -la /c/Python37/Scripts
23 | - virtualenv $HOME/venv
24 | - source $HOME/venv/Scripts/activate
25 | # command to install dependencies
26 | install:
27 | - pip install .[test]
28 | # command to run tests
29 | script:
30 | - tox
31 | # Push the results back to codecov
32 | after_success:
33 | - codecov
34 |
35 | notifications:
36 | email:
37 | on_success: never
38 | on_failure: always
39 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | In the interest of fostering an open and welcoming environment, we as
6 | contributors and maintainers pledge to making participation in our project and
7 | our community a harassment-free experience for everyone, regardless of age, body
8 | size, disability, ethnicity, sex characteristics, gender identity and expression,
9 | level of experience, education, socio-economic status, nationality, personal
10 | appearance, race, religion, or sexual identity and orientation.
11 |
12 | ## Our Standards
13 |
14 | Examples of behavior that contributes to creating a positive environment
15 | include:
16 |
17 | * Using welcoming and inclusive language
18 | * Being respectful of differing viewpoints and experiences
19 | * Gracefully accepting constructive criticism
20 | * Focusing on what is best for the community
21 | * Showing empathy towards other community members
22 |
23 | Examples of unacceptable behavior by participants include:
24 |
25 | * The use of sexualized language or imagery and unwelcome sexual attention or
26 | advances
27 | * Trolling, insulting/derogatory comments, and personal or political attacks
28 | * Public or private harassment
29 | * Publishing others' private information, such as a physical or electronic
30 | address, without explicit permission
31 | * Other conduct which could reasonably be considered inappropriate in a
32 | professional setting
33 |
34 | ## Our Responsibilities
35 |
36 | Project maintainers are responsible for clarifying the standards of acceptable
37 | behavior and are expected to take appropriate and fair corrective action in
38 | response to any instances of unacceptable behavior.
39 |
40 | Project maintainers have the right and responsibility to remove, edit, or
41 | reject comments, commits, code, wiki edits, issues, and other contributions
42 | that are not aligned to this Code of Conduct, or to ban temporarily or
43 | permanently any contributor for other behaviors that they deem inappropriate,
44 | threatening, offensive, or harmful.
45 |
46 | ## Scope
47 |
48 | This Code of Conduct applies both within project spaces and in public spaces
49 | when an individual is representing the project or its community. Examples of
50 | representing a project or community include using an official project e-mail
51 | address, posting via an official social media account, or acting as an appointed
52 | representative at an online or offline event. Representation of a project may be
53 | further defined and clarified by project maintainers.
54 |
55 | ## Enforcement
56 |
57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
58 | reported by contacting the project team at betontalpfa@gmail.com. All
59 | complaints will be reviewed and investigated and will result in a response that
60 | is deemed necessary and appropriate to the circumstances. The project team is
61 | obligated to maintain confidentiality with regard to the reporter of an incident.
62 | Further details of specific enforcement policies may be posted separately.
63 |
64 | Project maintainers who do not follow or enforce the Code of Conduct in good
65 | faith may face temporary or permanent repercussions as determined by other
66 | members of the project's leadership.
67 |
68 | ## Attribution
69 |
70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
72 |
73 | [homepage]: https://www.contributor-covenant.org
74 |
75 | For answers to common questions about this code of conduct, see
76 | https://www.contributor-covenant.org/faq
77 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2008 Noah Spurrier, Richard Holden, Marco Molteni, Kimberley Burchett, Robert Stone, Hartmut Goebel, Chad Schroeder, Erick Tryzelaar, Dave Kirby, Ids vander Molen, George Todd, Noel Taylor, Nicolas D. Cesar, Alexander Gattin, Geoffrey Marshall, Francisco Lourenco, Glen Mabey, Karthik Gurusamy, Fernando Perez, Corey Minyard, Jon Cohen, Guillaume Chazarain, Andrew Ryan, Nick Craig-Wood, Andrew Stone, Jorgen Grahn, Benedek Racz
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # **wexpect**
2 |
3 | [](https://ci.appveyor.com/project/raczben/wexpect)
4 | [](https://codecov.io/gh/raczben/wexpect)
5 | [](https://wexpect.readthedocs.io/en/latest/?badge=latest)
6 |
7 | *Wexpect* is a Windows variant of [pexpect](https://pexpect.readthedocs.io/en/stable/).
8 |
9 | *Pexpect* is a Python module for spawning child applications and controlling
10 | them automatically.
11 |
12 | ## You need wexpect if...
13 |
14 | - you want to control any windows console application from python script.
15 | - you want to write test-automation script for a windows console application.
16 | - you want to automate your job by controlling multiple application parallel, synchronously.
17 |
18 | ## **Install**
19 |
20 | pip install wexpect
21 |
22 | ## **Usage**
23 |
24 | To interract with a child process use `spawn` method:
25 |
26 | ```python
27 | import wexpect
28 |
29 | prompt = '[A-Z]\:.+>'
30 |
31 | child = wexpect.spawn('cmd.exe')
32 | child.expect(prompt) # Wait for startup prompt
33 |
34 | child.sendline('dir') # List the current directory
35 | child.expect(prompt)
36 |
37 | print(child.before) # Print the list
38 | child.sendline('exit')
39 | ```
40 |
41 | For more information see [examples](./examples) folder.
42 |
43 | ---
44 | ## REFACTOR
45 |
46 | **Refactor has been finished!!!** The default spawn class is `SpawnPipe` from now. For more
47 | information read [history](https://wexpect.readthedocs.io/en/latest/history.html#refactor).
48 |
49 | ---
50 | ## What is wexpect?
51 |
52 | Wexpect is a Python module for spawning child applications and controlling
53 | them automatically. Wexpect can be used for automating interactive applications
54 | such as ssh, ftp, passwd, telnet, etc. It can be used to a automate setup
55 | scripts for duplicating software package installations on different servers. It
56 | can be used for automated software testing. Wexpect is in the spirit of Don
57 | Libes' Expect, but Wexpect is pure Python. Other Expect-like modules for Python
58 | require TCL and Expect or require C extensions to be compiled. Wexpect does not
59 | use C, Expect, or TCL extensions.
60 |
61 | Original Pexpect should work on any platform that supports the standard Python pty module. While
62 | Wexpect works on Windows platforms. The Wexpect interface focuses on ease of use so that simple
63 | tasks are easy.
64 |
65 | ---
66 | ## Dev
67 |
68 | Thanks for any contributing!
69 |
70 | ### Test
71 |
72 | To run test, enter into the folder of the wexpect's repo then:
73 |
74 | `python -m unittest`
75 |
76 | ### Deploy
77 |
78 | The deployment itself is automated and done by [appveyor](https://ci.appveyor.com/project/raczben/wexpect).
79 | See `after_test` section in [appveyor.yml](appveyor.yml) for more details.
80 |
81 | The wexpect uses [pbr](https://docs.openstack.org/pbr/latest/) for managing releasing procedures.
82 | The versioning is handled by the pbr. The *"master-version"* is the git tag. Pbr derives the package
83 | version from the git tags.
84 |
--------------------------------------------------------------------------------
/appveyor.yml:
--------------------------------------------------------------------------------
1 | # appveyor.yml
2 | ---
3 |
4 | environment:
5 | # Encrypted password (more precisely: token) for upload distro to pypy:
6 | # The "pypipw" is a custom name.
7 | # The original (sensitive) token is generated by the pypi: https://pypi.org/manage/account/token/
8 | # The token has encripted by the appveyor: https://ci.appveyor.com/tools/encrypt
9 | pypipw:
10 | secure: N+tYP6JrVCZ12LX7MUmHYJ8kx07F4A1hXtmEW01RmhrrQBdylIf0SO/eEfW5f4ox3S4xG/lgGSzNlZckvgifrsYeeBkWwoSH5/AJ3SnOJ7HBVojVt2t3bAznS6x3aPT7WDpwGN7piwus9aHSmpKaOzRoEOBKfgHv3aUzb907C0d0Yr12LU/4cIoTAn7jMziifSq45Z50lsQwzYic/VkarxTh+GXuCCm1Mb8F686H8i6Smm1Q1n9BsXowYnzwdrTZSBVOUtpd48Mh9JKgSNhfmQ==
11 | testpypipw:
12 | secure: CcyBI8e/2LdIT2aYIytTAgR4795DNBDM/ztsz1kqZYYOeNc3zlJWLdYWrnjCHn5W6/ZcAHrsxCdCMHvtr6PIVgBRpl2RR3fk2jKTzKqJJsLW871q30BsE0kws32f1IiqfjVtLn8BUC91IJ2xBBXtOYktf1tCMi3zJMSF9+MIOQKIu298bIRnD1Lc+4lzcSZJOn4I7dOMdzlcCMRqhtO58TGwR/hD+22FHjyWVB8nLL18AO+XXS9lHSOUrH6rD5NYvVFZD68oV/RrCGAjRmfMnw==
13 | # Set default pytohn, the matrinx is in the tox settings.
14 | PYTHON: "C:\\Python37"
15 |
16 | build: on
17 |
18 | build_script:
19 | # Create source distribution.
20 | - cmd: |
21 | "%PYTHON%/python.exe" -m pip install --upgrade pip
22 | "%PYTHON%/python.exe" -m pip install --upgrade setuptools wheel
23 | "%PYTHON%/python.exe" -m setup sdist
24 |
25 | install:
26 | - set PATH=%PYTHON%;%PYTHON%\\Scripts;%PATH%
27 | - pip install .[test]
28 |
29 | test_script:
30 | - tox
31 |
32 | after_test:
33 | # Upload code coverage results.
34 | # https://github.com/codecov/codecov-python/issues/158#issuecomment-514282362
35 | - for %%f in (*coverage.xml) do (
36 | (codecov --no-color -X gcov --file %%f --required ) || (sleep 30 && codecov --no-color -X gcov --file %%f --required )
37 | )
38 |
39 | # fill .pypirc file.
40 | # pypi
41 | - cmd: "echo [pypi] > %USERPROFILE%\\.pypirc"
42 | - cmd: "echo repository: https://upload.pypi.org/legacy/ >> %USERPROFILE%\\.pypirc"
43 | - cmd: "echo username: __token__ >> %USERPROFILE%\\.pypirc"
44 | - cmd: "echo password: %pypipw% >> %USERPROFILE%\\.pypirc"
45 | # testpypi
46 | - cmd: "echo [testpypi] >> %USERPROFILE%\\.pypirc"
47 | - cmd: "echo repository: https://test.pypi.org/legacy/ >> %USERPROFILE%\\.pypirc"
48 | - cmd: "echo username: __token__ >> %USERPROFILE%\\.pypirc"
49 | - cmd: "echo password: %testpypipw% >> %USERPROFILE%\\.pypirc"
50 |
51 | artifacts:
52 | - path: dist\wexpect*.tar.gz
53 | name: wexpect-source-distro
54 |
55 | deploy_script:
56 | # Upload to pypi.
57 | # More precisely. The master and release builds will be uploaded to pypi. Test branch will be
58 | # uploaded to test-pypi the test builds.
59 | # See more at https://stackoverflow.com/a/39155147/2506522
60 |
61 | - IF %APPVEYOR_REPO_BRANCH%==master (
62 | twine upload -r pypi dist\\wexpect*.tar.gz
63 | )
64 | - IF %APPVEYOR_REPO_TAG%==true (
65 | twine upload -r pypi dist\\wexpect*.tar.gz
66 | )
67 | - IF %APPVEYOR_REPO_BRANCH%==test (
68 | twine upload -r testpypi dist\\wexpect*.tar.gz
69 | )
70 |
--------------------------------------------------------------------------------
/codecov.yml:
--------------------------------------------------------------------------------
1 | # Settings for https://codecov.io/gh/raczben/wexpect/
2 |
3 | ignore:
4 | - tests/* # ignore folders and all its contents
5 |
--------------------------------------------------------------------------------
/doc/Makefile:
--------------------------------------------------------------------------------
1 | # Minimal makefile for Sphinx documentation
2 | #
3 |
4 | # You can set these variables from the command line, and also
5 | # from the environment for the first two.
6 | SPHINXOPTS ?=
7 | SPHINXBUILD ?= sphinx-build
8 | SOURCEDIR = source
9 | BUILDDIR = build
10 |
11 | # Put it first so that "make" without argument is like "make help".
12 | help:
13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
14 |
15 | .PHONY: help Makefile
16 |
17 | # Catch-all target: route all unknown targets to Sphinx using the new
18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
19 | %: Makefile
20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
21 |
--------------------------------------------------------------------------------
/doc/make.bat:
--------------------------------------------------------------------------------
1 | @ECHO OFF
2 |
3 | pushd %~dp0
4 |
5 | REM Command file for Sphinx documentation
6 |
7 | if "%SPHINXBUILD%" == "" (
8 | set SPHINXBUILD=sphinx-build
9 | )
10 | set SOURCEDIR=source
11 | set BUILDDIR=build
12 |
13 | if "%1" == "" goto help
14 |
15 | %SPHINXBUILD% >NUL 2>NUL
16 | if errorlevel 9009 (
17 | echo.
18 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
19 | echo.installed, then set the SPHINXBUILD environment variable to point
20 | echo.to the full path of the 'sphinx-build' executable. Alternatively you
21 | echo.may add the Sphinx directory to PATH.
22 | echo.
23 | echo.If you don't have Sphinx installed, grab it from
24 | echo.http://sphinx-doc.org/
25 | exit /b 1
26 | )
27 |
28 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
29 | goto end
30 |
31 | :help
32 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
33 |
34 | :end
35 | popd
36 |
--------------------------------------------------------------------------------
/doc/requirements.txt:
--------------------------------------------------------------------------------
1 | pbr
2 |
--------------------------------------------------------------------------------
/doc/source/api/console_reader.rst:
--------------------------------------------------------------------------------
1 | Console reader
2 | ==============
3 |
4 | .. automodule:: wexpect.console_reader
5 |
6 |
7 | ConsoleReaderPipe
8 | -----------------
9 |
10 | .. autoclass:: ConsoleReaderPipe
11 |
12 | .. automethod:: __init__
13 | .. automethod:: read_loop
14 | .. automethod:: suspend_child
15 | .. automethod:: resume_child
16 | .. automethod:: refresh_console
17 | .. automethod:: terminate_child
18 | .. automethod:: isalive
19 | .. automethod:: write
20 | .. automethod:: createKeyEvent
21 | .. automethod:: initConsole
22 | .. automethod:: parseData
23 | .. automethod:: getConsoleOut
24 | .. automethod:: getCoord
25 | .. automethod:: getOffset
26 | .. automethod:: readConsole
27 | .. automethod:: readConsoleToCursor
28 | .. automethod:: interact
29 | .. automethod:: sendeof
30 | .. automethod:: create_connection
31 | .. automethod:: close_connection
32 | .. automethod:: send_to_host
33 | .. automethod:: get_from_host
34 |
35 |
36 | ConsoleReaderSocket
37 | -------------------
38 |
39 | .. autoclass:: ConsoleReaderSocket
40 |
41 | .. automethod:: __init__
42 | .. automethod:: read_loop
43 | .. automethod:: suspend_child
44 | .. automethod:: resume_child
45 | .. automethod:: refresh_console
46 | .. automethod:: terminate_child
47 | .. automethod:: isalive
48 | .. automethod:: write
49 | .. automethod:: createKeyEvent
50 | .. automethod:: initConsole
51 | .. automethod:: parseData
52 | .. automethod:: getConsoleOut
53 | .. automethod:: getCoord
54 | .. automethod:: getOffset
55 | .. automethod:: readConsole
56 | .. automethod:: readConsoleToCursor
57 | .. automethod:: interact
58 | .. automethod:: sendeof
59 | .. automethod:: create_connection
60 | .. automethod:: close_connection
61 | .. automethod:: send_to_host
62 | .. automethod:: get_from_host
63 |
--------------------------------------------------------------------------------
/doc/source/api/host.rst:
--------------------------------------------------------------------------------
1 | Host
2 | =============
3 |
4 | .. automodule:: wexpect.host
5 |
6 | Functions
7 | ---------
8 |
9 | .. automethod:: wexpect.host.run
10 |
11 | SpawnPipe
12 | ---------
13 |
14 | .. autoclass:: SpawnPipe
15 |
16 | .. automethod:: __init__
17 | .. automethod:: expect
18 | .. automethod:: expect_exact
19 | .. automethod:: expect_list
20 | .. automethod:: compile_pattern_list
21 | .. automethod:: send
22 | .. automethod:: sendline
23 | .. automethod:: write
24 | .. automethod:: writelines
25 | .. automethod:: sendeof
26 | .. automethod:: read
27 | .. automethod:: readline
28 | .. automethod:: read_nonblocking
29 |
30 |
31 | SpawnSocket
32 | -----------
33 |
34 | .. autoclass:: SpawnSocket
35 |
36 | .. automethod:: __init__
37 | .. automethod:: expect
38 | .. automethod:: expect_exact
39 | .. automethod:: expect_list
40 | .. automethod:: compile_pattern_list
41 | .. automethod:: send
42 | .. automethod:: sendline
43 | .. automethod:: write
44 | .. automethod:: writelines
45 | .. automethod:: sendeof
46 | .. automethod:: read
47 | .. automethod:: readline
48 | .. automethod:: read_nonblocking
49 |
--------------------------------------------------------------------------------
/doc/source/api/index.rst:
--------------------------------------------------------------------------------
1 | API documentation
2 | =================
3 |
4 | Wexpect symbols
5 | ---------------
6 |
7 | Wexpect package has the following symbols. (Exported by :code:`__all__` in code:`__init__.py`)
8 |
9 | .. _wexpect.spawn:
10 |
11 | **spawn**
12 |
13 | This is the main class interface for Wexpect. Use this class to start and control child applications.
14 | There are two implementation: :class:`wexpect.host.SpawnPipe` uses Windows-Pipe for communicate child.
15 | :class:`wexpect.SpawnSocket` uses TCP socket. Choose the default implementation with
16 | :code:`WEXPECT_SPAWN_CLASS` environment variable, or the :class:`wexpect.host.SpawnPipe` will be
17 | chosen by default.
18 |
19 | .. _wexpect.SpawnPipe:
20 |
21 | **SpawnPipe**
22 |
23 | :class:`wexpect.host.SpawnPipe` is the default spawn class, but you can access it directly with its
24 | exact name.
25 |
26 | .. _wexpect.SpawnSocket:
27 |
28 | **SpawnSocket**
29 |
30 | :class:`wexpect.host.SpawnSocket` is the secondary spawn class, you can access it directly with its
31 | exact name or by setting the :code:`WEXPECT_SPAWN_CLASS` environment variable to :code:`SpawnSocket`
32 |
33 | .. _wexpect.run:
34 |
35 | **run**
36 |
37 | :meth:`wexpect.host.run` runs the given command; waits for it to finish; then returns all output as a string.
38 | This function is similar to :code:`os.system()`.
39 |
40 | .. _wexpect.EOF:
41 |
42 | **EOF**
43 |
44 | :class:`wexpect.wexpect_util.EOF` is an exception. This usually means the child has exited.
45 |
46 | .. _wexpect.TIMEOUT:
47 |
48 | **TIMEOUT**
49 |
50 | :class:`wexpect.wexpect_util.TIMEOUT` raised when a read time exceeds the timeout.
51 |
52 | .. _wexpect.__version__:
53 |
54 | **__version__**
55 |
56 | This gives back the version of the wexpect release. Versioning is handled by the
57 | `pbr `_ package, which derives it from Git tags.
58 |
59 | .. _wexpect.spawn_class_name:
60 |
61 | **spawn_class_name**
62 |
63 | Contains the default spawn class' name even if the user has not specified it. The value can be
64 | :code:`SpawnPipe` or :code:`SpawnSocket`
65 |
66 | .. _wexpect.ConsoleReaderSocket:
67 |
68 | **ConsoleReaderSocket**
69 |
70 | For advanced users only!
71 | :class:`wexpect.console_reader.ConsoleReaderSocket`
72 |
73 | .. _wexpect.ConsoleReaderPipe:
74 |
75 | **ConsoleReaderPipe**
76 |
77 | For advanced users only!
78 | :class:`wexpect.console_reader.ConsoleReaderPipe`
79 |
80 | Wexpect modules
81 | ---------------
82 |
83 | .. toctree::
84 | :maxdepth: 2
85 |
86 | host
87 | wexpect_util
88 | console_reader
89 |
--------------------------------------------------------------------------------
/doc/source/api/wexpect_util.rst:
--------------------------------------------------------------------------------
1 | Wexpect util
2 | ============
3 |
4 | .. automodule:: wexpect.wexpect_util
5 |
6 | Functions
7 | ---------
8 |
9 | .. automethod:: wexpect.wexpect_util.str2bool
10 | .. automethod:: wexpect.wexpect_util.spam
11 | .. automethod:: wexpect.wexpect_util.init_logger
12 | .. automethod:: wexpect.wexpect_util.split_command_line
13 | .. automethod:: wexpect.wexpect_util.join_args
14 |
15 | ExceptionPexpect
16 | ----------------
17 |
18 | .. autoclass:: ExceptionPexpect
19 |
20 | EOF
21 | ---
22 |
23 | .. autoclass:: EOF
24 |
25 | TIMEOUT
26 | -------
27 |
28 | .. autoclass:: TIMEOUT
29 |
--------------------------------------------------------------------------------
/doc/source/conf.py:
--------------------------------------------------------------------------------
1 | # Configuration file for the Sphinx documentation builder.
2 | #
3 | # This file only contains a selection of the most common options. For a full
4 | # list see the documentation:
5 | # https://www.sphinx-doc.org/en/master/usage/configuration.html
6 |
7 | # -- Path setup --------------------------------------------------------------
8 |
9 | # If extensions (or modules to document with autodoc) are in another directory,
10 | # add these directories to sys.path here. If the directory is relative to the
11 | # documentation root, use os.path.abspath to make it absolute, like shown here.
12 |
13 | import os
14 | import sys
15 | from pbr.version import VersionInfo
16 |
17 | repo_path = os.path.dirname(os.path.dirname(os.path.dirname(os.path.realpath(__file__))))
18 | print(repo_path)
19 | sys.path.insert(0, repo_path)
20 |
21 | # import wexpect
22 |
23 | os.environ['WEXPECT_SPAWN_CLASS'] = 'SpawnPipe'
24 | autodoc_mock_imports = ["pywintypes", "win32process", "win32con", "win32file", "winerror",
25 | "win32pipe", "ctypes", "win32console", "win32gui", "psutil"]
26 |
27 | # from ctypes import windll
28 |
29 | # The master toctree document.
30 | master_doc = 'index'
31 |
32 | # -- Project information -----------------------------------------------------
33 |
34 | project = 'wexpect'
35 | copyright = '2020, Benedek Racz'
36 | author = 'Benedek Racz'
37 |
38 | # The version info for the project you're documenting, acts as replacement for
39 | # |version| and |release|, also used in various other places throughout the
40 | # built documents.
41 | #
42 | # The short X.Y version.
43 | package_name='wexpect'
44 | info = VersionInfo(package_name)
45 | version = info.version_string()
46 |
47 | # The full version, including alpha/beta/rc tags.
48 | release = version
49 |
50 |
51 |
52 | # -- General configuration ---------------------------------------------------
53 |
54 | # Add any Sphinx extension module names here, as strings. They can be
55 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
56 | # ones.
57 | extensions = [
58 | 'sphinx.ext.autodoc',
59 | 'sphinx.ext.intersphinx'
60 | ]
61 |
62 | # Add any paths that contain templates here, relative to this directory.
63 | templates_path = ['_templates']
64 |
65 | # List of patterns, relative to source directory, that match files and
66 | # directories to ignore when looking for source files.
67 | # This pattern also affects html_static_path and html_extra_path.
68 | exclude_patterns = []
69 |
70 |
71 | # -- Options for HTML output -------------------------------------------------
72 |
73 | # The theme to use for HTML and HTML Help pages. See the documentation for
74 | # a list of builtin themes.
75 | #
76 | html_theme = 'default'
77 |
78 | # Add any paths that contain custom static files (such as style sheets) here,
79 | # relative to this directory. They are copied after the builtin static files,
80 | # so a file named "default.css" will overwrite the builtin "default.css".
81 | html_static_path = []
82 |
--------------------------------------------------------------------------------
/doc/source/examples.rst:
--------------------------------------------------------------------------------
1 | Examples
2 | ========
3 |
4 | `hello_wexpect.py `_
5 |
6 | This is the simplest example. It starts a windows command interpreter (aka. cmd) lists the current
7 | directory and exits.
8 |
9 | .. code-block:: python
10 |
11 | import wexpect
12 |
13 | # Start cmd as child process
14 | child = wexpect.spawn('cmd.exe')
15 |
16 | # Wait for prompt when cmd becomes ready.
17 | child.expect('>')
18 |
19 | # Prints the cmd's start message
20 | print(child.before, end='')
21 | print(child.after, end='')
22 |
23 | # run list directory command
24 | child.sendline('ls')
25 |
26 | # Waiting for prompt
27 | child.expect('>')
28 |
29 | # Prints content of the directory
30 | print(child.before, end='')
31 | print(child.after, end='')
32 |
33 | # Exit from cmd
34 | child.sendline('exit')
35 |
36 | # Waiting for cmd termination.
37 | child.wait()
38 |
39 |
40 | `terminaton.py `_
41 |
42 | This script shows three method to terminate your application. `terminate_programmatically()`
43 | shows the recommended way, which kills the child by sending the application specific exit command.
44 | After that waiting for the terminaton is recommended.
45 | `terminate_eof()` shows how to terminate child program by sending EOF character. Some program can be
46 | terminated by sending EOF character. Waiting for the terminaton is recommended in this case too.
47 | `terminate_terminate()` shows how to terminate child program by sending kill signal. Some
48 | application requires sending SIGTERM to kill the child process. `terminate()` call `kill()`
49 | function, which sends SIGTERM to child process. Waiting for the terminaton is not required
50 | explicitly in this case. The wait is included in `terminate()` function.
51 |
52 | .. code-block:: python
53 |
54 | import wexpect
55 |
56 | def terminate_programmatically():
57 | '''Terminate child program by command. This is the recommended method. Send your application's
58 | exit command to quit the child's process. After that wait for the terminaton.
59 | '''
60 | print('terminate_programmatically')
61 |
62 | # Start cmd as child process
63 | child = wexpect.spawn('cmd.exe')
64 |
65 | # Wait for prompt when cmd becomes ready.
66 | child.expect('>')
67 |
68 | # Exit from cmd
69 | child.sendline('exit')
70 |
71 | # Waiting for cmd termination.
72 | child.wait()
73 |
74 | def terminate_eof():
75 | '''Terminate child program by sending EOF character. Some program can be terminated by sending
76 | an EOF character. Waiting for the terminaton is recommended in this case too.
77 | '''
78 | print('terminate_eof')
79 |
80 | # Start cat as child process
81 | child = wexpect.spawn('cat')
82 |
83 | # Exit by sending EOF
84 | child.sendeof()
85 |
86 | # Waiting for cmd termination.
87 | child.wait()
88 |
89 |
90 | def terminate_terminate():
91 | '''Terminate child program by sending kill signal. Some application requires sending SIGTERM to kill
92 | the child process. `terminate()` call `kill()` function, which sends SIGTERM to child process.
93 | Waiting for the terminaton is not required explicitly in this case. The wait is included in
94 | `terminate()` function.
95 | '''
96 | print('terminate_terminate')
97 |
98 | # Start cmd as child process
99 | child = wexpect.spawn('cmd.exe')
100 |
101 | # Wait for prompt when cmd becomes ready.
102 | child.expect('>')
103 |
104 | # Exit from cmd
105 | child.terminate()
106 |
107 |
108 | terminate_programmatically()
109 | terminate_eof()
110 | terminate_terminate()
111 |
--------------------------------------------------------------------------------
/doc/source/history.rst:
--------------------------------------------------------------------------------
1 | History
2 | =======
3 |
4 | Wexpect was a one-file code developed at University of Washington. There were several
5 | `copy `_ and
6 | `reference `_
7 | to this code with very few (almost none) documentation nor integration.
8 |
9 | This project fixes these limitations, with example codes, tests, and pypi integration.
10 |
11 | Refactor
12 | ^^^^^^^^
13 |
14 | The original wexpect was a monolith, one-file code, with several structural weaknesses. This leads
15 | me to rewrite the whole code. The first variant of the new structure is delivered with
16 | `v3.2.0 `_. (The default is the old variant
17 | (:code:`legacy_wexpect`) in v3.2.0. :code:`WEXPECT_SPAWN_CLASS` environment variable can choose the
18 | new-structured implementation.) Now :code:`SpawnPipe` is the default spawn class.
19 |
20 | Old vs new
21 | ^^^^^^^^^^
22 |
23 | But what is the difference between the old and new and what was the problem with the old?
24 |
25 | Generally, wexpect (both old and new) has three processes:
26 |
27 | - *host* is our original python script/program, which want to launch the child.
28 | - *console* is a process which started by the host, and launches the child. (This is a python script)
29 | - *child* is the process which want to be launched.
30 |
31 | The child and the console has a common Windows console, distict from the host.
32 |
33 | The :code:`legacy_wexpect`'s console is a thin script, almost do nothing. It initializes the Windows's
34 | console, and monitors the host and child processes. The magic is done by the host process, which has
35 | the :code:`switchTo()` and :code:`switchBack()` functions, which (de-) attaches the *child-console*
36 | Windows-console. The host manipulates the child's console directly. This direct manipulation is the
37 | main structural weakness. The following task/use-cases are hard/impossible:
38 |
39 | - thread-safe multiprocessing of the host.
40 | - logging (both console and host)
41 | - using in graphical IDE or with pytest
42 | - This variant is highly depends on the pywin32 package.
43 |
44 | The new structure's console is a thick script. The console process do the major console manipulation,
45 | which is controlled by the host via socket (see SpawnSocket) or named-pipe (SpawnPipe). The host
46 | only process the except-loops.
47 |
--------------------------------------------------------------------------------
/doc/source/index.rst:
--------------------------------------------------------------------------------
1 | Wexpect version |version|
2 | =========================
3 |
4 | .. image:: https://ci.appveyor.com/api/projects/status/tbji72d5s0tagrt9?svg=true
5 | :target: https://ci.appveyor.com/project/raczben/wexpect
6 | :align: right
7 | :alt: Build status
8 |
9 | *Wexpect* is a Windows variant of `Pexpect `_
10 | Wexpect and Pexpect makes Python a better tool for controlling other applications.
11 |
12 | Wexpect is a Python module for spawning child applications;
13 | controlling them; and responding to expected patterns in their output.
14 | Wexpect works like Don Libes' Expect. Wexpect allows your script to
15 | spawn a child application and control it as if a human were typing
16 | commands.
17 |
18 | Wexpect can be used for automating interactive applications such as
19 | ssh, ftp, passwd, telnet, etc. It can be used to a automate setup
20 | scripts for duplicating software package installations on different
21 | servers. It can be used for automated software testing.
22 | Wexpect highly depends on Mark Hammond's `pywin32 `_
23 | which provides access to many of the Windows APIs from Python.
24 |
25 | Install
26 | ^^^^^^^
27 |
28 | Wexpect is on PyPI, and can be installed with standard tools::
29 |
30 | pip install wexpect
31 |
32 | Hello Wexpect
33 | ^^^^^^^^^^^^^
34 |
35 | To interract with a child process use :code:`spawn` method:
36 |
37 | .. code-block:: python
38 |
39 | import wexpect
40 | child = wexpect.spawn('cmd.exe')
41 | child.expect('>')
42 | child.sendline('ls')
43 | child.expect('>')
44 | print(child.before)
45 | child.sendline('exit')
46 |
47 |
48 | For more information see `examples `_ folder.
49 |
50 |
51 | Contents:
52 |
53 |
54 | .. toctree::
55 | :maxdepth: 2
56 |
57 | api/index
58 | history
59 | examples
60 |
61 | Wexpect is developed `on Github `_. Please
62 | report `issues `_ there as well.
63 |
64 | Indices and tables
65 | ==================
66 |
67 | * :ref:`genindex`
68 | * :ref:`modindex`
69 | * :ref:`search`
70 |
--------------------------------------------------------------------------------
/examples/README.md:
--------------------------------------------------------------------------------
1 | # **wexpect examples**
2 |
3 | There are several example usage of wexpect. Choose one as template of your application.
4 |
5 | ## hello_wexpect
6 |
7 | [hello_wexpect](./hello_wexpect.py) is the simplest example. It starts a windows command interpreter
8 | (aka. cmd) lists the current directory and exits.
9 |
10 | ## python
11 |
12 | [python](./python.py) is a full custom example code. This example script runs [foo](./foo.py) python
13 | program, and communicates with it. For better understanding please run natively foo.py first, which
14 | is a very basic stdio handler script.
15 |
16 | ## cmd_wrapper
17 |
18 | [cmd_wrapper](./cmd_wrapper.py) is a simple wrapper around the cmd windows command interpreter. It
19 | waits for commands executes them in the spawned cmd, and prints the results.
20 |
--------------------------------------------------------------------------------
/examples/cmd_wrapper.py:
--------------------------------------------------------------------------------
1 | # A simple example code for wexpect
2 |
3 | from __future__ import print_function
4 |
5 | import sys
6 | import os
7 | import re
8 |
9 | here = os.path.dirname(os.path.abspath(__file__))
10 | wexpectPath = os.path.dirname(here)
11 |
12 | import wexpect
13 |
14 | # Path of cmd executable:
15 | cmd_exe = 'cmd'
16 | # The prompt should be more sophisticated than just a '>'.
17 | cmdPrompt = re.compile('[A-Z]\:.+>')
18 |
19 | # Start the child process
20 | p = wexpect.spawn(cmd_exe)
21 |
22 | # Wait for prompt
23 | p.expect(cmdPrompt, timeout = 5)
24 |
25 | # print the texts
26 | print(p.before, end='')
27 | print(p.match.group(0), end='')
28 |
29 |
30 | while True:
31 |
32 | # Wait and run a command.
33 | command = input()
34 | p.sendline(command)
35 |
36 | try:
37 | # Wait for prompt
38 | p.expect(cmdPrompt)
39 |
40 | # print the texts
41 | print(p.before, end='')
42 | print(p.match.group(0), end='')
43 |
44 | except wexpect.EOF:
45 | # The program has exited
46 | print('The program has exied... BY!')
47 | break
48 |
--------------------------------------------------------------------------------
/examples/foo.py:
--------------------------------------------------------------------------------
1 | '''
2 | This is is a very basic stdio handler script. This is used by python.py example.
3 | '''
4 |
5 | import time
6 |
7 | # Read an integer from the user:
8 | print('Give a small integer: ', end='')
9 | num = input()
10 |
11 | # Wait the given time
12 | for i in range(int(num)):
13 | print('waiter ' + str(i))
14 | time.sleep(0.2)
15 |
16 | # Ask the name of the user to say hello.
17 | print('Give your name: ', end='')
18 | name = input()
19 | print('Hello ' + str(name), end='')
20 |
--------------------------------------------------------------------------------
/examples/hello_wexpect.py:
--------------------------------------------------------------------------------
1 | '''
2 | This is the simplest example. It starts a windows command interpreter (aka. cmd) lists the current
3 | directory and exits.
4 | '''
5 |
6 | import wexpect
7 |
8 | # Start cmd as child process
9 | child = wexpect.spawn('cmd.exe')
10 |
11 | # Wait for prompt when cmd becomes ready.
12 | child.expect('>')
13 |
14 | # Prints the cmd's start message
15 | print(child.before, end='')
16 | print(child.after, end='')
17 |
18 | # run dir command to list directory contents
19 | child.sendline('dir')
20 |
21 | # Waiting for prompt
22 | child.expect('>')
23 |
24 | # Prints content of the directory
25 | print(child.before, end='')
26 | print(child.after, end='')
27 |
28 | # Exit from cmd
29 | child.sendline('exit')
30 |
31 | # Waiting for cmd termination.
32 | child.wait()
33 |
--------------------------------------------------------------------------------
/examples/python.py:
--------------------------------------------------------------------------------
1 | '''
2 | This example script runs foo python program, and communicates with it. For better understanding
3 | please run natively foo.py first, which is a very basic stdio handler script.
4 | '''
5 |
6 | import sys
7 | import wexpect
8 | import os
9 |
10 | here = os.path.dirname(os.path.realpath(__file__))
11 |
12 | # Path of python executable:
13 | pythonInterp = sys.executable
14 | prompt = ': '
15 |
16 | # Start the child process
17 | p = wexpect.spawn(pythonInterp, [here + '\\foo.py'])
18 |
19 | # Wait for prompt
20 | p.expect(prompt)
21 | print(p.before)
22 |
23 | # Send the 'small integer'
24 | p.sendline('3')
25 | p.expect(prompt)
26 | print(p.before)
27 |
28 | # print the texts
29 | print(p.before, end='')
30 | print(p.match.group(0), end='')
31 |
32 | # Send the name
33 | p.sendline('Bob')
34 |
35 | # wait for program exit.
36 | p.wait()
37 |
38 | # print the texts
39 | print(p.read(), end='')
40 |
--------------------------------------------------------------------------------
/examples/termination.py:
--------------------------------------------------------------------------------
1 | ''' This script shows three method to terminate your application. `terminate_programmatically()`
2 | shows the recommended way, which kills the child by sending the application specific exit command.
3 | After that waiting for the terminaton is recommended.
4 | `terminate_eof()` shows how to terminate child program by sending EOF character. Some program can be
5 | terminated by sending EOF character. Waiting for the terminaton is recommended in this case too.
6 | `terminate_terminate()` shows how to terminate child program by sending kill signal. Some
7 | application requires sending SIGTERM to kill the child process. `terminate()` call `kill()`
8 | function, which sends SIGTERM to child process. Waiting for the terminaton is not required
9 | explicitly in this case. The wait is included in `terminate()` function.
10 | '''
11 |
12 | import wexpect
13 |
14 | def terminate_programmatically():
15 | '''Terminate child program by command. This is the recommended method. Send your application's
16 | exit command to quit the child's process. After that wait for the terminaton.
17 | '''
18 | print('terminate_programmatically')
19 |
20 | # Start cmd as child process
21 | child = wexpect.spawn('cmd.exe')
22 |
23 | # Wait for prompt when cmd becomes ready.
24 | child.expect('>')
25 |
26 | # Exit from cmd
27 | child.sendline('exit')
28 |
29 | # Waiting for cmd termination.
30 | child.wait()
31 |
32 | def terminate_eof():
33 | '''Terminate child program by sending EOF character. Some program can be terminated by sending
34 | an EOF character. Waiting for the terminaton is recommended in this case too.
35 | '''
36 | print('terminate_eof')
37 |
38 | # Start cmd as child process
39 | child = wexpect.spawn('cat')
40 |
41 | # Exit from cmd
42 | child.sendeof()
43 |
44 | # Waiting for cmd termination.
45 | child.wait()
46 |
47 |
48 | def terminate_terminate():
49 | '''Terminate child program by sending kill signal. Some application requires sending SIGTERM to kill
50 | the child process. `terminate()` call `kill()` function, which sends SIGTERM to child process.
51 | Waiting for the terminaton is not required explicitly in this case. The wait is included in
52 | `terminate()` function.
53 | '''
54 | print('terminate_terminate')
55 |
56 | # Start cmd as child process
57 | child = wexpect.spawn('cmd.exe')
58 |
59 | # Wait for prompt when cmd becomes ready.
60 | child.expect('>')
61 |
62 | # Exit from cmd
63 | child.terminate()
64 |
65 |
66 | terminate_programmatically()
67 | terminate_eof()
68 | terminate_terminate()
69 |
--------------------------------------------------------------------------------
/issues/README.md:
--------------------------------------------------------------------------------
1 | issues folder contains scripts to help to reproduce a given issue
2 |
--------------------------------------------------------------------------------
/issues/bla.py:
--------------------------------------------------------------------------------
1 | import time
2 |
3 | # Wait the given time
4 | for i in range(2):
5 | print('apple\tbanana\tmelone\tcherry')
6 | print('pepper tomato\tcorn cabbage')
7 | print('apple banana\tmelone\tcherry2')
8 | print('pepper tomato\tcorn cabbage2')
9 | print('prompt> ')
10 | time.sleep(0.5)
11 |
12 |
--------------------------------------------------------------------------------
/issues/bla2.py:
--------------------------------------------------------------------------------
1 | '''
2 | This is is a very basic stdio handler script. This is used by python.py example.
3 | '''
4 |
5 | import time
6 |
7 |
8 | print('Welcome!')
9 |
10 | while True:
11 | print('>', end='')
12 | cmd = input()
13 |
14 | # Wait the given time
15 | if cmd == 'ls':
16 | print('apple\tbanana\tmelone\tcherry')
17 | print('pepper tomato\tcorn cabbage')
18 | print('apple banana\tmelone\tcherry2')
19 | print('pepper tomato\tcorn cabbage2')
20 | continue
21 | if cmd == 'exit':
22 | break
23 | else:
24 | print('unknown command')
--------------------------------------------------------------------------------
/issues/i09_cmd_error.py:
--------------------------------------------------------------------------------
1 |
2 | import wexpect
3 | import time
4 |
5 | print(wexpect.__version__)
6 |
7 |
8 | def testPath():
9 | # Path of cmd executable:
10 | cmdPath = 'cmd'
11 | cmdPrompt = '>'
12 | referenceOut = None
13 |
14 | while True:
15 | # Start the child process
16 | p = wexpect.spawn(cmdPath)
17 |
18 | # Wait for prompt
19 | p.expect(cmdPrompt)
20 |
21 | # Send a command
22 | p.sendline('ls')
23 | # time.sleep(0.6)
24 | p.expect(cmdPrompt)
25 |
26 |
27 | print(cmdPath + " >>" + p.before + '<<')
28 | if referenceOut:
29 | if referenceOut != p.before:
30 | p.interact()
31 | time.sleep(5)
32 | raise Exception()
33 | else:
34 | referenceOut = p.before
35 |
36 | testPath()
37 |
38 |
--------------------------------------------------------------------------------
/issues/i09_confused_string.py:
--------------------------------------------------------------------------------
1 | import wexpect
2 | import time
3 |
4 | print(wexpect.__version__)
5 |
6 |
7 | def testPath():
8 | # Path of cmd executable:
9 | cmdPath = 'cmd'
10 | cmdPrompt = '>'
11 | referenceOut = None
12 |
13 | while True:
14 | # Start the child process
15 | p = wexpect.spawn(cmdPath)
16 |
17 | # Wait for prompt
18 | p.expect(cmdPrompt)
19 |
20 | # Send a command
21 | p.sendline('ls')
22 | # time.sleep(0.6) <= this sleep solve...
23 | p.expect(cmdPrompt)
24 |
25 | print(cmdPath + " >>" + p.before + '<<')
26 | if referenceOut:
27 | if referenceOut != p.before:
28 | p.interact()
29 | time.sleep(5)
30 | raise Exception()
31 | else:
32 | referenceOut = p.before
33 |
34 | testPath()
35 |
--------------------------------------------------------------------------------
/issues/i09_py_sim_no_error.py:
--------------------------------------------------------------------------------
1 |
2 | import wexpect
3 | import time
4 |
5 | print(wexpect.__version__)
6 |
7 |
8 | def testPath():
9 | # Path of cmd executable:
10 | cmdPath = 'python bugfix\\bla.py'
11 | cmdPrompt = '> '
12 | referenceOut = None
13 |
14 | while True:
15 | # Start the child process
16 | p = wexpect.spawn(cmdPath)
17 |
18 | # Wait for prompt
19 | p.expect(cmdPrompt)
20 | # Wait for prompt
21 | p.expect(cmdPrompt)
22 |
23 | print(cmdPath + " >>" + p.before + '<<')
24 | if referenceOut:
25 | if referenceOut != p.before:
26 | p.interact()
27 | time.sleep(5)
28 | raise Exception()
29 | else:
30 | referenceOut = p.before
31 |
32 | testPath()
33 |
34 |
--------------------------------------------------------------------------------
/issues/i09_py_sim_no_error2.py:
--------------------------------------------------------------------------------
1 |
2 | import wexpect
3 | import time
4 |
5 | print(wexpect.__version__)
6 |
7 |
8 | def testPath():
9 | # Path of cmd executable:
10 | cmdPath = 'python bugfix\\bla2.py'
11 | cmdPrompt = '>'
12 | referenceOut = None
13 |
14 | while True:
15 | # Start the child process
16 | p = wexpect.spawn(cmdPath)
17 |
18 | # Wait for prompt
19 | p.expect(cmdPrompt)
20 |
21 | # Send a command
22 | p.sendline('ls')
23 | # time.sleep(0.6)
24 | p.expect(cmdPrompt)
25 |
26 |
27 | print(cmdPath + " >>" + p.before + '<<')
28 | if referenceOut:
29 | if referenceOut != p.before:
30 | p.interact()
31 | time.sleep(5)
32 | raise Exception()
33 | else:
34 | referenceOut = p.before
35 |
36 | testPath()
37 |
38 |
--------------------------------------------------------------------------------
/issues/i10_parent.py:
--------------------------------------------------------------------------------
1 | import wexpect
2 | import time
3 | import sys
4 | import os
5 |
6 | here = os.path.dirname(os.path.abspath(__file__))
7 | sys.path.insert(0, here)
8 |
9 | from long_printer import puskas_wiki
10 |
11 | print(wexpect.__version__)
12 |
13 |
14 | # With quotes (C:\Program Files\Python37\python.exe needs quotes)
15 | python_executable = '"' + sys.executable + '" '
16 | child_script = here + '\\long_printer.py'
17 |
18 |
19 | def main():
20 | longPrinter = python_executable + ' ' + child_script
21 | prompt = 'puskas> '
22 |
23 | # Start the child process
24 | p = wexpect.spawn(longPrinter)
25 | # Wait for prompt
26 | p.expect(prompt)
27 |
28 | try:
29 | for i in range(10):
30 | print('.', end='')
31 | p.sendline('0')
32 | p.expect(prompt)
33 | if p.before.splitlines()[1] != puskas_wiki[0]:
34 | print(p.before.splitlines()[1])
35 | raise Exception()
36 |
37 | p.sendline('all')
38 | p.expect(prompt)
39 | for a,b in zip(p.before.splitlines()[1:], puskas_wiki):
40 | if a!=b:
41 | print(a)
42 | print(b)
43 | raise Exception()
44 |
45 | for j, paragraph in enumerate(puskas_wiki):
46 | p.sendline(str(j))
47 | p.expect(prompt)
48 | if p.before.splitlines()[1] != paragraph:
49 | print(p.before.splitlines()[1])
50 | print(i)
51 | print(j)
52 | print(paragraph)
53 | raise Exception()
54 | except:
55 | p.interact()
56 | time.sleep(5)
57 | else:
58 | print('')
59 | print('[PASS]')
60 |
61 |
62 | main()
63 |
64 |
--------------------------------------------------------------------------------
/issues/i11_greek_parent.py:
--------------------------------------------------------------------------------
1 |
2 | import wexpect
3 | import time
4 | import sys
5 | import os
6 |
7 | here = os.path.dirname(os.path.abspath(__file__))
8 | sys.path.insert(0, here)
9 |
10 | import i11_greek_printer
11 |
12 | print(wexpect.__version__)
13 |
14 | # With quotes (C:\Program Files\Python37\python.exe needs quotes)
15 | python_executable = '"' + sys.executable + '" '
16 | child_script = here + '\\i11_greek_printer.py'
17 |
18 |
19 | def main():
20 | unicodePrinter = python_executable + ' ' + child_script
21 | prompt = 'give the name of a greek letter> '
22 |
23 | # Start the child process
24 | print('starting child: ' + unicodePrinter)
25 | p = wexpect.spawn(unicodePrinter)
26 | print('waiting for prompt')
27 |
28 | # Wait for prompt
29 | p.expect(prompt)
30 | print('Child prompt arrived, lets start commands!')
31 |
32 | # Send commands
33 | for letterName in i11_greek_printer.greekLetters.keys():
34 | p.sendline(letterName)
35 | p.expect(prompt)
36 | print(p.before.splitlines()[1])
37 | if i11_greek_printer.greekLetters[letterName] != p.before.splitlines()[1]:
38 | p.interact()
39 | time.sleep(5)
40 | raise Exception()
41 |
42 |
43 | main()
44 |
45 |
--------------------------------------------------------------------------------
/issues/i11_greek_printer.py:
--------------------------------------------------------------------------------
1 | greekLetters = {
2 | 'ALPHA': '\u03B1',
3 | 'BETA': '\u03B2',
4 | 'GAMMA': '\u03B3',
5 | 'DELTA': '\u03B4',
6 | 'EPSILON': '\u03B5',
7 | 'ZETA': '\u03B6',
8 | 'ETA': '\u03B7',
9 | 'THETA': '\u03B8',
10 | 'IOTA': '\u03B9',
11 | 'KAPPA': '\u03BA',
12 | 'LAMDA': '\u03BB',
13 | 'MU': '\u03BC',
14 | 'NU': '\u03BD',
15 | 'XI': '\u03BE',
16 | 'OMICRON': '\u03BF',
17 | 'PI': '\u03C0',
18 | 'RHO': '\u03C1',
19 | 'FINAL SIGMA': '\u03C2',
20 | 'SIGMA': '\u03C3',
21 | 'TAU': '\u03C4',
22 | 'UPSILON': '\u03C5',
23 | 'PHI': '\u03C6',
24 | 'CHI': '\u03C7',
25 | 'PSI': '\u03C8',
26 | 'OMEGA': '\u03C9'
27 | }
28 |
29 | def main():
30 | while True:
31 | letter = input('give the name of a greek letter> ')
32 | if letter.lower() == 'exit':
33 | print('Bye')
34 | break
35 | if letter.lower() == 'all':
36 | print(greekLetters)
37 | continue
38 | try:
39 | print(greekLetters[letter.upper()])
40 | except:
41 | print('ERROR!!! Uunknkown letter')
42 |
43 | if __name__ == '__main__':
44 | main()
45 |
--------------------------------------------------------------------------------
/issues/i11_unicode_parent.py:
--------------------------------------------------------------------------------
1 |
2 | import wexpect
3 | import time
4 | import sys
5 | import os
6 |
7 | here = os.path.dirname(os.path.abspath(__file__))
8 | sys.path.insert(0, here)
9 |
10 | import i11_unicode_printer
11 |
12 | print(wexpect.__version__)
13 |
14 | encodings = ['cp1250', 'utf-8']
15 |
16 |
17 | # With quotes (C:\Program Files\Python37\python.exe needs quotes)
18 | python_executable = '"' + sys.executable + '" '
19 | child_script = here + '\\i11_unicode_printer.py'
20 |
21 |
22 | def main():
23 | unicodePrinter = python_executable + ' ' + child_script
24 | prompt = '> '
25 |
26 | for ec in encodings:
27 | # Start the child process
28 | p = wexpect.spawn(unicodePrinter)
29 |
30 | # Wait for prompt
31 | p.expect(prompt)
32 | print('Child prompt arrived, lets set encoding!')
33 | p.sendline(ec)
34 | p.expect(prompt)
35 |
36 | # Send commands
37 | for cc in range(34, 500):
38 | p.sendline(str(cc))
39 | p.expect(prompt)
40 | print(p.before.splitlines()[1])
41 | if chr(int(cc)).encode("utf-8").decode(ec) != p.before.splitlines()[1]:
42 | p.interact()
43 | time.sleep(5)
44 | raise Exception()
45 |
46 |
47 | main()
48 |
49 |
--------------------------------------------------------------------------------
/issues/i11_unicode_printer.py:
--------------------------------------------------------------------------------
1 |
2 |
3 | def main():
4 | encoding = "utf-8"
5 | encoding = input('give the encoding> ')
6 | while True:
7 | code = input('give a number> ')
8 | if code.lower() == 'exit':
9 | print('Bye')
10 | break
11 | try:
12 | print(chr(int(code)).encode("utf-8").decode(encoding) )
13 | # code_int = int(code)
14 | # print(greekLetters[letter.upper()])
15 | except:
16 | print('ERROR!!!')
17 | raise
18 |
19 | if __name__ == '__main__':
20 | main()
21 |
--------------------------------------------------------------------------------
/issues/i18_setecho_error.py:
--------------------------------------------------------------------------------
1 | import wexpect
2 | import time
3 | import os
4 |
5 | print(wexpect.__version__)
6 |
7 | def test_setecho():
8 | # Path of cmd executable:
9 | cmdPath = 'cmd'
10 | cmdPrompt = '>'
11 | referenceOut = None
12 |
13 | # Start the child process
14 | p = wexpect.spawn(cmdPath)
15 | p.expect(cmdPrompt)
16 |
17 | p.setecho(0) # SETECHO
18 |
19 | p.interact()
20 | for c in 'echo Hello':
21 | p.send(c)
22 | time.sleep(0.2)
23 | p.send(os.linesep)
24 |
25 | time.sleep(2)
26 | p.stop_interact()
27 | p.sendline('exit')
28 |
29 |
30 | # Start the child process
31 | p = wexpect.spawn(cmdPath)
32 | p.expect(cmdPrompt)
33 |
34 | p.setecho(1) # SETECHO
35 |
36 | p.interact()
37 | for c in 'echo Hello':
38 | p.send(c)
39 | time.sleep(0.2)
40 | p.send(os.linesep)
41 |
42 | time.sleep(2)
43 | p.stop_interact()
44 | p.sendline('exit')
45 |
46 |
47 | test_setecho()
48 |
--------------------------------------------------------------------------------
/issues/long_printer.py:
--------------------------------------------------------------------------------
1 | '''
2 | This is is a very basic stdio handler script. This is used by python.py example.
3 | '''
4 |
5 | import time
6 |
7 | puskas_wiki = ['''Ferenc Puskas was a Hungarian footballer and manager, widely regarded as one of \
8 | the greatest players of all time. He is the son of former footballer Ferenc Puskas Senior. A \
9 | prolific forward, he scored 84 goals in 85 international matches for Hungary, played 4 \
10 | international matches for Spain and scored 514 goals in 529 matches in the Hungarian and Spanish \
11 | leagues. He became an Olympic champion in 1952 and led his nation to the final of the 1954 World \
12 | Cup where he was named the tournament's best player. He won three European Cups (1959, 1960, 1966),\
13 | 10 national championships (5 Hungarian and 5 Spanish Primera Division) and 8 top individual \
14 | scoring honors. In 1995, he was recognized as the top scorer of the 20th century by the IFFHS.''',
15 | '''Puskas started his career in Hungary playing for Kispest and Budapest Honved. He was the top scorer\
16 | in the Hungarian League on four occasions, and in 1948, he was the top goal scorer in Europe. \
17 | During the 1950s, he was both a prominent member and captain of the Hungarian national team, known\
18 | as the Mighty Magyars. In 1958, two years after the Hungarian Revolution, he emigrated to Spain \
19 | where he played for Real Madrid. While playing with Real Madrid, Puskas won four Pichichis and \
20 | scored seven goals in two European Champions Cup finals.''',
21 | '''After retiring as a player, he became a coach. The highlight of his coaching career came in 1971 \
22 | when he guided Panathinaikos to the European Cup final, where they lost 2-0 to AFC Ajax. In 1993, \
23 | he returned to Hungary and took temporary charge of the Hungarian national team. In 1998, he \
24 | became one of the first ever FIFA/SOS Charity ambassadors. In 2002, the Nepstadion in Budapest \
25 | was renamed the Puskas Ferenc Stadion in his honor. He was also declared the best Hungarian \
26 | player of the last 50 years by the Hungarian Football Federation in the UEFA Jubilee Awards in \
27 | November 2003. In October 2009, FIFA announced the introduction of the FIFA Puskas Award, \
28 | awarded to the player who has scored the "most beautiful goal" over the past year. He was also \
29 | listed in Pele's FIFA 100.''',
30 | '''Ferenc Purczeld was born on 2 April 1927 to a German (Danube Swabian) family in Budapest and \
31 | brought up in Kispest, then a suburb, today part of the city. His mother, Margit Biro \
32 | (1904-1976), was a seamstress. He began his career as a junior with Kispest AC,[10] where his \
33 | father, who had previously played for the club, was a coach. He had grandchildren, who were the \
34 | children of his brothers son[clarification needed]; the two sons of his brother are Zoltan and \
35 | Istvan, the first one have 3 children; Ilonka, Camila and Andres, and the second one have two.''',
36 | '''He changed his name to Puskas. He initially used the pseudonym "Miklos Kovacs" to help \
37 | circumvent the minimum age rules[12] before officially signing at the age of 12. Among his early \
38 | teammates was his childhood friend and future international teammate Jozsef Bozsik. He made his \
39 | first senior appearance for Kispest in November 1943 in a match against Nagyvaradi AC.[13] It was \
40 | here where he got the nickname "Ocsi" or "Buddy".[14]''',
41 | '''Kispest was taken over by the Hungarian Ministry of Defence in 1949, becoming the Hungarian Army \
42 | team and changing its name to Budapest Honved. As a result, football players were given military \
43 | ranks. Puskas eventually became a major (Hungarian: Ornagy), which led to the nickname "The \
44 | Galloping Major".[15] As the army club, Honved used conscription to acquire the best Hungarian \
45 | players, leading to the recruitment of Zoltan Czibor and Sandor Kocsis.[16] During his career at \
46 | Budapest Honved, Puskas helped the club win five Hungarian League titles. He also finished as top \
47 | goal scorer in the league in 1947-48, 1949-50, 1950 and 1953, scoring 50, 31, 25 and 27 goals, \
48 | respectively. In 1948, he was the top goal scorer in Europe.[17]''' ]
49 |
50 | def main():
51 | print('Welcome!')
52 |
53 | while True:
54 | print('puskas> ', end='')
55 | num = input()
56 |
57 | if num == 'exit':
58 | break
59 | if num == 'all':
60 | print('\r\n'.join(puskas_wiki))
61 | continue
62 | try:
63 | if int(num) in range(len(puskas_wiki)):
64 | print(puskas_wiki[int(num)])
65 | continue
66 | except:
67 | pass
68 | print('unknown command')
69 |
70 | if __name__ == '__main__':
71 | main()
72 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | pywin32>=220
2 | psutil>=5.0.0
3 |
--------------------------------------------------------------------------------
/setup.cfg:
--------------------------------------------------------------------------------
1 | [metadata]
2 | name = wexpect
3 | author = Noah Spurrier, Richard Holden, Marco Molteni, Kimberley Burchett, Robert Stone, Hartmut Goebel, Chad Schroeder, Erick Tryzelaar, Dave Kirby, Ids vander Molen, George Todd, Noel Taylor, Nicolas D. Cesar, Alexander Gattin, Geoffrey Marshall, Francisco Lourenco, Glen Mabey, Karthik Gurusamy, Fernando Perez, Corey Minyard, Jon Cohen, Guillaume Chazarain, Andrew Ryan, Nick Craig-Wood, Andrew Stone, Jorgen Grahn, Benedek Racz
4 | author-email = betontalpfa@gmail.com
5 | summary = Windows alternative of pexpect
6 | description-file = README.md
7 | long-description-content-type = text/markdown
8 | requires-python = >=3.4
9 | project_urls =
10 | Source Code = https://github.com/raczben/wexpect
11 | license = MIT
12 | classifier =
13 | Development Status :: 4 - Beta
14 | Environment :: Console
15 | Intended Audience :: Developers
16 | Intended Audience :: Information Technology
17 | Operating System :: Microsoft :: Windows
18 | Programming Language :: Python :: 3.3
19 | Programming Language :: Python :: 3.4
20 | Programming Language :: Python :: 3.5
21 | Programming Language :: Python :: 3.6
22 | Programming Language :: Python :: 3.7
23 | keywords =
24 | scripting, automation, expect, pexpect, wexpect
25 |
26 | [options.extras_require]
27 | test =
28 | coverage
29 | tox
30 | setuptools>=38.0
31 | codecov
32 | twine
33 | pyinstaller
34 |
35 | [flake8]
36 | max-line-length = 100
37 | ignore =
38 | # E402: Module level import not at top of the file
39 | E402
40 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | from setuptools import setup
4 |
5 | setup(
6 | setup_requires=['pbr'],
7 | pbr=True,
8 |
9 | # packages=[''],
10 | py_modules=['wexpect'],
11 | )
--------------------------------------------------------------------------------
/tests/PexpectTestCase.py:
--------------------------------------------------------------------------------
1 | from __future__ import print_function
2 |
3 | import contextlib
4 | import unittest
5 | import signal
6 | import sys
7 | import os
8 |
9 |
10 | class PexpectTestCase(unittest.TestCase):
11 | def setUp(self):
12 | self.PYTHONBIN = sys.executable
13 | self.original_path = os.getcwd()
14 | tests_dir = os.path.dirname(__file__)
15 | self.project_dir = project_dir = os.path.dirname(tests_dir)
16 |
17 | # all tests are executed in this folder; there are many auxiliary
18 | # programs in this folder executed by spawn().
19 | os.chdir(tests_dir)
20 |
21 | # If the pexpect raises an exception after fork(), but before
22 | # exec(), our test runner *also* forks. We prevent this by
23 | # storing our pid and asserting equality on tearDown.
24 | self.pid = os.getpid()
25 |
26 | coverage_rc = os.path.join(project_dir, '.coveragerc')
27 | os.environ['COVERAGE_PROCESS_START'] = coverage_rc
28 | os.environ['COVERAGE_FILE'] = os.path.join(project_dir, '.coverage')
29 | print('\n', self.id(), end=' ')
30 | sys.stdout.flush()
31 |
32 | # some build agents will ignore SIGHUP and SIGINT, which python
33 | # inherits. This causes some of the tests related to terminate()
34 | # to fail. We set them to the default handlers that they should
35 | # be, and restore them back to their SIG_IGN value on tearDown.
36 | #
37 | # I'm not entirely convinced they need to be restored, only our
38 | # test runner is affected.
39 | self.restore_ignored_signals = [
40 | value for value in (signal.SIGINT,)
41 | if signal.getsignal(value) == signal.SIG_IGN]
42 | if signal.SIGINT in self.restore_ignored_signals:
43 | # SIGINT should be set to signal.default_int_handler
44 | signal.signal(signal.SIGINT, signal.default_int_handler)
45 | unittest.TestCase.setUp(self)
46 |
47 | def tearDown(self):
48 | # restore original working folder
49 | os.chdir(self.original_path)
50 |
51 | if self.pid != os.getpid():
52 | # The build server pattern-matches phrase 'Test runner has forked!'
53 | print("Test runner has forked! This means a child process raised "
54 | "an exception before exec() in a test case, the error is "
55 | "more than likely found above this line in stderr.",
56 | file=sys.stderr)
57 | exit(1)
58 |
59 | # restore signal handlers
60 | for signal_value in self.restore_ignored_signals:
61 | signal.signal(signal_value, signal.SIG_IGN)
62 |
63 | if sys.version_info < (2, 7):
64 | # We want to use these methods, which are new/improved in 2.7, but
65 | # we are still supporting 2.6 for the moment. This section can be
66 | # removed when we drop Python 2.6 support.
67 | @contextlib.contextmanager
68 | def assertRaises(self, excClass):
69 | try:
70 | yield
71 | except Exception as e:
72 | assert isinstance(e, excClass)
73 | else:
74 | raise AssertionError("%s was not raised" % excClass)
75 |
76 | @contextlib.contextmanager
77 | def assertRaisesRegexp(self, excClass, pattern):
78 | import re
79 | try:
80 | yield
81 | except Exception as e:
82 | assert isinstance(e, excClass)
83 | assert re.match(pattern, str(e))
84 | else:
85 | raise AssertionError("%s was not raised" % excClass)
86 |
--------------------------------------------------------------------------------
/tests/__init__.py:
--------------------------------------------------------------------------------
1 | # __init__.py
2 | # The mere presence of this file makes the dir a package.
3 | pass
4 |
--------------------------------------------------------------------------------
/tests/echo_w_prompt.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | from __future__ import print_function
3 |
4 | try:
5 | raw_input
6 | except NameError:
7 | raw_input = input
8 |
9 | while True:
10 | try:
11 | a = raw_input('')
12 | except EOFError:
13 | print('')
14 | break
15 | print('', a, sep='')
16 |
--------------------------------------------------------------------------------
/tests/exit1.py:
--------------------------------------------------------------------------------
1 | import os, sys
2 |
3 | print("Hello")
4 | sys.stdout.flush()
5 | os._exit(1)
6 |
--------------------------------------------------------------------------------
/tests/foo.py:
--------------------------------------------------------------------------------
1 | '''
2 | This is is a very basic stdio handler script. This is used by python.py example.
3 | '''
4 |
5 | import time
6 |
7 | # Read an integer from the user:
8 | print('Give a small integer: ', end='')
9 | num = input()
10 |
11 | # Wait the given time
12 | for i in range(int(num)):
13 | print('waiter ' + str(i))
14 | time.sleep(0.2)
15 |
16 | # Ask the name of the user to say hello.
17 | print('Give your name: ', end='')
18 | name = input()
19 | print('Hello ' + str(name), end='')
20 |
--------------------------------------------------------------------------------
/tests/lines_printer.py:
--------------------------------------------------------------------------------
1 | '''
2 | This is is a very basic stdio handler script. This is used by python.py example.
3 | '''
4 |
5 | import time
6 |
7 | # Read an integer from the user:
8 | print('Give a small integer: ', end='')
9 | num = input()
10 |
11 | # Wait the given time
12 | for i in range(int(num)):
13 | print('waiter ' + str(i))
14 | time.sleep(0.2)
15 | print('Bye!')
--------------------------------------------------------------------------------
/tests/list100.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | print(list(range(100)))
3 |
--------------------------------------------------------------------------------
/tests/long_printer.py:
--------------------------------------------------------------------------------
1 | '''
2 | This is is a very basic stdio handler script. This is used by python.py example.
3 | '''
4 |
5 | import time
6 |
7 | puskas_wiki = ['''Ferenc Puskas was a Hungarian footballer and manager, widely regarded as one of \
8 | the greatest players of all time. He is the son of former footballer Ferenc Puskas Senior. A \
9 | prolific forward, he scored 84 goals in 85 international matches for Hungary, played 4 \
10 | international matches for Spain and scored 514 goals in 529 matches in the Hungarian and Spanish \
11 | leagues. He became an Olympic champion in 1952 and led his nation to the final of the 1954 World \
12 | Cup where he was named the tournament's best player. He won three European Cups (1959, 1960, 1966),\
13 | 10 national championships (5 Hungarian and 5 Spanish Primera Division) and 8 top individual \
14 | scoring honors. In 1995, he was recognized as the top scorer of the 20th century by the IFFHS.''',
15 | '''Puskas started his career in Hungary playing for Kispest and Budapest Honved. He was the top scorer\
16 | in the Hungarian League on four occasions, and in 1948, he was the top goal scorer in Europe. \
17 | During the 1950s, he was both a prominent member and captain of the Hungarian national team, known\
18 | as the Mighty Magyars. In 1958, two years after the Hungarian Revolution, he emigrated to Spain \
19 | where he played for Real Madrid. While playing with Real Madrid, Puskas won four Pichichis and \
20 | scored seven goals in two European Champions Cup finals.''',
21 | '''After retiring as a player, he became a coach. The highlight of his coaching career came in 1971 \
22 | when he guided Panathinaikos to the European Cup final, where they lost 2-0 to AFC Ajax. In 1993, \
23 | he returned to Hungary and took temporary charge of the Hungarian national team. In 1998, he \
24 | became one of the first ever FIFA/SOS Charity ambassadors. In 2002, the Nepstadion in Budapest \
25 | was renamed the Puskas Ferenc Stadion in his honor. He was also declared the best Hungarian \
26 | player of the last 50 years by the Hungarian Football Federation in the UEFA Jubilee Awards in \
27 | November 2003. In October 2009, FIFA announced the introduction of the FIFA Puskas Award, \
28 | awarded to the player who has scored the "most beautiful goal" over the past year. He was also \
29 | listed in Pele's FIFA 100.''',
30 | '''Ferenc Purczeld was born on 2 April 1927 to a German (Danube Swabian) family in Budapest and \
31 | brought up in Kispest, then a suburb, today part of the city. His mother, Margit Biro \
32 | (1904-1976), was a seamstress. He began his career as a junior with Kispest AC,[10] where his \
33 | father, who had previously played for the club, was a coach. He had grandchildren, who were the \
34 | children of his brothers son[clarification needed]; the two sons of his brother are Zoltan and \
35 | Istvan, the first one have 3 children; Ilonka, Camila and Andres, and the second one have two.''',
36 | '''He changed his name to Puskas. He initially used the pseudonym "Miklos Kovacs" to help \
37 | circumvent the minimum age rules[12] before officially signing at the age of 12. Among his early \
38 | teammates was his childhood friend and future international teammate Jozsef Bozsik. He made his \
39 | first senior appearance for Kispest in November 1943 in a match against Nagyvaradi AC.[13] It was \
40 | here where he got the nickname "Ocsi" or "Buddy".[14]''',
41 | '''Kispest was taken over by the Hungarian Ministry of Defence in 1949, becoming the Hungarian Army \
42 | team and changing its name to Budapest Honved. As a result, football players were given military \
43 | ranks. Puskas eventually became a major (Hungarian: Ornagy), which led to the nickname "The \
44 | Galloping Major".[15] As the army club, Honved used conscription to acquire the best Hungarian \
45 | players, leading to the recruitment of Zoltan Czibor and Sandor Kocsis.[16] During his career at \
46 | Budapest Honved, Puskas helped the club win five Hungarian League titles. He also finished as top \
47 | goal scorer in the league in 1947-48, 1949-50, 1950 and 1953, scoring 50, 31, 25 and 27 goals, \
48 | respectively. In 1948, he was the top goal scorer in Europe.[17]''' ]
49 |
50 | def main():
51 | print('Welcome!')
52 |
53 | while True:
54 | print('puskas> ', end='')
55 | num = input()
56 |
57 | if num == 'exit':
58 | break
59 | if num == 'all':
60 | print('\r\n'.join(puskas_wiki))
61 | continue
62 | try:
63 | if int(num) in range(len(puskas_wiki)):
64 | print(puskas_wiki[int(num)])
65 | continue
66 | except:
67 | pass
68 | print('unknown command')
69 |
70 | if __name__ == '__main__':
71 | main()
72 |
--------------------------------------------------------------------------------
/tests/needs_kill.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | """This script can only be killed by SIGKILL."""
3 | import signal, time
4 |
5 | # Ignore interrupt, hangup and continue signals - only SIGKILL will work
6 | signal.signal(signal.SIGINT, signal.SIG_IGN)
7 |
8 | print('READY')
9 | while True:
10 | time.sleep(10)
11 |
--------------------------------------------------------------------------------
/tests/parametric_printer.py:
--------------------------------------------------------------------------------
1 | '''
2 | This is is a very basic stdio handler script. This is used by python.py example.
3 | '''
4 |
5 | import time
6 | import sys
7 |
8 | # Read an integer from the user:
9 |
10 | while True:
11 | print('Format:character,character_count,line_count,speed_ms> ', end='')
12 | command = input()
13 | (character,character_count,line_count,speed_ms) = command.split(',')
14 | character_count = int(character_count)
15 | speed_ms = int(speed_ms)
16 | line_count = int(line_count)
17 |
18 | if line_count<1:
19 | sys.exit(0)
20 | for _ in range(line_count):
21 | if speed_ms<0:
22 | print(character*character_count)
23 | sys.stdout.flush()
24 | else:
25 | for i in range(character_count):
26 | if i == character_count-1:
27 | print(character)
28 | sys.stdout.flush()
29 | else:
30 | print(character, end='')
31 | sys.stdout.flush()
32 | time.sleep(speed_ms/1000)
33 |
--------------------------------------------------------------------------------
/tests/pyinstaller_test.py:
--------------------------------------------------------------------------------
1 | import unittest
2 | import sys
3 | import re
4 | import os
5 | import time
6 |
7 | import wexpect
8 |
9 | # the program cat(1) may display ^D\x08\x08 when \x04 (EOF, Ctrl-D) is sent
10 | _CAT_EOF = '^D\x08\x08'
11 |
12 | def _u(s):
13 | return s
14 |
15 | class TestCaseMisc(unittest.TestCase):
16 |
17 | def test_isatty(self):
18 | " Test isatty() is True after spawning process on most platforms. "
19 | child = wexpect.spawn('cat')
20 | if not child.isatty() and sys.platform.lower().startswith('sunos'):
21 | if hasattr(unittest, 'SkipTest'):
22 | raise unittest.SkipTest("Not supported on this platform.")
23 | return 'skip'
24 | assert child.isatty()
25 |
26 | def test_read(self):
27 | " Test spawn.read by calls of various size. "
28 | child = wexpect.spawn('cat')
29 | child.sendline("abc")
30 | child.sendeof()
31 | child.readline()
32 | self.assertEqual(child.read(0), '')
33 | self.assertEqual(child.read(1), 'a')
34 | self.assertEqual(child.read(1), 'b')
35 | self.assertEqual(child.read(1), 'c')
36 | self.assertEqual(child.read(2), '\r\n')
37 |
38 | def test_readline_bin_echo(self):
39 | " Test spawn('echo'). "
40 | # given,
41 | child = wexpect.spawn('echo', ['alpha', 'beta'])
42 |
43 | # exercise,
44 | self.assertEqual(child.readline(), 'alpha beta\r\n')
45 |
46 | def test_readline(self):
47 | " Test spawn.readline(). "
48 | # when argument 0 is sent, nothing is returned.
49 | # Otherwise the argument value is meaningless.
50 | child = wexpect.spawn('cat')
51 | child.sendline("alpha")
52 | child.sendline("beta")
53 | child.sendline("gamma")
54 | child.sendline("delta")
55 | child.sendeof()
56 | self.assertEqual(child.readline(0), '')
57 | child.readline().rstrip()
58 | self.assertEqual(child.readline().rstrip(), 'alpha')
59 | child.readline().rstrip()
60 | self.assertEqual(child.readline(1).rstrip(), 'beta')
61 | child.readline().rstrip()
62 | self.assertEqual(child.readline(2).rstrip(), 'gamma')
63 | child.readline().rstrip()
64 | self.assertEqual(child.readline().rstrip(), 'delta')
65 | child.expect(wexpect.EOF)
66 | if type(child).__name__ in ['SpawnPipe', 'SpawnSocket']:
67 | time.sleep(child.delayafterterminate)
68 | assert not child.isalive(trust_console=False)
69 | else:
70 | assert not child.isalive()
71 | self.assertEqual(child.exitstatus, 0)
72 |
73 | def test_iter(self):
74 | " iterating over lines of spawn.__iter__(). "
75 | child = wexpect.spawn('echo "abc\r\n123"')
76 | # Don't use ''.join() because we want to test __iter__().
77 | page = ''
78 | for line in child:
79 | page += line
80 | page = page.replace(_CAT_EOF, '')
81 | self.assertEqual(page, 'abc\r\n123\r\n')
82 |
83 | def test_readlines(self):
84 | " reading all lines of spawn.readlines(). "
85 | child = wexpect.spawn('cat', echo=False)
86 | child.sendline("abc")
87 | child.sendline("123")
88 | child.sendeof()
89 | page = ''.join(child.readlines()).replace(_CAT_EOF, '')
90 | self.assertEqual(page, '\r\nabc\r\n\r\n123\r\n')
91 | child.expect(wexpect.EOF)
92 | if type(child).__name__ in ['SpawnPipe', 'SpawnSocket']:
93 | time.sleep(child.delayafterterminate)
94 | assert not child.isalive(trust_console=False)
95 | else:
96 | assert not child.isalive()
97 | self.assertEqual(child.exitstatus, 0)
98 |
99 | def test_write(self):
100 | " write a character and return it in return. "
101 | child = wexpect.spawn('cat')
102 | child.write('a')
103 | child.write('\r')
104 | child.readline()
105 | self.assertEqual(child.readline(), 'a\r\n')
106 |
107 | def test_writelines(self):
108 | " spawn.writelines() "
109 | child = wexpect.spawn('cat')
110 | # notice that much like file.writelines, we do not delimit by newline
111 | # -- it is equivalent to calling write(''.join([args,]))
112 | child.writelines(['abc', '123', 'xyz', '\r'])
113 | child.sendeof()
114 | child.readline()
115 | line = child.readline()
116 | self.assertEqual(line, 'abc123xyz\r\n')
117 |
118 | def test_eof(self):
119 | " call to expect() after EOF is received raises wexpect.EOF "
120 | child = wexpect.spawn('cat')
121 | child.sendeof()
122 | with self.assertRaises(wexpect.EOF):
123 | child.expect('the unexpected')
124 |
125 | def test_terminate(self):
126 | " test force terminate always succeeds (SIGKILL). "
127 | child = wexpect.spawn('cat')
128 | child.terminate(force=1)
129 | assert child.terminated
130 |
131 | def test_bad_arguments_suggest_fdpsawn(self):
132 | " assert custom exception for spawn(int). "
133 | expect_errmsg = "maybe you want to use fdpexpect.fdspawn"
134 | with self.assertRaisesRegex(wexpect.ExceptionPexpect,
135 | ".*" + expect_errmsg):
136 | wexpect.spawn(1)
137 |
138 | def test_bad_arguments_second_arg_is_list(self):
139 | " Second argument to spawn, if used, must be only a list."
140 | with self.assertRaises(TypeError):
141 | wexpect.spawn('ls', '-la')
142 |
143 | with self.assertRaises(TypeError):
144 | # not even a tuple,
145 | wexpect.spawn('ls', ('-la',))
146 |
147 | def test_read_after_close_raises_value_error(self):
148 | " Calling read_nonblocking after close raises ValueError. "
149 | # as read_nonblocking underlies all other calls to read,
150 | # ValueError should be thrown for all forms of read.
151 | with self.assertRaises(ValueError):
152 | p = wexpect.spawn('cat')
153 | p.close()
154 | p.read_nonblocking()
155 |
156 | with self.assertRaises(ValueError):
157 | p = wexpect.spawn('cat')
158 | p.close()
159 | p.read()
160 |
161 | with self.assertRaises(ValueError):
162 | p = wexpect.spawn('cat')
163 | p.close()
164 | p.readline()
165 |
166 | with self.assertRaises(ValueError):
167 | p = wexpect.spawn('cat')
168 | p.close()
169 | p.readlines()
170 |
171 | def test_isalive(self):
172 | " check isalive() before and after EOF. (True, False) "
173 | child = wexpect.spawn('cat')
174 | self.assertTrue(child.isalive())
175 | child.sendeof()
176 | child.expect(wexpect.EOF)
177 | assert child.isalive() is False
178 | self.assertFalse(child.isalive())
179 |
180 | def test_bad_type_in_expect(self):
181 | " expect() does not accept dictionary arguments. "
182 | child = wexpect.spawn('cat')
183 | with self.assertRaises(TypeError):
184 | child.expect({})
185 |
186 | def test_cwd(self):
187 | " check keyword argument `cwd=' of wexpect.run() "
188 | try:
189 | os.mkdir('cwd_tmp')
190 | except:
191 | pass
192 | tmp_dir = os.path.realpath('cwd_tmp')
193 | child = wexpect.spawn('cmd')
194 | child.expect('>')
195 | child.sendline('cd')
196 | child.expect('>')
197 | default = child.before.splitlines()[1]
198 | child.terminate()
199 | child = wexpect.spawn('cmd', cwd=tmp_dir)
200 | child.expect('>')
201 | child.sendline('cd')
202 | child.expect('>')
203 | pwd_tmp = child.before.splitlines()[1]
204 | child.terminate()
205 | self.assertNotEqual(default, pwd_tmp)
206 | self.assertEqual(tmp_dir, _u(pwd_tmp))
207 |
208 | def _test_searcher_as(self, searcher, plus=None):
209 | # given,
210 | given_words = ['alpha', 'beta', 'gamma', 'delta', ]
211 | given_search = given_words
212 | if searcher == wexpect.searcher_re:
213 | given_search = [re.compile(word) for word in given_words]
214 | if plus is not None:
215 | given_search = given_search + [plus]
216 | search_string = searcher(given_search)
217 | basic_fmt = '\n {0}: {1}'
218 | fmt = basic_fmt
219 | if searcher is wexpect.searcher_re:
220 | fmt = '\n {0}: re.compile({1})'
221 | expected_output = '{0}:'.format(searcher.__name__)
222 | idx = 0
223 | for word in given_words:
224 | expected_output += fmt.format(idx, '"{0}"'.format(word))
225 | idx += 1
226 | if plus is not None:
227 | if plus == wexpect.EOF:
228 | expected_output += basic_fmt.format(idx, 'EOF')
229 | elif plus == wexpect.TIMEOUT:
230 | expected_output += basic_fmt.format(idx, 'TIMEOUT')
231 |
232 | # exercise,
233 | self.assertEqual(search_string.__str__(), expected_output)
234 |
235 | def test_searcher_as_string(self):
236 | " check searcher_string(..).__str__() "
237 | self._test_searcher_as(wexpect.searcher_string)
238 |
239 | def test_searcher_as_string_with_EOF(self):
240 | " check searcher_string(..).__str__() that includes EOF "
241 | self._test_searcher_as(wexpect.searcher_string, plus=wexpect.EOF)
242 |
243 | def test_searcher_as_string_with_TIMEOUT(self):
244 | " check searcher_string(..).__str__() that includes TIMEOUT "
245 | self._test_searcher_as(wexpect.searcher_string, plus=wexpect.TIMEOUT)
246 |
247 | def test_searcher_re_as_string(self):
248 | " check searcher_re(..).__str__() "
249 | self._test_searcher_as(wexpect.searcher_re)
250 |
251 | def test_searcher_re_as_string_with_EOF(self):
252 | " check searcher_re(..).__str__() that includes EOF "
253 | self._test_searcher_as(wexpect.searcher_re, plus=wexpect.EOF)
254 |
255 | def test_searcher_re_as_string_with_TIMEOUT(self):
256 | " check searcher_re(..).__str__() that includes TIMEOUT "
257 | self._test_searcher_as(wexpect.searcher_re, plus=wexpect.TIMEOUT)
258 |
259 | def test_exception_tb(self):
260 | " test get_trace() filters away wexpect/__init__.py calls. "
261 | p = wexpect.spawn('sleep 1')
262 | try:
263 | p.expect('BLAH')
264 | except wexpect.ExceptionPexpect as e:
265 | # get_trace should filter out frames in wexpect's own code
266 | tb = e.get_trace()
267 | # exercise,
268 | assert 'raise ' not in tb
269 | assert 'wexpect/__init__.py' not in tb
270 | else:
271 | assert False, "Should have raised an exception."
272 |
273 | if __name__ == '__main__':
274 | unittest.main()
275 |
276 | suite = unittest.makeSuite(TestCaseMisc,'test')
277 |
--------------------------------------------------------------------------------
/tests/test_command_list_split.py:
--------------------------------------------------------------------------------
1 | import wexpect
2 | import unittest
3 | from tests import PexpectTestCase
4 |
5 | class TestCaseSplitCommandLine(PexpectTestCase.PexpectTestCase):
6 | def test_split_sizes(self):
7 | self.assertEqual(len(wexpect.split_command_line(r'')), 0)
8 | self.assertEqual(len(wexpect.split_command_line(r'one')), 1)
9 | self.assertEqual(len(wexpect.split_command_line(r'one two')), 2)
10 | self.assertEqual(len(wexpect.split_command_line(r'one two')), 2)
11 | self.assertEqual(len(wexpect.split_command_line(r'one two')), 2)
12 | self.assertEqual(len(wexpect.split_command_line(r'one^ one')), 1)
13 | self.assertEqual(len(wexpect.split_command_line('\'one one\'')), 1)
14 | self.assertEqual(len(wexpect.split_command_line(r'one\"one')), 1)
15 | self.assertEqual(len(wexpect.split_command_line(r"This^' is a^'^ test")), 3)
16 |
17 | def test_join_args(self):
18 | cmd = 'foo bar "b a z"'
19 | cmd2 = wexpect.join_args(wexpect.split_command_line(cmd))
20 | self.assertEqual(cmd2, cmd)
21 |
22 | cmd = ['foo', 'bar', 'b a z']
23 | cmd2 = wexpect.split_command_line(wexpect.join_args(cmd))
24 | self.assertEqual(cmd2, cmd)
25 |
26 | if __name__ == '__main__':
27 | unittest.main()
28 |
29 | suite = unittest.makeSuite(TestCaseSplitCommandLine,'test')
30 |
--------------------------------------------------------------------------------
/tests/test_console_reader.py:
--------------------------------------------------------------------------------
1 | import wexpect
2 | import time
3 | import unittest
4 | import win32process
5 | import win32api
6 | import os
7 | from tests import PexpectTestCase
8 |
9 |
10 | if "RUN_CONSOLE_READER_TEST" not in os.environ:
11 | skip = True
12 |
13 | class ConsoleReaderTestCase(PexpectTestCase.PexpectTestCase):
14 |
15 | @unittest.skipIf(skip, "Skipping test")
16 | def test_console_reader(self):
17 |
18 |
19 | pid = win32process.GetCurrentProcessId()
20 | tid = win32api.GetCurrentThreadId()
21 | args = ['sleep', '1']
22 |
23 | with self.assertRaises(SystemExit):
24 | wexpect.ConsoleReader(wexpect.join_args(args), tid=tid, pid=pid, cp=1250, logdir='wexpect')
25 |
26 | os.system('cls')
27 |
28 | if __name__ == '__main__':
29 | unittest.main()
30 |
31 | suite = unittest.makeSuite(ConsoleReaderTestCase,'test')
32 |
--------------------------------------------------------------------------------
/tests/test_constructor.py:
--------------------------------------------------------------------------------
1 | import wexpect
2 | import unittest
3 | import time
4 | from tests import PexpectTestCase
5 |
6 | class TestCaseConstructor(PexpectTestCase.PexpectTestCase):
7 | def test_constructor (self):
8 | '''This tests that the constructor will work and give
9 | the same results for different styles of invoking __init__().
10 | This assumes that the root directory / is static during the test.
11 | '''
12 | p1 = wexpect.spawn('uname -m -n -p -r -s -v', timeout=5)
13 | p1.expect(wexpect.EOF)
14 | time.sleep(p1.delayafterterminate)
15 | p2 = wexpect.spawn('uname', ['-m', '-n', '-p', '-r', '-s', '-v'], timeout=5)
16 | p2.expect(wexpect.EOF)
17 | time.sleep(p1.delayafterterminate)
18 | self.assertEqual(p1.before, p2.before)
19 | self.assertEqual(str(p1).splitlines()[1:9], str(p2).splitlines()[1:9])
20 |
21 | run_result = wexpect.run('uname -m -n -p -r -s -v')
22 | self.assertEqual(run_result, p2.before)
23 |
24 | def test_named_parameters (self):
25 | '''This tests that named parameters work.
26 | '''
27 | p = wexpect.spawn('ls',timeout=10)
28 | p.wait()
29 | p = wexpect.spawn(timeout=10, command='ls')
30 | p.wait()
31 | p = wexpect.spawn(args=[], command='ls')
32 | p.wait()
33 |
34 | if __name__ == '__main__':
35 | unittest.main()
36 |
37 | suite = unittest.makeSuite(TestCaseConstructor,'test')
38 |
--------------------------------------------------------------------------------
/tests/test_delay.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | from tests import PexpectTestCase
4 | import wexpect
5 |
6 |
7 | class TestCaseDelay(PexpectTestCase.PexpectTestCase):
8 | """
9 | Tests for various delay attributes.
10 | """
11 | def test_delaybeforesend(self):
12 | """
13 | Test various values for delaybeforesend.
14 | """
15 | p = wexpect.spawn("cat")
16 |
17 | p.delaybeforesend = 1
18 | p.sendline("line 1")
19 | p.expect("line 1")
20 |
21 | p.delaybeforesend = 0.0
22 | p.sendline("line 2")
23 | p.expect("line 2")
24 |
25 | p.delaybeforesend = None
26 | p.sendline("line 3")
27 | p.expect("line 3")
28 |
29 | def test_delayafterread(self):
30 | """
31 | Test various values for delayafterread.
32 | """
33 | p = wexpect.spawn("cat")
34 |
35 | p.delayafterread = 1
36 | p.sendline("line 1")
37 | p.expect("line 1")
38 |
39 | p.delayafterread = 0.0
40 | p.sendline("line 2")
41 | p.expect("line 2")
42 |
43 | p.delayafterread = None
44 | p.sendline("line 3")
45 | p.expect("line 3")
46 |
--------------------------------------------------------------------------------
/tests/test_destructor.py:
--------------------------------------------------------------------------------
1 | import wexpect
2 | import unittest
3 | from tests import PexpectTestCase
4 | import gc
5 | import platform
6 | import time
7 |
8 | class TestCaseDestructor(PexpectTestCase.PexpectTestCase):
9 | def test_destructor (self):
10 | if platform.python_implementation() != 'CPython':
11 | # Details of garbage collection are different on other implementations
12 | return 'SKIP'
13 | gc.collect()
14 | time.sleep(2)
15 | p1 = wexpect.spawn('ls', port=4321)
16 | p2 = wexpect.spawn('ls', port=4322)
17 | p3 = wexpect.spawn('ls', port=4323)
18 | p4 = wexpect.spawn('ls', port=4324)
19 | p1.expect(wexpect.EOF)
20 | p2.expect(wexpect.EOF)
21 | p3.expect(wexpect.EOF)
22 | p4.expect(wexpect.EOF)
23 | p1.kill()
24 | p2.kill()
25 | p3.kill()
26 | p4.kill()
27 | p1 = None
28 | p2 = None
29 | p3 = None
30 | p4 = None
31 | gc.collect()
32 | time.sleep(2) # Some platforms are slow at gc... Solaris!
33 |
34 |
35 | p1 = wexpect.spawn('ls', port=4321)
36 | p2 = wexpect.spawn('ls', port=4322)
37 | p3 = wexpect.spawn('ls', port=4323)
38 | p4 = wexpect.spawn('ls', port=4324)
39 | p1.kill()
40 | p2.kill()
41 | p3.kill()
42 | p4.kill()
43 | del (p1)
44 | del (p2)
45 | del (p3)
46 | del (p4)
47 | gc.collect()
48 | time.sleep(2)
49 |
50 | p1 = wexpect.spawn('ls', port=4321)
51 | p2 = wexpect.spawn('ls', port=4322)
52 | p3 = wexpect.spawn('ls', port=4323)
53 | p4 = wexpect.spawn('ls', port=4324)
54 |
55 | @unittest.skipIf(wexpect.spawn_class_name == 'legacy_wexpect', "legacy unsupported")
56 | def test_failed_termination(self):
57 | "Test failed termination."
58 | child = wexpect.spawn('cat')
59 | delayafterterminate_orig = child.delayafterterminate
60 | child.delayafterterminate =.1
61 |
62 | msg = 'Child has not been terminated even after it was killed.'
63 | with self.assertRaisesRegex(wexpect.ExceptionPexpect, msg):
64 | child.terminate()
65 |
66 | child.isalive(timeout = delayafterterminate_orig)
67 |
68 | if __name__ == '__main__':
69 | unittest.main()
70 |
71 | suite = unittest.makeSuite(TestCaseDestructor,'test')
72 |
--------------------------------------------------------------------------------
/tests/test_echo.py:
--------------------------------------------------------------------------------
1 | import wexpect
2 | import unittest
3 | from tests import PexpectTestCase
4 |
5 | class EchoTestCase(PexpectTestCase.PexpectTestCase):
6 | def testPath(self):
7 | # Path of cmd executable:
8 | cmd_exe = 'cmd'
9 | cmdPrompt = '>'
10 |
11 | # Start the child process
12 | p = wexpect.spawn(cmd_exe)
13 |
14 | # Wait for prompt
15 | p.expect(cmdPrompt)
16 |
17 | # Send a command
18 | p.sendline('echo hello')
19 | p.expect(cmdPrompt)
20 |
21 | self.assertEqual('hello', p.before.splitlines()[1])
22 |
23 | if __name__ == '__main__':
24 | unittest.main()
25 |
26 | suite = unittest.makeSuite(EchoTestCase,'test')
27 |
--------------------------------------------------------------------------------
/tests/test_expect.py:
--------------------------------------------------------------------------------
1 | import multiprocessing
2 | import unittest
3 | import subprocess
4 | import time
5 | import signal
6 | import sys
7 | import os
8 |
9 | import wexpect
10 | from tests import PexpectTestCase
11 |
12 | # Many of these test cases blindly assume that sequential directory
13 | # listings of the /bin directory will yield the same results.
14 | # This may not be true, but seems adequate for testing now.
15 | # I should fix this at some point.
16 |
17 | PYTHONBINQUOTE = '"{}"'.format(sys.executable)
18 | FILTER=''.join([(len(repr(chr(x)))==3) and chr(x) or '.' for x in range(256)])
19 | def hex_dump(src, length=16):
20 | result=[]
21 | for i in range(0, len(src), length):
22 | s = src[i:i+length]
23 | hexa = ' '.join(["%02X"%ord(x) for x in s])
24 | printable = s.translate(FILTER)
25 | result.append("%04X %-*s %s\n" % (i, length*3, hexa, printable))
26 | return ''.join(result)
27 |
28 | def hex_diff(left, right):
29 | diff = ['< %s\n> %s' % (_left, _right,) for _left, _right in zip(
30 | hex_dump(left).splitlines(), hex_dump(right).splitlines())
31 | if _left != _right]
32 | return '\n' + '\n'.join(diff,)
33 |
34 |
35 | class ExpectTestCase (PexpectTestCase.PexpectTestCase):
36 |
37 | def test_expect_basic (self):
38 | p = wexpect.spawn('cat', timeout=5)
39 | p.sendline ('Hello')
40 | p.sendline ('there')
41 | p.sendline ('Mr. Python')
42 | p.expect ('Hello')
43 | p.expect ('there')
44 | p.expect ('Mr. Python')
45 | p.sendeof ()
46 | p.expect (wexpect.EOF)
47 |
48 | def test_expect_exact_basic (self):
49 | p = wexpect.spawn('cat', timeout=5)
50 | p.sendline ('Hello')
51 | p.sendline ('there')
52 | p.sendline ('Mr. Python')
53 | p.expect_exact ('Hello')
54 | p.expect_exact ('there')
55 | p.expect_exact ('Mr. Python')
56 | p.sendeof ()
57 | p.expect_exact (wexpect.EOF)
58 |
59 | def test_expect_ignore_case(self):
60 | '''This test that the ignorecase flag will match patterns
61 | even if case is different using the regex (?i) directive.
62 | '''
63 | p = wexpect.spawn('cat', timeout=5)
64 | p.sendline ('HELLO')
65 | p.sendline ('there')
66 | p.expect ('(?i)hello')
67 | p.expect ('(?i)THERE')
68 | p.sendeof ()
69 | p.expect (wexpect.EOF)
70 |
71 | def test_expect_ignore_case_flag(self):
72 | '''This test that the ignorecase flag will match patterns
73 | even if case is different using the ignorecase flag.
74 | '''
75 | p = wexpect.spawn('cat', timeout=5)
76 | p.ignorecase = True
77 | p.sendline ('HELLO')
78 | p.sendline ('there')
79 | p.expect ('hello')
80 | p.expect ('THERE')
81 | p.sendeof ()
82 | p.expect (wexpect.EOF)
83 |
84 | def test_expect_order (self):
85 | '''This tests that patterns are matched in the same order as given in the pattern_list.
86 |
87 | (Or does it? Doesn't it also pass if expect() always chooses
88 | (one of the) the leftmost matches in the input? -- grahn)
89 | ... agreed! -jquast, the buffer ptr isn't forwarded on match, see first two test cases
90 | '''
91 | p = wexpect.spawn('cat', timeout=5, echo=True)
92 | self._expect_order(p)
93 |
94 | def test_expect_order_exact (self):
95 | '''Like test_expect_order(), but using expect_exact().
96 | '''
97 | p = wexpect.spawn('cat', timeout=5, echo=True)
98 | p.expect = p.expect_exact
99 | self._expect_order(p)
100 |
101 | def _expect_order (self, p):
102 | p.sendline ('1234')
103 | p.sendline ('abcd')
104 | p.sendline ('wxyz')
105 | p.sendline ('7890')
106 | p.sendeof ()
107 | for _ in range(2):
108 | index = p.expect ([
109 | '1234',
110 | 'abcd',
111 | 'wxyz',
112 | wexpect.EOF,
113 | '7890' ])
114 | self.assertEqual(index, 0, (index, p.before, p.after))
115 |
116 | for _ in range(2):
117 | index = p.expect ([
118 | '54321',
119 | wexpect.TIMEOUT,
120 | '1234',
121 | 'abcd',
122 | 'wxyz',
123 | wexpect.EOF], timeout=5)
124 | self.assertEqual(index, 3, (index, p.before, p.after))
125 |
126 | for _ in range(2):
127 | index = p.expect ([
128 | '54321',
129 | wexpect.TIMEOUT,
130 | '1234',
131 | 'abcd',
132 | 'wxyz',
133 | wexpect.EOF], timeout=5)
134 | self.assertEqual(index, 4, (index, p.before, p.after))
135 |
136 | for _ in range(2):
137 | index = p.expect ([
138 | wexpect.EOF,
139 | 'abcd',
140 | 'wxyz',
141 | '7890' ])
142 | self.assertEqual(index, 3, (index, p.before, p.after))
143 |
144 | index = p.expect ([
145 | 'abcd',
146 | 'wxyz',
147 | '7890',
148 | wexpect.EOF])
149 | self.assertEqual(index, 3, (index, p.before, p.after))
150 |
151 | # Termination of the SpawnSocket is slow. We have to wait to prevent the failure of the next test.
152 | if wexpect.spawn_class_name == 'SpawnSocket':
153 | p.wait()
154 |
155 | def test_expect_index (self):
156 | '''This tests that mixed list of regex strings, TIMEOUT, and EOF all
157 | return the correct index when matched.
158 | '''
159 | p = wexpect.spawn('cat', timeout=5, echo=False)
160 | self._expect_index(p)
161 |
162 | def test_expect_index_exact (self):
163 | '''Like test_expect_index(), but using expect_exact().
164 | '''
165 | p = wexpect.spawn('cat', timeout=5, echo=False)
166 | p.expect = p.expect_exact
167 | self._expect_index(p)
168 | # Termination of the SpawnSocket is slow. We have to wait to prevent the failure of the next test.
169 | if wexpect.spawn_class_name == 'SpawnSocket':
170 | p.wait()
171 |
172 | def _expect_index (self, p):
173 | p.sendline ('1234')
174 | index = p.expect (['abcd','wxyz','1234',wexpect.EOF])
175 | self.assertEqual(index, 2, "index="+str(index))
176 | p.sendline ('abcd')
177 | index = p.expect ([wexpect.TIMEOUT,'abcd','wxyz','1234',wexpect.EOF])
178 | self.assertEqual(index, 1, "index="+str(index))
179 | p.sendline ('wxyz')
180 | index = p.expect (['54321',wexpect.TIMEOUT,'abcd','wxyz','1234',wexpect.EOF])
181 | self.assertEqual(index, 3, "index="+str(index)) # Expect 'wxyz'
182 | p.sendline ('$*!@?')
183 | index = p.expect (['54321',wexpect.TIMEOUT,'abcd','wxyz','1234',wexpect.EOF],
184 | timeout=1)
185 | self.assertEqual(index, 1, "index="+str(index)) # Expect TIMEOUT
186 | p.sendeof ()
187 | index = p.expect (['54321',wexpect.TIMEOUT,'abcd','wxyz','1234',wexpect.EOF])
188 | self.assertEqual(index, 5, "index="+str(index)) # Expect EOF
189 |
190 | def test_expect (self):
191 | the_old_way = subprocess.Popen(args=['ls', '-l'],
192 | stdout=subprocess.PIPE).communicate()[0].rstrip()
193 | p = wexpect.spawn('ls -l')
194 | the_new_way = ''
195 | while 1:
196 | i = p.expect (['\n', wexpect.EOF])
197 | the_new_way = the_new_way + p.before
198 | if i == 1:
199 | break
200 | the_new_way = the_new_way.rstrip()
201 | the_new_way = the_new_way.replace('\r\n', '\n'
202 | ).replace('\r', '\n').replace('\n\n', '\n').rstrip()
203 | the_old_way = the_old_way.decode('utf-8')
204 | the_old_way = the_old_way.replace('\r\n', '\n'
205 | ).replace('\r', '\n').replace('\n\n', '\n').rstrip()
206 | # print(repr(the_old_way))
207 | # print(repr(the_new_way))
208 | # the_old_way = the_old_way[0:10000]
209 | # the_new_way = the_new_way[0:10000]
210 | self.assertEqual(the_old_way, the_new_way)
211 |
212 | def test_expect_exact (self):
213 | the_old_way = subprocess.Popen(args=['ls', '-l'],
214 | stdout=subprocess.PIPE).communicate()[0].rstrip()
215 | p = wexpect.spawn('ls -l')
216 | the_new_way = ''
217 | while 1:
218 | i = p.expect_exact (['\n', wexpect.EOF])
219 | the_new_way = the_new_way + p.before
220 | if i == 1:
221 | break
222 | the_new_way = the_new_way.replace('\r\n', '\n'
223 | ).replace('\r', '\n').replace('\n\n', '\n').rstrip()
224 | the_old_way = the_old_way.decode('utf-8')
225 | the_old_way = the_old_way.replace('\r\n', '\n'
226 | ).replace('\r', '\n').replace('\n\n', '\n').rstrip()
227 | self.assertEqual(the_old_way, the_new_way)
228 | # Termination of the SpawnSocket is slow. We have to wait to prevent the failure of the next test.
229 | if wexpect.spawn_class_name == 'SpawnSocket':
230 | p.wait()
231 |
232 | p = wexpect.spawn('echo hello.?world')
233 | i = p.expect_exact('.?')
234 | self.assertEqual(p.before, 'hello')
235 | self.assertEqual(p.after, '.?')
236 |
237 | def test_expect_eof (self):
238 | the_old_way = subprocess.Popen(args=['ls', '-l'],
239 | stdout=subprocess.PIPE).communicate()[0].rstrip()
240 | p = wexpect.spawn('ls -l')
241 | p.expect(wexpect.EOF) # This basically tells it to read everything. Same as wexpect.run() function.
242 | the_new_way = p.before
243 | the_new_way = the_new_way.replace('\r\n', '\n'
244 | ).replace('\r', '\n').replace('\n\n', '\n').rstrip()
245 | the_old_way = the_old_way.decode('utf-8')
246 | the_old_way = the_old_way.replace('\r\n', '\n'
247 | ).replace('\r', '\n').replace('\n\n', '\n').rstrip()
248 | self.assertEqual(the_old_way, the_new_way)
249 |
250 | # Termination of the SpawnSocket is slow. We have to wait to prevent the failure of the next test.
251 | if wexpect.spawn_class_name == 'SpawnSocket':
252 | p.wait()
253 |
254 | def test_expect_timeout (self):
255 | p = wexpect.spawn('cat', timeout=5)
256 | p.expect(wexpect.TIMEOUT) # This tells it to wait for timeout.
257 | self.assertEqual(p.after, wexpect.TIMEOUT)
258 |
259 | def test_unexpected_eof (self):
260 | p = wexpect.spawn('ls -l /bin')
261 | try:
262 | p.expect('_Z_XY_XZ') # Probably never see this in ls output.
263 | except wexpect.EOF:
264 | pass
265 | else:
266 | self.fail ('Expected an EOF exception.')
267 |
268 | if wexpect.spawn_class_name == 'SpawnSocket':
269 | p.wait()
270 |
271 | def test_buffer_interface(self):
272 | p = wexpect.spawn('cat', timeout=5)
273 | p.sendline ('Hello')
274 | p.expect ('Hello')
275 | assert len(p.buffer)
276 | p.buffer = 'Testing'
277 | p.sendeof ()
278 | if wexpect.spawn_class_name == 'SpawnSocket':
279 | p.wait()
280 |
281 | def _before_after(self, p):
282 | p.timeout = 5
283 |
284 | p.expect('5')
285 | self.assertEqual(p.after, '5')
286 | self.assertTrue(p.before.startswith('[0, 1, 2'), p.before)
287 |
288 | p.expect('50')
289 | self.assertEqual(p.after, '50')
290 | self.assertTrue(p.before.startswith(', 6, 7, 8'), p.before[:20])
291 | self.assertTrue(p.before.endswith('48, 49, '), p.before[-20:])
292 |
293 | p.expect(wexpect.EOF)
294 | self.assertEqual(p.after, wexpect.EOF)
295 | assert p.before.startswith(', 51, 52'), p.before[:20]
296 | assert p.before.endswith(', 99]\r\n'), p.before[-20:]
297 |
298 | if wexpect.spawn_class_name == 'SpawnSocket':
299 | p.wait()
300 |
301 | def test_before_after(self):
302 | '''This tests expect() for some simple before/after things.
303 | '''
304 | p = wexpect.spawn('%s -Wi list100.py' % PYTHONBINQUOTE)
305 | self._before_after(p)
306 |
307 | def test_before_after_exact(self):
308 | '''This tests some simple before/after things, for
309 | expect_exact(). (Grahn broke it at one point.)
310 | '''
311 | p = wexpect.spawn('%s -Wi list100.py' % PYTHONBINQUOTE)
312 | # mangle the spawn so we test expect_exact() instead
313 | p.expect = p.expect_exact
314 | self._before_after(p)
315 |
316 | def _ordering(self, p):
317 | p.timeout = 20
318 | p.expect('>>> ')
319 |
320 | p.sendline('list(range(4*3))')
321 | self.assertEqual(p.expect(['5,', '5,']), 0)
322 | p.expect('>>> ')
323 |
324 | p.sendline('list(range(4*3))')
325 | self.assertEqual(p.expect(['7,', '5,']), 1)
326 | p.expect('>>> ')
327 |
328 | p.sendline('list(range(4*3))')
329 | self.assertEqual(p.expect(['5,', '7,']), 0)
330 | p.expect('>>> ')
331 |
332 | p.sendline('list(range(4*5))')
333 | self.assertEqual(p.expect(['2,', '12,']), 0)
334 | p.expect('>>> ')
335 |
336 | p.sendline('list(range(4*5))')
337 | self.assertEqual(p.expect(['12,', '2,']), 1)
338 |
339 | p.sendline('exit()')
340 |
341 | def test_ordering(self):
342 | '''This tests expect() for which pattern is returned
343 | when many may eventually match. I (Grahn) am a bit
344 | confused about what should happen, but this test passes
345 | with wexpect 2.1.
346 | '''
347 | p = wexpect.spawn(PYTHONBINQUOTE)
348 | self._ordering(p)
349 |
350 | def test_ordering_exact(self):
351 | '''This tests expect_exact() for which pattern is returned
352 | when many may eventually match. I (Grahn) am a bit
353 | confused about what should happen, but this test passes
354 | for the expect() method with wexpect 2.1.
355 | '''
356 | p = wexpect.spawn(PYTHONBINQUOTE)
357 | # mangle the spawn so we test expect_exact() instead
358 | p.expect = p.expect_exact
359 | self._ordering(p)
360 |
361 | # Termination of the SpawnSocket is slow. We have to wait to prevent the failure of the next test.
362 | if wexpect.spawn_class_name == 'SpawnSocket':
363 | p.wait()
364 |
365 | def _greed(self, expect):
366 | # End at the same point: the one with the earliest start should win
367 | self.assertEqual(expect(['3, 4', '2, 3, 4']), 1)
368 |
369 | # Start at the same point: first pattern passed wins
370 | self.assertEqual(expect(['5,', '5, 6']), 0)
371 |
372 | # Same pattern passed twice: first instance wins
373 | self.assertEqual(expect(['7, 8', '7, 8, 9', '7, 8']), 0)
374 |
375 | def _greed_read1(self, expect):
376 | # Here, one has an earlier start and a later end. When processing
377 | # one character at a time, the one that finishes first should win,
378 | # because we don't know about the other match when it wins.
379 | # If maxread > 1, this behaviour is currently undefined, although in
380 | # most cases the one that starts first will win.
381 | self.assertEqual(expect(['1, 2, 3', '2,']), 1)
382 |
383 | def test_greed(self):
384 | p = wexpect.spawn(PYTHONBINQUOTE + ' list100.py')
385 | self._greed(p.expect)
386 |
387 | def test_greed_exact(self):
388 | p = wexpect.spawn(PYTHONBINQUOTE + ' list100.py')
389 | self._greed(p.expect_exact)
390 |
391 | def test_bad_arg(self):
392 | p = wexpect.spawn('cat')
393 | with self.assertRaisesRegex(TypeError, '.*must be one of'):
394 | p.expect(1)
395 | with self.assertRaisesRegex(TypeError, '.*must be one of'):
396 | p.expect([1, '2'])
397 | with self.assertRaisesRegex(TypeError, '.*must be one of'):
398 | p.expect_exact(1)
399 | with self.assertRaisesRegex(TypeError, '.*must be one of'):
400 | p.expect_exact([1, '2'])
401 |
402 | def test_timeout_none(self):
403 | p = wexpect.spawn('echo abcdef', timeout=None)
404 | p.expect('abc')
405 | p.expect_exact('def')
406 | p.expect(wexpect.EOF)
407 |
408 | if __name__ == '__main__':
409 | unittest.main()
410 |
411 | suite = unittest.makeSuite(ExpectTestCase, 'test')
412 |
--------------------------------------------------------------------------------
/tests/test_interact.py:
--------------------------------------------------------------------------------
1 | import wexpect
2 | import time
3 | import unittest
4 | from tests import PexpectTestCase
5 |
6 | class InteractTestCase(PexpectTestCase.PexpectTestCase):
7 |
8 | @unittest.skipIf(not (hasattr(wexpect, 'legacy_wexpect')) or (hasattr(wexpect.spawn, 'interact')), "spawn does not support runtime interact switching.")
9 | def test_interact(self):
10 | # Path of cmd executable:
11 | cmd_exe = 'cmd'
12 | cmdPrompt = '>'
13 |
14 | # Start the child process
15 | p = wexpect.spawn(cmd_exe)
16 |
17 | # Wait for prompt
18 | p.expect(cmdPrompt)
19 |
20 | p.interact()
21 | time.sleep(1)
22 |
23 | # Send a command
24 | p.sendline('echo hello')
25 | p.expect(cmdPrompt)
26 |
27 | p.stop_interact()
28 |
29 | self.assertEqual('hello', p.before.splitlines()[1])
30 |
31 | @unittest.skipIf(not (hasattr(wexpect, 'legacy_wexpect')) or (hasattr(wexpect.spawn, 'interact')), "spawn does not support runtime interact switching.")
32 | def test_interact_dead(self):
33 | # Path of cmd executable:
34 | echo = 'echo hello'
35 |
36 | # Start the child process
37 | p = wexpect.spawn(echo)
38 |
39 | p.expect('hello')
40 | time.sleep(0.5)
41 |
42 |
43 | with self.assertRaises(wexpect.ExceptionPexpect):
44 | p.interact()
45 |
46 | with self.assertRaises(wexpect.ExceptionPexpect):
47 | p.stop_interact()
48 |
49 | if __name__ == '__main__':
50 | unittest.main()
51 |
52 | suite = unittest.makeSuite(InteractTestCase,'test')
53 |
--------------------------------------------------------------------------------
/tests/test_isalive.py:
--------------------------------------------------------------------------------
1 | import wexpect
2 | import unittest
3 | import signal
4 | import sys
5 | import time
6 | from tests import PexpectTestCase
7 |
8 | PYBIN = '"{}"'.format(sys.executable)
9 |
10 | class IsAliveTestCase(PexpectTestCase.PexpectTestCase):
11 | """Various tests for the running status of processes."""
12 |
13 | def test_expect_wait(self):
14 | """Ensure consistency in wait() and isalive()."""
15 | p = wexpect.spawn('sleep 1')
16 | assert p.isalive()
17 | self.assertEqual(p.wait(), 0)
18 | assert not p.isalive()
19 | # In previous versions of ptyprocess/wexpect, calling wait() a second
20 | # time would raise an exception, but not since v4.0
21 | self.assertEqual(p.wait(), 0)
22 |
23 | def test_signal_wait(self):
24 | '''Test calling wait with a process terminated by a signal.'''
25 | if not hasattr(signal, 'SIGALRM'):
26 | return 'SKIP'
27 | p = wexpect.spawn(PYBIN, ['alarm_die.py'])
28 | p.wait()
29 | assert p.exitstatus is None
30 | self.assertEqual(p.signalstatus, signal.SIGALRM)
31 |
32 | def test_expect_isalive_dead_after_normal_termination (self):
33 | p = wexpect.spawn('ls', timeout=15)
34 | p.expect(wexpect.EOF)
35 | assert not p.isalive()
36 |
37 | def test_expect_isalive_dead_after_SIGHUP(self):
38 | p = wexpect.spawn('cat', timeout=5)
39 | assert p.isalive()
40 | self.assertTrue(p.terminate())
41 | p.expect(wexpect.EOF)
42 | assert not p.isalive()
43 |
44 | def test_expect_isalive_dead_after_SIGINT(self):
45 | p = wexpect.spawn('cat', timeout=5)
46 | assert p.isalive()
47 | force = False
48 | if sys.platform.lower().startswith('sunos'):
49 | # On Solaris (SmartOs), and only when executed from cron(1), SIGKILL
50 | # is required to end the sub-process. This is done using force=True
51 | force = True
52 | self.assertEqual(p.terminate(force), True)
53 | p.expect(wexpect.EOF)
54 | assert not p.isalive()
55 |
56 | def test_expect_isalive_dead_after_SIGKILL(self):
57 | p = wexpect.spawn('cat', timeout=5)
58 | assert p.isalive()
59 | p.kill()
60 | p.expect(wexpect.EOF)
61 | assert not p.isalive()
62 |
63 | def test_forced_terminate(self):
64 |
65 | p = wexpect.spawn(PYBIN + ' needs_kill.py')
66 | p.expect('READY')
67 | self.assertEqual(p.terminate(force=True), True)
68 | p.expect(wexpect.EOF)
69 | assert not p.isalive()
70 |
71 | ### Some platforms allow this. Some reset status after call to waitpid.
72 | ### probably not necessary, isalive() returns early when terminate is False.
73 | def test_expect_isalive_consistent_multiple_calls (self):
74 | '''This tests that multiple calls to isalive() return same value.
75 | '''
76 | p = wexpect.spawn('cat')
77 | assert p.isalive()
78 | assert p.isalive()
79 | p.sendeof()
80 | p.expect(wexpect.EOF)
81 | assert not p.isalive()
82 | assert not p.isalive()
83 |
84 | if __name__ == '__main__':
85 | unittest.main()
86 |
87 | suite = unittest.makeSuite(IsAliveTestCase, 'test')
88 |
--------------------------------------------------------------------------------
/tests/test_long.py:
--------------------------------------------------------------------------------
1 | import wexpect
2 | import unittest
3 | import sys
4 | import os
5 | from tests import PexpectTestCase
6 | from . import long_printer
7 |
8 | puskas_wiki = long_printer.puskas_wiki
9 |
10 | class TestCaseLong(PexpectTestCase.PexpectTestCase):
11 | def test_long (self):
12 |
13 | here = os.path.dirname(os.path.abspath(__file__))
14 | sys.path.insert(0, here)
15 |
16 | # With quotes (C:\Program Files\Python37\python.exe needs quotes)
17 | python_executable = '"' + sys.executable + '" '
18 | child_script = here + '\\long_printer.py'
19 |
20 |
21 | longPrinter = python_executable + ' ' + child_script
22 | prompt = 'puskas> '
23 |
24 | # Start the child process
25 | p = wexpect.spawn(longPrinter)
26 | # Wait for prompt
27 | p.expect(prompt)
28 |
29 | for i in range(10):
30 | p.sendline('0')
31 | p.expect(prompt)
32 | self.assertEqual(p.before.splitlines()[1], puskas_wiki[0])
33 |
34 | p.sendline('all')
35 | p.expect(prompt)
36 | for a,b in zip(p.before.splitlines()[1:], puskas_wiki):
37 | self.assertEqual(a, b)
38 |
39 | for j, paragraph in enumerate(puskas_wiki):
40 | p.sendline(str(j))
41 | p.expect(prompt)
42 | self.assertEqual(p.before.splitlines()[1], paragraph)
43 |
44 |
45 | if __name__ == '__main__':
46 | unittest.main()
47 |
48 | suite = unittest.makeSuite(TestCaseLong,'test')
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/tests/test_misc.py:
--------------------------------------------------------------------------------
1 | import unittest
2 | import sys
3 | import re
4 | import os
5 | import time
6 |
7 | import wexpect
8 | from tests import PexpectTestCase
9 |
10 | # the program cat(1) may display ^D\x08\x08 when \x04 (EOF, Ctrl-D) is sent
11 | _CAT_EOF = '^D\x08\x08'
12 |
13 | def _u(s):
14 | return s
15 |
16 | PYBIN = '"{}"'.format(sys.executable)
17 |
18 | class TestCaseMisc(PexpectTestCase.PexpectTestCase):
19 |
20 | def test_wrong_command(self):
21 | "Test raise of exception in case of wrong program"
22 | with self.assertRaisesRegex(wexpect.ExceptionPexpect,"The command was not found.+"):
23 | child = wexpect.spawn('unexecutable')
24 |
25 | def test_isatty(self):
26 | " Test isatty() is True after spawning process on most platforms. "
27 | child = wexpect.spawn('cat')
28 | assert child.isatty()
29 |
30 | def test_read(self):
31 | " Test spawn.read by calls of various size. "
32 | child = wexpect.spawn('cat')
33 | child.sendline("abc")
34 | child.sendeof()
35 | child.readline()
36 | self.assertEqual(child.read(0), '')
37 | self.assertEqual(child.read(1), 'a')
38 | self.assertEqual(child.read(1), 'b')
39 | self.assertEqual(child.read(1), 'c')
40 | self.assertEqual(child.read(2), '\r\n')
41 |
42 | def test_readline_bin_echo(self):
43 | " Test spawn('echo'). "
44 | # given,
45 | child = wexpect.spawn('echo', ['alpha', 'beta'])
46 |
47 | # exercise,
48 | self.assertEqual(child.readline(), 'alpha beta\r\n')
49 |
50 | def test_readline(self):
51 | " Test spawn.readline(). "
52 | # when argument 0 is sent, nothing is returned.
53 | # Otherwise the argument value is meaningless.
54 | child = wexpect.spawn('cat')
55 | child.sendline("alpha")
56 | child.sendline("beta")
57 | child.sendline("gamma")
58 | child.sendline("delta")
59 | child.sendeof()
60 | self.assertEqual(child.readline(0), '')
61 | child.readline().rstrip()
62 | self.assertEqual(child.readline().rstrip(), 'alpha')
63 | child.readline().rstrip()
64 | self.assertEqual(child.readline(1).rstrip(), 'beta')
65 | child.readline().rstrip()
66 | self.assertEqual(child.readline(2).rstrip(), 'gamma')
67 | child.readline().rstrip()
68 | self.assertEqual(child.readline().rstrip(), 'delta')
69 | child.expect(wexpect.EOF)
70 | if type(child).__name__ in ['SpawnPipe', 'SpawnSocket']:
71 | time.sleep(child.delayafterterminate)
72 | assert not child.isalive(trust_console=False)
73 | else:
74 | assert not child.isalive()
75 | self.assertEqual(child.exitstatus, 0)
76 |
77 | def test_iter(self):
78 | " iterating over lines of spawn.__iter__(). "
79 | child = wexpect.spawn('echo "abc\r\n123"')
80 | # Don't use ''.join() because we want to test __iter__().
81 | page = ''
82 | for line in child:
83 | page += line
84 | page = page.replace(_CAT_EOF, '')
85 | self.assertEqual(page, 'abc\r\n123\r\n')
86 |
87 | def test_readlines(self):
88 | " reading all lines of spawn.readlines(). "
89 | child = wexpect.spawn('cat', echo=False)
90 | child.sendline("abc")
91 | child.sendline("123")
92 | child.sendeof()
93 | page = ''.join(child.readlines()).replace(_CAT_EOF, '')
94 | self.assertEqual(page, '\r\nabc\r\n\r\n123\r\n')
95 | child.expect(wexpect.EOF)
96 | if type(child).__name__ in ['SpawnPipe', 'SpawnSocket']:
97 | time.sleep(child.delayafterterminate)
98 | assert not child.isalive(trust_console=False)
99 | else:
100 | assert not child.isalive()
101 | self.assertEqual(child.exitstatus, 0)
102 |
103 | def test_write(self):
104 | " write a character and return it in return. "
105 | child = wexpect.spawn('cat')
106 | child.write('a')
107 | child.write('\r')
108 | child.readline()
109 | self.assertEqual(child.readline(), 'a\r\n')
110 |
111 | def test_writelines(self):
112 | " spawn.writelines() "
113 | child = wexpect.spawn('cat')
114 | # notice that much like file.writelines, we do not delimit by newline
115 | # -- it is equivalent to calling write(''.join([args,]))
116 | child.writelines(['abc', '123', 'xyz', '\r'])
117 | child.sendeof()
118 | child.readline()
119 | line = child.readline()
120 | self.assertEqual(line, 'abc123xyz\r\n')
121 |
122 | def test_eof(self):
123 | " call to expect() after EOF is received raises wexpect.EOF "
124 | child = wexpect.spawn('cat')
125 | child.sendeof()
126 | with self.assertRaises(wexpect.EOF):
127 | child.expect('the unexpected')
128 |
129 | def test_with(self):
130 | "spawn can be used as a context manager"
131 | with wexpect.spawn(PYBIN + ' echo_w_prompt.py') as p:
132 | p.expect('')
133 | p.sendline('alpha')
134 | p.expect('alpha')
135 | assert p.isalive()
136 |
137 | assert not p.isalive()
138 |
139 | def test_terminate(self):
140 | " test force terminate always succeeds (SIGKILL). "
141 | child = wexpect.spawn('cat')
142 | child.terminate(force=1)
143 | assert child.terminated
144 |
145 | def test_bad_arguments_suggest_fdpsawn(self):
146 | " assert custom exception for spawn(int). "
147 | expect_errmsg = "maybe you want to use fdpexpect.fdspawn"
148 | with self.assertRaisesRegex(wexpect.ExceptionPexpect,
149 | ".*" + expect_errmsg):
150 | wexpect.spawn(1)
151 |
152 | def test_bad_arguments_second_arg_is_list(self):
153 | " Second argument to spawn, if used, must be only a list."
154 | with self.assertRaises(TypeError):
155 | wexpect.spawn('ls', '-la')
156 |
157 | with self.assertRaises(TypeError):
158 | # not even a tuple,
159 | wexpect.spawn('ls', ('-la',))
160 |
161 | def test_read_after_close_raises_value_error(self):
162 | " Calling read_nonblocking after close raises ValueError. "
163 | # as read_nonblocking underlies all other calls to read,
164 | # ValueError should be thrown for all forms of read.
165 | with self.assertRaises(ValueError):
166 | p = wexpect.spawn('cat')
167 | p.close()
168 | p.read_nonblocking()
169 |
170 | with self.assertRaises(ValueError):
171 | p = wexpect.spawn('cat')
172 | p.close()
173 | p.read()
174 |
175 | with self.assertRaises(ValueError):
176 | p = wexpect.spawn('cat')
177 | p.close()
178 | p.readline()
179 |
180 | with self.assertRaises(ValueError):
181 | p = wexpect.spawn('cat')
182 | p.close()
183 | p.readlines()
184 |
185 | def test_isalive(self):
186 | " check isalive() before and after EOF. (True, False) "
187 | child = wexpect.spawn('cat')
188 | self.assertTrue(child.isalive())
189 | child.sendeof()
190 | child.expect(wexpect.EOF)
191 | assert child.isalive() is False
192 | self.assertFalse(child.isalive())
193 |
194 | def test_bad_type_in_expect(self):
195 | " expect() does not accept dictionary arguments. "
196 | child = wexpect.spawn('cat')
197 | with self.assertRaises(TypeError):
198 | child.expect({})
199 |
200 | def test_cwd(self):
201 | " check keyword argument `cwd=' of wexpect.run() "
202 | try:
203 | os.mkdir('cwd_tmp')
204 | except:
205 | pass
206 | tmp_dir = os.path.realpath('cwd_tmp')
207 | child = wexpect.spawn('cmd')
208 | child.expect('>')
209 | child.sendline('cd')
210 | child.expect('>')
211 | default = child.before.splitlines()[1]
212 | child.terminate()
213 | child = wexpect.spawn('cmd', cwd=tmp_dir)
214 | child.expect('>')
215 | child.sendline('cd')
216 | child.expect('>')
217 | pwd_tmp = child.before.splitlines()[1]
218 | child.terminate()
219 | self.assertNotEqual(default, pwd_tmp)
220 | self.assertEqual(tmp_dir, _u(pwd_tmp))
221 |
222 | def _test_searcher_as(self, searcher, plus=None):
223 | # given,
224 | given_words = ['alpha', 'beta', 'gamma', 'delta', ]
225 | given_search = given_words
226 | if searcher == wexpect.searcher_re:
227 | given_search = [re.compile(word) for word in given_words]
228 | if plus is not None:
229 | given_search = given_search + [plus]
230 | search_string = searcher(given_search)
231 | basic_fmt = '\n {0}: {1}'
232 | fmt = basic_fmt
233 | if searcher is wexpect.searcher_re:
234 | fmt = '\n {0}: re.compile({1})'
235 | expected_output = '{0}:'.format(searcher.__name__)
236 | idx = 0
237 | for word in given_words:
238 | expected_output += fmt.format(idx, '"{0}"'.format(word))
239 | idx += 1
240 | if plus is not None:
241 | if plus == wexpect.EOF:
242 | expected_output += basic_fmt.format(idx, 'EOF')
243 | elif plus == wexpect.TIMEOUT:
244 | expected_output += basic_fmt.format(idx, 'TIMEOUT')
245 |
246 | # exercise,
247 | self.assertEqual(search_string.__str__(), expected_output)
248 |
249 | def test_searcher_as_string(self):
250 | " check searcher_string(..).__str__() "
251 | self._test_searcher_as(wexpect.searcher_string)
252 |
253 | def test_searcher_as_string_with_EOF(self):
254 | " check searcher_string(..).__str__() that includes EOF "
255 | self._test_searcher_as(wexpect.searcher_string, plus=wexpect.EOF)
256 |
257 | def test_searcher_as_string_with_TIMEOUT(self):
258 | " check searcher_string(..).__str__() that includes TIMEOUT "
259 | self._test_searcher_as(wexpect.searcher_string, plus=wexpect.TIMEOUT)
260 |
261 | def test_searcher_re_as_string(self):
262 | " check searcher_re(..).__str__() "
263 | self._test_searcher_as(wexpect.searcher_re)
264 |
265 | def test_searcher_re_as_string_with_EOF(self):
266 | " check searcher_re(..).__str__() that includes EOF "
267 | self._test_searcher_as(wexpect.searcher_re, plus=wexpect.EOF)
268 |
269 | def test_searcher_re_as_string_with_TIMEOUT(self):
270 | " check searcher_re(..).__str__() that includes TIMEOUT "
271 | self._test_searcher_as(wexpect.searcher_re, plus=wexpect.TIMEOUT)
272 |
273 | def test_exception_tb(self):
274 | " test get_trace() filters away wexpect/__init__.py calls. "
275 | p = wexpect.spawn('sleep 1')
276 | try:
277 | p.expect('BLAH')
278 | except wexpect.ExceptionPexpect as e:
279 | # get_trace should filter out frames in wexpect's own code
280 | tb = e.get_trace()
281 | # exercise,
282 | assert 'raise ' not in tb
283 | assert 'wexpect/__init__.py' not in tb
284 | else:
285 | assert False, "Should have raised an exception."
286 |
287 | if __name__ == '__main__':
288 | unittest.main()
289 |
290 | suite = unittest.makeSuite(TestCaseMisc,'test')
291 |
--------------------------------------------------------------------------------
/tests/test_misc_console_coverage.py:
--------------------------------------------------------------------------------
1 | import unittest
2 | import sys
3 | import re
4 | import os
5 | import time
6 |
7 | import wexpect
8 | from tests import PexpectTestCase
9 |
10 | # the program cat(1) may display ^D\x08\x08 when \x04 (EOF, Ctrl-D) is sent
11 | _CAT_EOF = '^D\x08\x08'
12 |
13 | def _u(s):
14 | return s
15 |
16 | PYBIN = '"{}"'.format(sys.executable)
17 |
18 | class TestCaseMisc(PexpectTestCase.PexpectTestCase):
19 |
20 | @unittest.skipIf(wexpect.spawn_class_name == 'legacy_wexpect', "legacy unsupported")
21 | def test_isatty(self):
22 | " Test isatty() is True after spawning process on most platforms. "
23 | child = wexpect.spawn('cat', coverage_console_reader=True)
24 | if not child.isatty() and sys.platform.lower().startswith('sunos'):
25 | if hasattr(unittest, 'SkipTest'):
26 | raise unittest.SkipTest("Not supported on this platform.")
27 | return 'skip'
28 | assert child.isatty()
29 |
30 | @unittest.skipIf(wexpect.spawn_class_name == 'legacy_wexpect', "legacy unsupported")
31 | def test_read(self):
32 | " Test spawn.read by calls of various size. "
33 | child = wexpect.spawn('cat', coverage_console_reader=True)
34 | child.sendline("abc")
35 | child.sendeof()
36 | child.readline()
37 | self.assertEqual(child.read(0), '')
38 | self.assertEqual(child.read(1), 'a')
39 | self.assertEqual(child.read(1), 'b')
40 | self.assertEqual(child.read(1), 'c')
41 | self.assertEqual(child.read(2), '\r\n')
42 |
43 | @unittest.skipIf(wexpect.spawn_class_name == 'legacy_wexpect', "legacy unsupported")
44 | def test_readline_bin_echo(self):
45 | " Test spawn('echo'). "
46 | # given,
47 | child = wexpect.spawn('echo', ['alpha', 'beta'], coverage_console_reader=True)
48 |
49 | # exercise,
50 | self.assertEqual(child.readline(), 'alpha beta\r\n')
51 |
52 | @unittest.skipIf(wexpect.spawn_class_name == 'legacy_wexpect', "legacy unsupported")
53 | def test_readline(self):
54 | " Test spawn.readline(). "
55 | # when argument 0 is sent, nothing is returned.
56 | # Otherwise the argument value is meaningless.
57 | child = wexpect.spawn('cat', coverage_console_reader=True)
58 | child.sendline("alpha")
59 | child.sendline("beta")
60 | child.sendline("gamma")
61 | child.sendline("delta")
62 | child.sendeof()
63 | self.assertEqual(child.readline(0), '')
64 | child.readline().rstrip()
65 | self.assertEqual(child.readline().rstrip(), 'alpha')
66 | child.readline().rstrip()
67 | self.assertEqual(child.readline(1).rstrip(), 'beta')
68 | child.readline().rstrip()
69 | self.assertEqual(child.readline(2).rstrip(), 'gamma')
70 | child.readline().rstrip()
71 | self.assertEqual(child.readline().rstrip(), 'delta')
72 | child.expect(wexpect.EOF)
73 | if type(child).__name__ in ['SpawnPipe', 'SpawnSocket']:
74 | time.sleep(child.delayafterterminate)
75 | assert not child.isalive(trust_console=False)
76 | else:
77 | assert not child.isalive()
78 | self.assertEqual(child.exitstatus, 0)
79 |
80 | @unittest.skipIf(wexpect.spawn_class_name == 'legacy_wexpect', "legacy unsupported")
81 | def test_iter(self):
82 | " iterating over lines of spawn.__iter__(). "
83 | child = wexpect.spawn('echo "abc\r\n123"', coverage_console_reader=True)
84 | # Don't use ''.join() because we want to test __iter__().
85 | page = ''
86 | for line in child:
87 | page += line
88 | page = page.replace(_CAT_EOF, '')
89 | self.assertEqual(page, 'abc\r\n123\r\n')
90 |
91 | @unittest.skipIf(wexpect.spawn_class_name == 'legacy_wexpect', "legacy unsupported")
92 | def test_readlines(self):
93 | " reading all lines of spawn.readlines(). "
94 | child = wexpect.spawn('cat', echo=False, coverage_console_reader=True)
95 | child.sendline("abc")
96 | child.sendline("123")
97 | child.sendeof()
98 | page = ''.join(child.readlines()).replace(_CAT_EOF, '')
99 | self.assertEqual(page, '\r\nabc\r\n\r\n123\r\n')
100 | child.expect(wexpect.EOF)
101 | if type(child).__name__ in ['SpawnPipe', 'SpawnSocket']:
102 | time.sleep(child.delayafterterminate)
103 | assert not child.isalive(trust_console=False)
104 | else:
105 | assert not child.isalive()
106 | self.assertEqual(child.exitstatus, 0)
107 |
108 | @unittest.skipIf(wexpect.spawn_class_name == 'legacy_wexpect', "legacy unsupported")
109 | def test_write(self):
110 | " write a character and return it in return. "
111 | child = wexpect.spawn('cat', coverage_console_reader=True)
112 | child.write('a')
113 | child.write('\r')
114 | child.readline()
115 | self.assertEqual(child.readline(), 'a\r\n')
116 |
117 | @unittest.skipIf(wexpect.spawn_class_name == 'legacy_wexpect', "legacy unsupported")
118 | def test_writelines(self):
119 | " spawn.writelines() "
120 | child = wexpect.spawn('cat', coverage_console_reader=True)
121 | # notice that much like file.writelines, we do not delimit by newline
122 | # -- it is equivalent to calling write(''.join([args,]))
123 | child.writelines(['abc', '123', 'xyz', '\r'])
124 | child.sendeof()
125 | child.readline()
126 | line = child.readline()
127 | self.assertEqual(line, 'abc123xyz\r\n')
128 |
129 | @unittest.skipIf(wexpect.spawn_class_name == 'legacy_wexpect', "legacy unsupported")
130 | def test_eof(self):
131 | " call to expect() after EOF is received raises wexpect.EOF "
132 | child = wexpect.spawn('cat', coverage_console_reader=True)
133 | child.sendeof()
134 | with self.assertRaises(wexpect.EOF):
135 | child.expect('the unexpected')
136 |
137 | @unittest.skipIf(wexpect.spawn_class_name == 'legacy_wexpect', "legacy unsupported")
138 | def test_with(self):
139 | "spawn can be used as a context manager"
140 | with wexpect.spawn(PYBIN + ' echo_w_prompt.py', coverage_console_reader=True) as p:
141 | p.expect('')
142 | p.sendline('alpha')
143 | p.expect('alpha')
144 | assert p.isalive()
145 |
146 | assert not p.isalive()
147 |
148 | @unittest.skipIf(wexpect.spawn_class_name == 'legacy_wexpect', "legacy unsupported")
149 | def test_terminate(self):
150 | " test force terminate always succeeds (SIGKILL). "
151 | child = wexpect.spawn('cat', coverage_console_reader=True)
152 | child.terminate(force=1)
153 | assert child.terminated
154 |
155 | @unittest.skipIf(wexpect.spawn_class_name == 'legacy_wexpect', "legacy unsupported")
156 | def test_bad_arguments_suggest_fdpsawn(self):
157 | " assert custom exception for spawn(int). "
158 | expect_errmsg = "maybe you want to use fdpexpect.fdspawn"
159 | with self.assertRaisesRegex(wexpect.ExceptionPexpect,
160 | ".*" + expect_errmsg):
161 | wexpect.spawn(1, coverage_console_reader=True)
162 |
163 | @unittest.skipIf(wexpect.spawn_class_name == 'legacy_wexpect', "legacy unsupported")
164 | def test_bad_arguments_second_arg_is_list(self):
165 | " Second argument to spawn, if used, must be only a list."
166 | with self.assertRaises(TypeError):
167 | wexpect.spawn('ls', '-la', coverage_console_reader=True)
168 |
169 | with self.assertRaises(TypeError):
170 | # not even a tuple,
171 | wexpect.spawn('ls', ('-la',), coverage_console_reader=True)
172 |
173 | @unittest.skipIf(wexpect.spawn_class_name == 'legacy_wexpect', "legacy unsupported")
174 | def test_read_after_close_raises_value_error(self):
175 | " Calling read_nonblocking after close raises ValueError. "
176 | # as read_nonblocking underlies all other calls to read,
177 | # ValueError should be thrown for all forms of read.
178 | with self.assertRaises(ValueError):
179 | p = wexpect.spawn('cat', coverage_console_reader=True)
180 | p.close()
181 | p.read_nonblocking()
182 |
183 | with self.assertRaises(ValueError):
184 | p = wexpect.spawn('cat', coverage_console_reader=True)
185 | p.close()
186 | p.read()
187 |
188 | with self.assertRaises(ValueError):
189 | p = wexpect.spawn('cat', coverage_console_reader=True)
190 | p.close()
191 | p.readline()
192 |
193 | with self.assertRaises(ValueError):
194 | p = wexpect.spawn('cat', coverage_console_reader=True)
195 | p.close()
196 | p.readlines()
197 |
198 | @unittest.skipIf(wexpect.spawn_class_name == 'legacy_wexpect', "legacy unsupported")
199 | def test_isalive(self):
200 | " check isalive() before and after EOF. (True, False) "
201 | child = wexpect.spawn('cat', coverage_console_reader=True)
202 | self.assertTrue(child.isalive())
203 | child.sendeof()
204 | child.expect(wexpect.EOF)
205 | assert child.isalive() is False
206 | self.assertFalse(child.isalive())
207 |
208 | @unittest.skipIf(wexpect.spawn_class_name == 'legacy_wexpect', "legacy unsupported")
209 | def test_bad_type_in_expect(self):
210 | " expect() does not accept dictionary arguments. "
211 | child = wexpect.spawn('cat', coverage_console_reader=True)
212 | with self.assertRaises(TypeError):
213 | child.expect({})
214 |
215 | @unittest.skipIf(wexpect.spawn_class_name == 'legacy_wexpect', "legacy unsupported")
216 | def test_cwd(self):
217 | " check keyword argument `cwd=' of wexpect.run() "
218 | try:
219 | os.mkdir('cwd_tmp')
220 | except:
221 | pass
222 | tmp_dir = os.path.realpath('cwd_tmp')
223 | child = wexpect.spawn('cmd', coverage_console_reader=True)
224 | child.expect('>')
225 | child.sendline('cd')
226 | child.expect('>')
227 | default = child.before.splitlines()[1]
228 | child.terminate()
229 | child = wexpect.spawn('cmd', cwd=tmp_dir, coverage_console_reader=True)
230 | child.expect('>')
231 | child.sendline('cd')
232 | child.expect('>')
233 | pwd_tmp = child.before.splitlines()[1]
234 | child.terminate()
235 | self.assertNotEqual(default, pwd_tmp)
236 | self.assertEqual(tmp_dir, _u(pwd_tmp))
237 |
238 | def _test_searcher_as(self, searcher, plus=None):
239 | # given,
240 | given_words = ['alpha', 'beta', 'gamma', 'delta', ]
241 | given_search = given_words
242 | if searcher == wexpect.searcher_re:
243 | given_search = [re.compile(word) for word in given_words]
244 | if plus is not None:
245 | given_search = given_search + [plus]
246 | search_string = searcher(given_search)
247 | basic_fmt = '\n {0}: {1}'
248 | fmt = basic_fmt
249 | if searcher is wexpect.searcher_re:
250 | fmt = '\n {0}: re.compile({1})'
251 | expected_output = '{0}:'.format(searcher.__name__)
252 | idx = 0
253 | for word in given_words:
254 | expected_output += fmt.format(idx, '"{0}"'.format(word))
255 | idx += 1
256 | if plus is not None:
257 | if plus == wexpect.EOF:
258 | expected_output += basic_fmt.format(idx, 'EOF')
259 | elif plus == wexpect.TIMEOUT:
260 | expected_output += basic_fmt.format(idx, 'TIMEOUT')
261 |
262 | # exercise,
263 | self.assertEqual(search_string.__str__(), expected_output)
264 |
265 | @unittest.skipIf(wexpect.spawn_class_name == 'legacy_wexpect', "legacy unsupported")
266 | def test_searcher_as_string(self):
267 | " check searcher_string(..).__str__() "
268 | self._test_searcher_as(wexpect.searcher_string)
269 |
270 | @unittest.skipIf(wexpect.spawn_class_name == 'legacy_wexpect', "legacy unsupported")
271 | def test_searcher_as_string_with_EOF(self):
272 | " check searcher_string(..).__str__() that includes EOF "
273 | self._test_searcher_as(wexpect.searcher_string, plus=wexpect.EOF)
274 |
275 | @unittest.skipIf(wexpect.spawn_class_name == 'legacy_wexpect', "legacy unsupported")
276 | def test_searcher_as_string_with_TIMEOUT(self):
277 | " check searcher_string(..).__str__() that includes TIMEOUT "
278 | self._test_searcher_as(wexpect.searcher_string, plus=wexpect.TIMEOUT)
279 |
280 | @unittest.skipIf(wexpect.spawn_class_name == 'legacy_wexpect', "legacy unsupported")
281 | def test_searcher_re_as_string(self):
282 | " check searcher_re(..).__str__() "
283 | self._test_searcher_as(wexpect.searcher_re)
284 |
285 | @unittest.skipIf(wexpect.spawn_class_name == 'legacy_wexpect', "legacy unsupported")
286 | def test_searcher_re_as_string_with_EOF(self):
287 | " check searcher_re(..).__str__() that includes EOF "
288 | self._test_searcher_as(wexpect.searcher_re, plus=wexpect.EOF)
289 |
290 | @unittest.skipIf(wexpect.spawn_class_name == 'legacy_wexpect', "legacy unsupported")
291 | def test_searcher_re_as_string_with_TIMEOUT(self):
292 | " check searcher_re(..).__str__() that includes TIMEOUT "
293 | self._test_searcher_as(wexpect.searcher_re, plus=wexpect.TIMEOUT)
294 |
295 | @unittest.skipIf(wexpect.spawn_class_name == 'legacy_wexpect', "legacy unsupported")
296 | def test_exception_tb(self):
297 | " test get_trace() filters away wexpect/__init__.py calls. "
298 | p = wexpect.spawn('sleep 1', coverage_console_reader=True)
299 | try:
300 | p.expect('BLAH')
301 | except wexpect.ExceptionPexpect as e:
302 | # get_trace should filter out frames in wexpect's own code
303 | tb = e.get_trace()
304 | # exercise,
305 | assert 'raise ' not in tb
306 | assert 'wexpect/__init__.py' not in tb
307 | else:
308 | assert False, "Should have raised an exception."
309 |
310 | if __name__ == '__main__':
311 | unittest.main()
312 |
313 | suite = unittest.makeSuite(TestCaseMisc,'test')
314 |
--------------------------------------------------------------------------------
/tests/test_parametric_printer.py:
--------------------------------------------------------------------------------
1 | import wexpect
2 | import unittest
3 | import sys
4 | import os
5 | import time
6 | from tests import PexpectTestCase
7 |
8 | class TestCaseParametricPrinter(PexpectTestCase.PexpectTestCase):
9 | def test_all_line_length (self):
10 |
11 | here = os.path.dirname(os.path.abspath(__file__))
12 | sys.path.insert(0, here)
13 |
14 | # With quotes (C:\Program Files\Python37\python.exe needs quotes)
15 | python_executable = '"' + sys.executable + '" '
16 | child_script = here + '\\parametric_printer.py'
17 |
18 | self.prompt = '> '
19 |
20 | # Start the child process
21 | self.p = wexpect.spawn(python_executable + ' ' + child_script)
22 | # Wait for prompt
23 | self.p.expect(self.prompt)
24 |
25 | self._test(['a'], range(1,200), [1], [0])
26 |
27 | self.p.terminate()
28 |
29 | def test_random(self):
30 |
31 | here = os.path.dirname(os.path.abspath(__file__))
32 | sys.path.insert(0, here)
33 |
34 | # With quotes (C:\Program Files\Python37\python.exe needs quotes)
35 | python_executable = '"' + sys.executable + '" '
36 | child_script = here + '\\parametric_printer.py'
37 |
38 | self.prompt = '> '
39 |
40 | # Start the child process
41 | self.p = wexpect.spawn(python_executable + ' ' + child_script)
42 | # Wait for prompt
43 | self.p.expect(self.prompt)
44 |
45 | self._test(['a', 'b', 'c'], [1, 2, 4, 8], [1, 2, 4, 8], [-1, 0, 1, 2])
46 | self._test(['a', 'b', 'c'], [16], [16], [-1, 0, 1])
47 | self._test(['a', 'b', 'c'], [16, 32, 64], [16, 32, 64], [-1, 0])
48 |
49 | self.p.terminate()
50 |
51 | @unittest.skipIf(wexpect.spawn_class_name == 'legacy_wexpect', "legacy has bug around refreshing long consoles")
52 | def test_long_console(self):
53 |
54 | here = os.path.dirname(os.path.abspath(__file__))
55 | sys.path.insert(0, here)
56 |
57 | # With quotes (C:\Program Files\Python37\python.exe needs quotes)
58 | python_executable = '"' + sys.executable + '" '
59 | child_script = here + '\\parametric_printer.py'
60 |
61 | self.prompt = '> '
62 |
63 | # Start the child process
64 | self.p = wexpect.spawn(python_executable + ' ' + child_script)
65 | # Wait for prompt
66 | self.p.expect(self.prompt)
67 |
68 | self._test(['a', 'b', 'c', 'd', 'e', 'f'], [8, 16, 32, 64], [64, 128, 256], [-1, 0])
69 |
70 | self.p.terminate()
71 |
72 | def _test(self, character_list, character_count_list, line_count_list, speed_ms_list):
73 |
74 | # print(f'character_list: {character_list} character_count_list: {character_count_list} line_count_list: {line_count_list} speed_ms_list: {speed_ms_list}')
75 | for character in character_list:
76 | for character_count in character_count_list:
77 | for line_count in line_count_list:
78 | for speed_ms in speed_ms_list:
79 | command = f'{character},{character_count},{line_count},{speed_ms}'
80 | self.p.sendline(command)
81 | self.p.expect(self.prompt)
82 | expected = [character*character_count] * line_count
83 | try:
84 | self.assertEqual(self.p.before.splitlines()[1:-1], expected)
85 | except:
86 | raise
87 |
88 | if __name__ == '__main__':
89 | unittest.main()
90 |
91 | suite = unittest.makeSuite(TestCaseParametricPrinter,'test')
92 |
--------------------------------------------------------------------------------
/tests/test_parametric_printer_coverage.py:
--------------------------------------------------------------------------------
1 | import wexpect
2 | import unittest
3 | import sys
4 | import os
5 | import time
6 | from tests import PexpectTestCase
7 |
8 | @unittest.skipIf(wexpect.spawn_class_name == 'legacy_wexpect', "legacy unsupported")
9 | class TestCaseParametricPrinter(PexpectTestCase.PexpectTestCase):
10 | def test_all_line_length (self):
11 |
12 | here = os.path.dirname(os.path.abspath(__file__))
13 | sys.path.insert(0, here)
14 |
15 | # With quotes (C:\Program Files\Python37\python.exe needs quotes)
16 | python_executable = '"' + sys.executable + '" '
17 | child_script = here + '\\parametric_printer.py'
18 |
19 | self.prompt = '> '
20 |
21 | # Start the child process
22 | self.p = wexpect.spawn(python_executable + ' ' + child_script, coverage_console_reader=True)
23 | # Wait for prompt
24 | self.p.expect(self.prompt)
25 |
26 | self._test(['a'], range(1,200), [1], [0])
27 |
28 | self.p.terminate()
29 |
30 | def test_long_console(self):
31 |
32 | here = os.path.dirname(os.path.abspath(__file__))
33 | sys.path.insert(0, here)
34 |
35 | # With quotes (C:\Program Files\Python37\python.exe needs quotes)
36 | python_executable = '"' + sys.executable + '" '
37 | child_script = here + '\\parametric_printer.py'
38 |
39 | self.prompt = '> '
40 |
41 | # Start the child process
42 | self.p = wexpect.spawn(python_executable + ' ' + child_script, coverage_console_reader=True)
43 | # Wait for prompt
44 | self.p.expect(self.prompt)
45 |
46 | self._test(['a', 'b', 'c', 'd', 'e', 'f'], [8, 16, 32, 64], [64, 128, 256], [-1, 0])
47 |
48 | self.p.terminate()
49 |
50 | def _test(self, character_list, character_count_list, line_count_list, speed_ms_list):
51 |
52 | # print(f'character_list: {character_list} character_count_list: {character_count_list} line_count_list: {line_count_list} speed_ms_list: {speed_ms_list}')
53 | for character in character_list:
54 | for character_count in character_count_list:
55 | for line_count in line_count_list:
56 | for speed_ms in speed_ms_list:
57 | command = f'{character},{character_count},{line_count},{speed_ms}'
58 | self.p.sendline(command)
59 | self.p.expect(self.prompt)
60 | expected = [character*character_count] * line_count
61 | try:
62 | self.assertEqual(self.p.before.splitlines()[1:-1], expected)
63 | except:
64 | raise
65 |
66 | if __name__ == '__main__':
67 | unittest.main()
68 |
69 | suite = unittest.makeSuite(TestCaseParametricPrinter,'test')
70 |
--------------------------------------------------------------------------------
/tests/test_readline.py:
--------------------------------------------------------------------------------
1 | import wexpect
2 | import time
3 | import sys
4 | import os
5 | import unittest
6 | from tests import PexpectTestCase
7 |
8 | here = os.path.dirname(os.path.abspath(__file__))
9 | sys.path.insert(0, here)
10 |
11 | print(wexpect.__version__)
12 |
13 | # With quotes (C:\Program Files\Python37\python.exe needs quotes)
14 | python_executable = '"' + sys.executable + '" '
15 | child_script = here + '\\lines_printer.py'
16 |
17 | class ReadLineTestCase(PexpectTestCase.PexpectTestCase):
18 | def testReadline(self):
19 | fooPath = python_executable + ' ' + child_script
20 | prompt = ': '
21 | num = 5
22 |
23 | # Start the child process
24 | p = wexpect.spawn(fooPath)
25 | # Wait for prompt
26 | p.expect(prompt)
27 | p.sendline(str(num))
28 | p.expect('Bye!\r\n')
29 | expected_lines = p.before.splitlines(True) # Keep the line end
30 | expected_lines += [p.match.group()]
31 |
32 | # Termination of the SpawnSocket is slow. We have to wait to prevent the failure of the next test.
33 | if wexpect.spawn_class_name == 'SpawnSocket':
34 | p.wait()
35 |
36 | # Start the child process
37 | p = wexpect.spawn(fooPath)
38 | # Wait for prompt
39 | p.expect(prompt)
40 |
41 | p.sendline(str(num))
42 | for i in range(num +2): # +1 the line of sendline +1: Bye
43 | line = p.readline()
44 | self.assertEqual(expected_lines[i], line)
45 |
46 | # Termination of the SpawnSocket is slow. We have to wait to prevent the failure of the next test.
47 | if wexpect.spawn_class_name == 'SpawnSocket':
48 | p.wait()
49 |
50 | # Start the child process
51 | p = wexpect.spawn(fooPath)
52 | # Wait for prompt
53 | p.expect(prompt)
54 |
55 | p.sendline(str(num))
56 | readlines_lines = p.readlines()
57 | self.assertEqual(expected_lines, readlines_lines)
58 |
59 | # Termination of the SpawnSocket is slow. We have to wait to prevent the failure of the next test.
60 | if wexpect.spawn_class_name == 'SpawnSocket':
61 | p.wait()
62 |
63 |
64 | if __name__ == '__main__':
65 | unittest.main()
66 |
67 | suite = unittest.makeSuite(ReadLineTestCase,'test')
68 |
--------------------------------------------------------------------------------
/tests/test_run.py:
--------------------------------------------------------------------------------
1 | import wexpect
2 | import unittest
3 | import subprocess
4 | import tempfile
5 | import sys
6 | import os
7 | import re
8 | from tests import PexpectTestCase
9 |
10 | unicode_type = str
11 |
12 |
13 | def timeout_callback(values):
14 | if values["event_count"] > 3:
15 | return 1
16 | return 0
17 |
18 |
19 | def function_events_callback(values):
20 | try:
21 | previous_echoed = values["child_result_list"][-1]
22 | # lets pick up the line with valuable
23 | previous_echoed = previous_echoed.splitlines()[-3].strip()
24 | if previous_echoed.endswith("reserved."):
25 | return "echo stage-1\r\n"
26 | if previous_echoed.endswith("stage-1"):
27 | return "echo stage-2\r\n"
28 | elif previous_echoed.endswith("stage-2"):
29 | return "echo stage-3\r\n"
30 | elif previous_echoed.endswith("stage-3"):
31 | return "exit\r\n"
32 | else:
33 | raise Exception("Unexpected output {0}".format(previous_echoed))
34 | except IndexError:
35 | return "echo stage-1\r\n"
36 |
37 |
38 | class RunFuncTestCase(PexpectTestCase.PexpectTestCase):
39 | runfunc = staticmethod(wexpect.run)
40 | cr = '\r'
41 | empty = ''
42 | prep_subprocess_out = staticmethod(lambda x: x)
43 |
44 | def test_run_exit(self):
45 | (data, exitstatus) = self.runfunc('python exit1.py', withexitstatus=1)
46 | assert exitstatus == 1, "Exit status of 'python exit1.py' should be 1."
47 |
48 | def test_run(self):
49 | the_old_way = subprocess.Popen(
50 | args=['uname', '-m', '-n'],
51 | stdout=subprocess.PIPE
52 | ).communicate()[0].rstrip()
53 |
54 | (the_new_way, exitstatus) = self.runfunc(
55 | 'uname -m -n', withexitstatus=1)
56 | the_new_way = the_new_way.replace(self.cr, self.empty).rstrip()
57 |
58 | self.assertEqual(self.prep_subprocess_out(the_old_way).decode('utf-8'), the_new_way)
59 | self.assertEqual(exitstatus, 0)
60 |
61 | def test_run_callback(self):
62 | # TODO it seems like this test could block forever if run fails...
63 | events = {wexpect.TIMEOUT: timeout_callback}
64 | self.runfunc("cat", timeout=1, events=events)
65 |
66 | def test_run_bad_exitstatus(self):
67 | (the_new_way, exitstatus) = self.runfunc(
68 | 'ls -l /najoeufhdnzkxjd', withexitstatus=1)
69 | self.assertNotEqual(exitstatus, 0)
70 |
71 | def test_run_event_as_string(self):
72 | re_flags = re.DOTALL | re.MULTILINE
73 | events = {
74 | # second match on 'abc', echo 'def'
75 | re.compile('abc.*>', re_flags): 'echo "def"\r\n',
76 | # final match on 'def': exit
77 | re.compile('def.*>', re_flags): 'exit\r\n',
78 | # first match on 'GO:' prompt, echo 'abc'
79 | re.compile('Microsoft.*>', re_flags): 'echo "abc"\r\n'
80 | }
81 |
82 | (data, exitstatus) = wexpect.run(
83 | 'cmd',
84 | withexitstatus=True,
85 | events=events,
86 | timeout=5)
87 | assert exitstatus == 0
88 |
89 | def test_run_event_as_function(self):
90 | events = {'>': function_events_callback}
91 |
92 | (data, exitstatus) = wexpect.run(
93 | 'cmd',
94 | withexitstatus=True,
95 | events=events,
96 | timeout=10)
97 | assert exitstatus == 0
98 |
99 | # Unsupported
100 | # def test_run_event_as_method(self):
101 | # events = {
102 | # '>': RunFuncTestCase._method_events_callback
103 | # }
104 | #
105 | # (data, exitstatus) = wexpect.run(
106 | # 'cmd',
107 | # withexitstatus=True,
108 | # events=events,
109 | # timeout=10)
110 | # assert exitstatus == 0
111 |
112 | def test_run_event_typeerror(self):
113 | events = {'>': -1}
114 | with self.assertRaises(TypeError):
115 | wexpect.run('cmd',
116 | withexitstatus=True,
117 | events=events,
118 | timeout=10)
119 |
120 | def _method_events_callback(self, values):
121 | try:
122 | previous_echoed = (values["child_result_list"][-1].decode()
123 | .split("\n")[-2].strip())
124 | if previous_echoed.endswith("foo1"):
125 | return "echo foo2\n"
126 | elif previous_echoed.endswith("foo2"):
127 | return "echo foo3\n"
128 | elif previous_echoed.endswith("foo3"):
129 | return "exit\n"
130 | else:
131 | raise Exception("Unexpected output {0!r}"
132 | .format(previous_echoed))
133 | except IndexError:
134 | return "echo foo1\n"
135 |
136 |
137 | if __name__ == '__main__':
138 | unittest.main()
139 |
--------------------------------------------------------------------------------
/tests/test_timeout_pattern.py:
--------------------------------------------------------------------------------
1 | import multiprocessing
2 | import unittest
3 | import subprocess
4 | import time
5 | import signal
6 | import sys
7 | import os
8 |
9 | import wexpect
10 | from tests import PexpectTestCase
11 |
12 | @unittest.skipIf(wexpect.spawn_class_name == 'legacy_wexpect', "legacy unsupported")
13 | class Exp_TimeoutTestCase(PexpectTestCase.PexpectTestCase):
14 | def test_matches_exp_timeout (self):
15 | '''This tests that we can raise and catch TIMEOUT.
16 | '''
17 | try:
18 | raise wexpect.TIMEOUT("TIMEOUT match test")
19 | except wexpect.TIMEOUT:
20 | pass
21 | #print "Correctly caught TIMEOUT when raising TIMEOUT."
22 | else:
23 | self.fail('TIMEOUT not caught by an except TIMEOUT clause.')
24 |
25 | def test_pattern_printout (self):
26 | '''Verify that a TIMEOUT returns the proper patterns it is trying to match against.
27 | Make sure it is returning the pattern from the correct call.'''
28 | try:
29 | p = wexpect.spawn('cat')
30 | p.sendline('Hello')
31 | p.expect('Hello')
32 | p.expect('Goodbye',timeout=5)
33 | except wexpect.TIMEOUT:
34 | assert p.match_index == None
35 | else:
36 | self.fail("Did not generate a TIMEOUT exception.")
37 |
38 | # Termination of the SpawnSocket is slow. We have to wait to prevent the failure of the next test.
39 | if wexpect.spawn_class_name == 'SpawnSocket':
40 | p.terminate()
41 |
42 | def test_exp_timeout_notThrown (self):
43 | '''Verify that a TIMEOUT is not thrown when we match what we expect.'''
44 | try:
45 | p = wexpect.spawn('cat')
46 | p.sendline('Hello')
47 | p.expect('Hello')
48 | except wexpect.TIMEOUT:
49 | self.fail("TIMEOUT caught when it shouldn't be raised because we match the proper pattern.")
50 |
51 | # Termination of the SpawnSocket is slow. We have to wait to prevent the failure of the next test.
52 | if wexpect.spawn_class_name == 'SpawnSocket':
53 | p.terminate()
54 |
55 | def test_stacktraceMunging (self):
56 | '''Verify that the stack trace returned with a TIMEOUT instance does not contain references to wexpect.'''
57 | try:
58 | p = wexpect.spawn('cat')
59 | p.sendline('Hello')
60 | p.expect('Goodbye',timeout=5)
61 | except wexpect.TIMEOUT:
62 | err = sys.exc_info()[1]
63 | if err.get_trace().count("wexpect/__init__.py") != 0:
64 | self.fail("The TIMEOUT get_trace() referenced wexpect.py. "
65 | "It should only reference the caller.\n" + err.get_trace())
66 |
67 | # Termination of the SpawnSocket is slow. We have to wait to prevent the failure of the next test.
68 | if wexpect.spawn_class_name == 'SpawnSocket':
69 | p.terminate()
70 |
71 | def test_correctStackTrace (self):
72 | '''Verify that the stack trace returned with a TIMEOUT instance correctly handles function calls.'''
73 | def nestedFunction (spawnInstance):
74 | spawnInstance.expect("junk", timeout=3)
75 |
76 | try:
77 | p = wexpect.spawn('cat')
78 | p.sendline('Hello')
79 | nestedFunction(p)
80 | except wexpect.TIMEOUT:
81 | err = sys.exc_info()[1]
82 | if err.get_trace().count("nestedFunction") == 0:
83 | self.fail("The TIMEOUT get_trace() did not show the call "
84 | "to the nestedFunction function.\n" + str(err) + "\n"
85 | + err.get_trace())
86 |
87 | # Termination of the SpawnSocket is slow. We have to wait to prevent the failure of the next test.
88 | if wexpect.spawn_class_name == 'SpawnSocket':
89 | p.terminate()
90 |
91 | if __name__ == '__main__':
92 | unittest.main()
93 |
94 | suite = unittest.makeSuite(Exp_TimeoutTestCase,'test')
95 |
--------------------------------------------------------------------------------
/tests/utils.py:
--------------------------------------------------------------------------------
1 | import os
2 |
3 | def no_coverage_env():
4 | "Return a copy of os.environ that won't trigger coverage measurement."
5 | env = os.environ.copy()
6 | env.pop('COV_CORE_SOURCE', None)
7 | return env
--------------------------------------------------------------------------------
/tox.ini:
--------------------------------------------------------------------------------
1 | # tox.ini
2 | # Run tests of wexpect in multiple configuration.
3 |
4 | [tox]
5 | # The following configuration will run automatically.
6 | envlist = py{37}-{legacy_wexpect,spawn_pipe,spawn_socket},installed,pyinstaller
7 |
8 |
9 | [testenv]
10 | description = Unit tests
11 |
12 | # Set environment variables to select the proper configuration for each envirnment.
13 | setenv =
14 | spawn_pipe: WEXPECT_SPAWN_CLASS=SpawnPipe
15 | legacy_wexpect: WEXPECT_SPAWN_CLASS=legacy_wexpect
16 | spawn_socket: WEXPECT_SPAWN_CLASS=SpawnSocket
17 |
18 | commands =
19 | # install the dependencies:
20 | pip install .[test]
21 |
22 | # Run the test itself
23 | coverage run --parallel-mode -m unittest
24 |
25 | # Combine the parallel results
26 | coverage combine
27 |
28 | # Dump coverage summary to console
29 | coverage report --omit=tests/*,site-packages/*
30 |
31 | # Convert coverage report to standard xml formula the filename includes the tox environment name
32 | # https://tox.readthedocs.io/en/latest/config.html#environment-variable-substitutions
33 | coverage xml --omit=tests/*,site-packages -o {env:TOX_ENV_NAME}_coverage.xml
34 |
35 |
36 | [testenv:installed]
37 | # normal tests test the cloned files. This testenv tests the installation itself, with the default
38 | # spawn class.
39 | description = Unit tests installed, and default spawn class.
40 |
41 | changedir = test_01_installed
42 |
43 | whitelist_externals =
44 | cp
45 |
46 | commands =
47 | # copy all testcase into working dir
48 | cp -r ../tests tests
49 |
50 |
51 | # Run the test itself. Running all tests is not needed, because it just test the installation,
52 | # not functions.
53 | python -m unittest tests.test_misc
54 |
55 |
56 | [testenv:pyinstaller]
57 | # Test if wexpect working with pyinstaller. See #12 for more details.
58 | description = Unit tests pyinstaller
59 |
60 | whitelist_externals =
61 | pyinstaller
62 | pyinstaller_test
63 |
64 | setenv =
65 | WEXPECT_SPAWN_CLASS=SpawnPipe
66 |
67 | commands =
68 | # install the dependencies:
69 | pip install .[test]
70 |
71 | # create wexpect executable for console-reader.
72 | pyinstaller wexpect.spec
73 |
74 | # create test executable, to thest the console-reader
75 | pyinstaller tests/pyinstaller_test.py
76 |
77 | # run test:
78 | dist\pyinstaller_test\pyinstaller_test.exe
79 |
--------------------------------------------------------------------------------
/wexpect.spec:
--------------------------------------------------------------------------------
1 | # -*- mode: python ; coding: utf-8 -*-
2 |
3 | block_cipher = None
4 |
5 |
6 | a = Analysis(['wexpect\\__main__.py'],
7 | pathex=[],
8 | binaries=[],
9 | datas=[],
10 | hiddenimports=[],
11 | hookspath=[],
12 | runtime_hooks=[],
13 | excludes=[],
14 | win_no_prefer_redirects=False,
15 | win_private_assemblies=False,
16 | cipher=block_cipher,
17 | noarchive=False)
18 | pyz = PYZ(a.pure, a.zipped_data,
19 | cipher=block_cipher)
20 | exe = EXE(pyz,
21 | a.scripts,
22 | [],
23 | exclude_binaries=True,
24 | name='wexpect',
25 | debug=False,
26 | bootloader_ignore_signals=False,
27 | strip=False,
28 | upx=True,
29 | console=True )
30 | coll = COLLECT(exe,
31 | a.binaries,
32 | a.zipfiles,
33 | a.datas,
34 | strip=False,
35 | upx=True,
36 | upx_exclude=[],
37 | name='wexpect')
38 |
--------------------------------------------------------------------------------
/wexpect/__init__.py:
--------------------------------------------------------------------------------
1 | # __init__.py
2 |
3 | import os
4 | import pkg_resources
5 |
6 | try:
7 | spawn_class_name = os.environ['WEXPECT_SPAWN_CLASS']
8 | except KeyError:
9 | spawn_class_name = 'SpawnPipe'
10 |
11 | if spawn_class_name == 'legacy_wexpect':
12 | from .legacy_wexpect import ExceptionPexpect
13 | from .legacy_wexpect import EOF
14 | from .legacy_wexpect import TIMEOUT
15 | from .legacy_wexpect import spawn
16 | from .legacy_wexpect import run
17 | from .legacy_wexpect import split_command_line
18 | from .legacy_wexpect import join_args
19 | from .legacy_wexpect import ConsoleReader
20 | from .legacy_wexpect import __version__
21 | from .legacy_wexpect import searcher_string
22 | from .legacy_wexpect import searcher_re
23 |
24 | __all__ = ['ExceptionPexpect', 'EOF', 'TIMEOUT', 'spawn', 'run', 'split_command_line',
25 | '__version__', 'ConsoleReader', 'join_args', 'searcher_string', 'searcher_re',
26 | 'spawn_class_name']
27 |
28 | else:
29 |
30 | from .wexpect_util import split_command_line
31 | from .wexpect_util import join_args
32 | from .wexpect_util import ExceptionPexpect
33 | from .wexpect_util import EOF
34 | from .wexpect_util import TIMEOUT
35 |
36 | from .console_reader import ConsoleReaderSocket
37 | from .console_reader import ConsoleReaderPipe
38 |
39 | from .host import SpawnSocket
40 | from .host import SpawnPipe
41 | from .host import run
42 | from .host import searcher_string
43 | from .host import searcher_re
44 |
45 | try:
46 | spawn = globals()[spawn_class_name]
47 | except KeyError:
48 | print(f'Error: no spawn class: {spawn_class_name}')
49 | raise
50 |
51 | # The version is handled by the package: pbr, which derives the version from the git tags.
52 | try:
53 | __version__ = pkg_resources.require("wexpect")[0].version
54 | except Exception: # pragma: no cover
55 | __version__ = '0.0.1.unkowndev0'
56 |
57 | __all__ = ['ExceptionPexpect', 'EOF', 'TIMEOUT', 'ConsoleReaderSocket', 'ConsoleReaderPipe',
58 | 'spawn', 'SpawnSocket', 'SpawnPipe', 'run', '__version__', 'spawn_class_name']
59 |
--------------------------------------------------------------------------------
/wexpect/__main__.py:
--------------------------------------------------------------------------------
1 | import argparse
2 | import sys
3 | import logging
4 | import traceback
5 |
6 | import wexpect.console_reader as console_reader
7 | import wexpect.wexpect_util as wexpect_util
8 |
9 |
10 | logger = logging.getLogger('wexpect')
11 | logger.info('Hello')
12 |
13 | def main():
14 | try:
15 | parser = argparse.ArgumentParser(description='Wexpect: executable automation for Windows.')
16 |
17 | parser.add_argument('--console_reader_class', type=str,
18 | help='Class name of the console reader class.')
19 |
20 | parser.add_argument('command', type=str, nargs='+', help='Command to be run with its arguments')
21 | parser.add_argument('--host_pid', type=int, help='Host process process ID')
22 | parser.add_argument('--codepage', type=str, help='Codepage')
23 | parser.add_argument('--window_size_x', type=int, help='Width of the console window', default=80)
24 | parser.add_argument('--window_size_y', type=int, help='Height of the console window', default=25)
25 | parser.add_argument('--buffer_size_x', type=int, help='Width of the console buffer', default=80)
26 | parser.add_argument('--buffer_size_y', type=int, help='Height of the console buffer',
27 | default=16000)
28 | parser.add_argument('--local_echo', type=str, help='Echo sent characters', default=True)
29 | parser.add_argument('--interact', type=str, help='Show console window', default=False)
30 | parser.add_argument('--port', type=int, help=
31 | "If the console reader class is SpawnSocket, this option specifies the "
32 | "socket's port.", default=False)
33 |
34 | try:
35 | args = parser.parse_args()
36 | except SystemExit: # pragma: no cover
37 | logger.error('Unexpected exception.')
38 | logger.info(traceback.format_exc())
39 | raise
40 |
41 | logger.info(f'Starter arguments: {args}')
42 |
43 | if args.console_reader_class == 'ConsoleReaderSocket':
44 | conole_reader_class = console_reader.ConsoleReaderSocket
45 | elif args.console_reader_class == 'ConsoleReaderPipe':
46 | conole_reader_class = console_reader.ConsoleReaderPipe
47 |
48 | command = wexpect_util.join_args(args.command)
49 |
50 | cons = conole_reader_class(
51 | path=command, host_pid=args.host_pid, codepage=args.codepage, port=args.port,
52 | window_size_x=args.window_size_x, window_size_y=args.window_size_y,
53 | buffer_size_x=args.buffer_size_x, buffer_size_y=args.buffer_size_y,
54 | local_echo=wexpect_util.str2bool(args.local_echo), interact=wexpect_util.str2bool(args.interact))
55 |
56 | logger.info(f'Exiting with status: {cons.child_exitstatus}')
57 | sys.exit(cons.child_exitstatus)
58 |
59 | except Exception as e: # pragma: no cover
60 | logger.error('Unexpected exception.')
61 | logger.info(traceback.format_exc())
62 | raise
63 |
64 | if __name__ == "__main__":
65 | main()
66 |
--------------------------------------------------------------------------------
/wexpect/console_reader.py:
--------------------------------------------------------------------------------
1 | """Wexpect is a Windows variant of pexpect https://pexpect.readthedocs.io.
2 |
3 | Wexpect is a Python module for spawning child applications and controlling
4 | them automatically.
5 |
6 | console_reader Implements a virtual terminal, and starts the child program.
7 | The main wexpect.spawn class connect to this class to reach the child's terminal.
8 | """
9 |
10 | import time
11 | import logging
12 | import os
13 | import traceback
14 | import psutil
15 | from io import StringIO
16 |
17 | import ctypes
18 | from ctypes import windll
19 | import win32console
20 | import win32process
21 | import win32con
22 | import win32file
23 | import win32gui
24 | import win32pipe
25 | import socket
26 |
27 | from .wexpect_util import init_logger
28 | from .wexpect_util import EOF_CHAR
29 | from .wexpect_util import SIGNAL_CHARS
30 | from .wexpect_util import TIMEOUT
31 |
32 | #
33 | # System-wide constants
34 | #
35 | screenbufferfillchar = '\4'
36 | maxconsoleY = 8000
37 | default_port = 4321
38 |
39 | #
40 | # Create logger: We write logs only to file. Printing out logs are dangerous, because of the deep
41 | # console manipulation.
42 | #
43 | logger = logging.getLogger('wexpect')
44 |
45 |
46 | class ConsoleReaderBase:
47 | """Consol class (aka. client-side python class) for the child.
48 |
49 | This class initialize the console starts the child in it and reads the console periodically.
50 | """
51 |
52 | def __init__(self, path, host_pid, codepage=None, window_size_x=80, window_size_y=25,
53 | buffer_size_x=80, buffer_size_y=16000, local_echo=True, interact=False, **kwargs):
54 | """Initialize the console starts the child in it and reads the console periodically.
55 |
56 | Args:
57 | path (str): Child's executable with arguments.
58 | parent_pid (int): Parent (aka. host) process process-ID
59 | codepage (:obj:, optional): Output console code page.
60 | """
61 | self.lastRead = 0
62 | self.__bufferY = 0
63 | self.lastReadData = ""
64 | self.totalRead = 0
65 | self.__buffer = StringIO()
66 | self.__currentReadCo = win32console.PyCOORDType(0, 0)
67 | self.pipe = None
68 | self.connection = None
69 | self.consin = None
70 | self.consout = None
71 | self.local_echo = local_echo
72 | self.console_pid = os.getpid()
73 | self.host_pid = host_pid
74 | self.host_process = psutil.Process(host_pid)
75 | self.child_process = None
76 | self.child_pid = None
77 | self.enable_signal_chars = True
78 | self.timeout = 30
79 | self.child_exitstatus = None
80 |
81 | logger.info(f'ConsoleReader started. location {os.path.abspath(__file__)}')
82 |
83 | if codepage is None:
84 | codepage = windll.kernel32.GetACP()
85 |
86 | try:
87 | logger.info("Setting console output code page to %s" % codepage)
88 | win32console.SetConsoleOutputCP(codepage)
89 | logger.info(
90 | "Console output code page: %s" % ctypes.windll.kernel32.GetConsoleOutputCP())
91 | except Exception as e: # pragma: no cover
92 | # I hope this code is unreachable...
93 | logger.error(e)
94 |
95 | try:
96 | self.create_connection(**kwargs)
97 | logger.info('Spawning %s' % path)
98 | try:
99 | self.initConsole()
100 | si = win32process.GetStartupInfo()
101 | self.__childProcess, _, self.child_pid, self.child_tid = win32process.CreateProcess(
102 | None, path, None, None, False, 0, None, None, si)
103 | self.child_process = psutil.Process(self.child_pid)
104 |
105 | logger.info(f'Child pid: {self.child_pid} Console pid: {self.console_pid}')
106 |
107 | except Exception: # pragma: no cover
108 | # I hope this code is unreachable...
109 | logger.error(traceback.format_exc())
110 | return
111 |
112 | if interact:
113 | self.interact()
114 | self.interact()
115 |
116 | self.read_loop()
117 | except Exception: # pragma: no cover
118 | # I hope this code is unreachable...
119 | logger.error(traceback.format_exc())
120 | finally:
121 | try:
122 | self.terminate_child()
123 | time.sleep(.01)
124 | self.send_to_host(self.readConsoleToCursor())
125 | self.sendeof()
126 | time.sleep(.1)
127 | self.close_connection()
128 | logger.info('Console finished.')
129 | except Exception: # pragma: no cover
130 | # I hope this code is unreachable...
131 | logger.error(traceback.format_exc())
132 |
133 | def read_loop(self):
134 |
135 | while True:
136 | if not self.isalive(self.host_process):
137 | logger.info('Host process has been died.')
138 | return
139 |
140 | self.child_exitstatus = win32process.GetExitCodeProcess(self.__childProcess)
141 | if self.child_exitstatus != win32con.STILL_ACTIVE:
142 | logger.info(f'Child finished with code: {self.child_exitstatus}')
143 | return
144 |
145 | consinfo = self.consout.GetConsoleScreenBufferInfo()
146 | cursorPos = consinfo['CursorPosition']
147 |
148 | if cursorPos.Y > maxconsoleY:
149 | '''If the console output becomes long, we suspend the child, read all output then
150 | clear the console before we resume the child.
151 | '''
152 | logger.info('cursorPos %s' % cursorPos)
153 | self.suspend_child()
154 | time.sleep(.2)
155 | self.send_to_host(self.readConsoleToCursor())
156 | self.refresh_console()
157 | self.resume_child()
158 | else:
159 | self.send_to_host(self.readConsoleToCursor())
160 |
161 | s = self.get_from_host()
162 | if s:
163 | logger.debug(f'get_from_host: {s}')
164 | else:
165 | logger.spam(f'get_from_host: {s}')
166 | if self.enable_signal_chars:
167 | for sig, char in SIGNAL_CHARS.items():
168 | if char in s:
169 | self.child_process.send_signal(sig)
170 | s = s.decode()
171 | self.write(s)
172 |
173 |
174 | time.sleep(.02)
175 |
176 | def suspend_child(self):
177 | """Pauses the main thread of the child process."""
178 | handle = windll.kernel32.OpenThread(win32con.THREAD_SUSPEND_RESUME, 0, self.child_tid)
179 | win32process.SuspendThread(handle)
180 |
181 | def resume_child(self):
182 | """Un-pauses the main thread of the child process."""
183 | handle = windll.kernel32.OpenThread(win32con.THREAD_SUSPEND_RESUME, 0, self.child_tid)
184 | win32process.ResumeThread(handle)
185 |
186 | def refresh_console(self):
187 | """Clears the console after pausing the child and
188 | reading all the data currently on the console."""
189 |
190 | orig = win32console.PyCOORDType(0, 0)
191 | self.consout.SetConsoleCursorPosition(orig)
192 | self.__currentReadCo.X = 0
193 | self.__currentReadCo.Y = 0
194 | writelen = self.__consSize.X * self.__consSize.Y
195 | # Use NUL as fill char because it displays as whitespace
196 | # (if we interact() with the child)
197 | self.consout.FillConsoleOutputCharacter(screenbufferfillchar, writelen, orig)
198 |
199 | self.__bufferY = 0
200 | self.__buffer.truncate(0)
201 |
202 | def terminate_child(self):
203 | try:
204 | if self.child_process:
205 | self.child_process.kill()
206 | except psutil.NoSuchProcess:
207 | logger.info('The process has already died.')
208 | return
209 |
210 | def isalive(self, process):
211 | """True if the child is still alive, false otherwise"""
212 | try:
213 | self.exitstatus = process.wait(timeout=0)
214 | return False
215 | except psutil.TimeoutExpired:
216 | return True
217 |
218 | def write(self, s):
219 | """Writes input into the child consoles input buffer."""
220 |
221 | if len(s) == 0:
222 | return 0
223 | if s[-1] == '\n':
224 | s = s[:-1]
225 | records = [self.createKeyEvent(c) for c in str(s)]
226 | if not self.consout:
227 | return ""
228 |
229 | # Store the current cursor position to hide characters in local echo disabled mode
230 | # (workaround).
231 | consinfo = self.consout.GetConsoleScreenBufferInfo()
232 | startCo = consinfo['CursorPosition']
233 |
234 | # Send the string to console input
235 | wrote = self.consin.WriteConsoleInput(records)
236 |
237 | # Wait until all input has been recorded by the console.
238 | ts = time.time()
239 | while self.consin.PeekConsoleInput(8) != ():
240 | if time.time() > ts + len(s) * .1 + .5:
241 | break
242 | time.sleep(.05)
243 |
244 | # Hide characters in local echo disabled mode (workaround).
245 | if not self.local_echo:
246 | self.consout.FillConsoleOutputCharacter(screenbufferfillchar, len(s), startCo)
247 |
248 | return wrote
249 |
250 | def createKeyEvent(self, char):
251 | """Creates a single key record corrosponding to
252 | the ascii character char."""
253 |
254 | evt = win32console.PyINPUT_RECORDType(win32console.KEY_EVENT)
255 | evt.KeyDown = True
256 | evt.Char = char
257 | evt.RepeatCount = 1
258 | return evt
259 |
260 | def initConsole(self, consout=None, window_size_x=80, window_size_y=25, buffer_size_x=80,
261 | buffer_size_y=16000):
262 | if not consout:
263 | consout = self.getConsoleOut()
264 |
265 | self.consin = win32console.GetStdHandle(win32console.STD_INPUT_HANDLE)
266 |
267 | rect = win32console.PySMALL_RECTType(0, 0, window_size_x - 1, window_size_y - 1)
268 | consout.SetConsoleWindowInfo(True, rect)
269 | size = win32console.PyCOORDType(buffer_size_x, buffer_size_y)
270 | consout.SetConsoleScreenBufferSize(size)
271 | pos = win32console.PyCOORDType(0, 0)
272 | # Use NUL as fill char because it displays as whitespace
273 | # (if we interact() with the child)
274 | consout.FillConsoleOutputCharacter(screenbufferfillchar, size.X * size.Y, pos)
275 |
276 | consinfo = consout.GetConsoleScreenBufferInfo()
277 | self.__consSize = consinfo['Size']
278 | logger.info('self.__consSize: ' + str(self.__consSize))
279 | self.startCursorPos = consinfo['CursorPosition']
280 |
281 | def parseData(self, s):
282 | """Ensures that special characters are interpretted as
283 | newlines or blanks, depending on if there written over
284 | characters or screen-buffer-fill characters."""
285 |
286 | strlist = []
287 | for i, c in enumerate(s):
288 | if c == screenbufferfillchar:
289 | if (self.totalRead - self.lastRead + i + 1) % self.__consSize.X == 0:
290 | strlist.append('\r\n')
291 | else:
292 | strlist.append(c)
293 |
294 | s = ''.join(strlist)
295 | return s
296 |
297 | def getConsoleOut(self):
298 | consfile = win32file.CreateFile(
299 | 'CONOUT$',
300 | win32con.GENERIC_READ | win32con.GENERIC_WRITE,
301 | win32con.FILE_SHARE_READ | win32con.FILE_SHARE_WRITE,
302 | None,
303 | win32con.OPEN_EXISTING,
304 | 0,
305 | 0)
306 |
307 | self.consout = win32console.PyConsoleScreenBufferType(consfile)
308 | return self.consout
309 |
310 | def getCoord(self, offset):
311 | """Converts an offset to a point represented as a tuple."""
312 |
313 | x = offset % self.__consSize.X
314 | y = offset // self.__consSize.X
315 | return win32console.PyCOORDType(x, y)
316 |
317 | def getOffset(self, coord):
318 | """Converts a tuple-point to an offset."""
319 |
320 | return coord.X + coord.Y * self.__consSize.X
321 |
322 | def readConsole(self, startCo, endCo):
323 | """Reads the console area from startCo to endCo and returns it
324 | as a string."""
325 |
326 | if startCo is None:
327 | startCo = self.startCursorPos
328 | startCo.Y = startCo.Y
329 |
330 | if endCo is None:
331 | consinfo = self.consout.GetConsoleScreenBufferInfo()
332 | endCo = consinfo['CursorPosition']
333 | endCo = self.getCoord(0 + self.getOffset(endCo))
334 |
335 | buff = []
336 | self.lastRead = 0
337 |
338 | while True:
339 | startOff = self.getOffset(startCo)
340 | endOff = self.getOffset(endCo)
341 | readlen = endOff - startOff
342 |
343 | if readlen <= 0:
344 | break
345 |
346 | if readlen > 4000:
347 | readlen = 4000
348 | endPoint = self.getCoord(startOff + readlen)
349 |
350 | s = self.consout.ReadConsoleOutputCharacter(readlen, startCo)
351 | self.lastRead += len(s)
352 | self.totalRead += len(s)
353 | buff.append(s)
354 |
355 | startCo = endPoint
356 |
357 | return ''.join(buff)
358 |
359 | def readConsoleToCursor(self):
360 | """Reads from the current read position to the current cursor
361 | position and inserts the string into self.__buffer."""
362 |
363 | if not self.consout:
364 | return ""
365 |
366 | consinfo = self.consout.GetConsoleScreenBufferInfo()
367 | cursorPos = consinfo['CursorPosition']
368 |
369 | logger.spam('cursor: %r, current: %r' % (cursorPos, self.__currentReadCo))
370 |
371 | isSameX = cursorPos.X == self.__currentReadCo.X
372 | isSameY = cursorPos.Y == self.__currentReadCo.Y
373 | isSamePos = isSameX and isSameY
374 |
375 | logger.spam('isSameY: %r' % isSameY)
376 | logger.spam('isSamePos: %r' % isSamePos)
377 |
378 | if isSameY or not self.lastReadData.endswith('\r\n'):
379 | # Read the current slice again
380 | self.totalRead -= self.lastRead
381 | self.__currentReadCo.X = 0
382 | self.__currentReadCo.Y = self.__bufferY
383 |
384 | logger.spam('cursor: %r, current: %r' % (cursorPos, self.__currentReadCo))
385 |
386 | raw = self.readConsole(self.__currentReadCo, cursorPos)
387 | rawlist = []
388 | while raw:
389 | rawlist.append(raw[:self.__consSize.X])
390 | raw = raw[self.__consSize.X:]
391 | raw = ''.join(rawlist)
392 | s = self.parseData(raw)
393 | for i, line in enumerate(reversed(rawlist)):
394 | if line.endswith(screenbufferfillchar):
395 | # Record the Y offset where the most recent line break was detected
396 | self.__bufferY += len(rawlist) - i
397 | break
398 |
399 | logger.spam('lastReadData: %r' % self.lastReadData)
400 | if s:
401 | logger.debug('Read: %r' % s)
402 | else:
403 | logger.spam('Read: %r' % s)
404 |
405 | if isSamePos and self.lastReadData == s:
406 | logger.spam('isSamePos and self.lastReadData == s')
407 | s = ''
408 |
409 | if s:
410 | lastReadData = self.lastReadData
411 | pos = self.getOffset(self.__currentReadCo)
412 | self.lastReadData = s
413 | if isSameY or not lastReadData.endswith('\r\n'):
414 | # Detect changed lines
415 | self.__buffer.seek(pos)
416 | buf = self.__buffer.read()
417 | if raw.startswith(buf):
418 | # Line has grown
419 | rawslice = raw[len(buf):]
420 | # Update last read bytes so line breaks can be detected in parseData
421 | lastRead = self.lastRead
422 | self.lastRead = len(rawslice)
423 | s = self.parseData(rawslice)
424 | self.lastRead = lastRead
425 | else:
426 | # Cursor has been repositioned
427 | s = '\r' + s
428 | self.__buffer.seek(pos)
429 | self.__buffer.truncate()
430 | self.__buffer.write(raw)
431 |
432 | self.__currentReadCo.X = cursorPos.X
433 | self.__currentReadCo.Y = cursorPos.Y
434 |
435 | return s
436 |
437 | def interact(self):
438 | """Displays the child console for interaction."""
439 |
440 | logger.debug('Start interact window')
441 | win32gui.ShowWindow(win32console.GetConsoleWindow(), win32con.SW_SHOW)
442 |
443 | def sendeof(self):
444 | """This sends an EOF to the host. This sends a character which inform the host that child
445 | has been finished, and all of it's output has been send to host.
446 | """
447 |
448 | self.send_to_host(EOF_CHAR)
449 |
450 |
451 | class ConsoleReaderSocket(ConsoleReaderBase):
452 |
453 | def create_connection(self, **kwargs):
454 | try:
455 | self.port = kwargs['port']
456 | # Create a TCP/IP socket
457 | self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
458 | server_address = ('localhost', self.port)
459 | self.sock.bind(server_address)
460 | logger.info(f'Socket started at port: {self.port}')
461 |
462 | # Listen for incoming connections
463 | self.sock.settimeout(5)
464 | self.sock.listen(1)
465 | self.connection, client_address = self.sock.accept()
466 | self.connection.settimeout(.01)
467 | logger.info(f'Client connected: {client_address}')
468 | except Exception as e: # pragma: no cover
469 | # I hope this code is unreachable.
470 | logger.error(f"Port: {self.port} {e}")
471 | raise
472 |
473 | def close_connection(self):
474 | if self.connection:
475 | self.connection.shutdown(socket.SHUT_RDWR)
476 | self.connection.close()
477 | self.connection = None
478 |
479 | def send_to_host(self, msg):
480 | # convert to bytes
481 | if isinstance(msg, str):
482 | msg = str.encode(msg)
483 | if msg:
484 | logger.debug(f'Sending msg: {msg}')
485 | else:
486 | logger.spam(f'Sending msg: {msg}')
487 | self.connection.sendall(msg)
488 |
489 | def get_from_host(self):
490 | try:
491 | msg = self.connection.recv(4096)
492 | except socket.timeout as e:
493 | err = e.args[0]
494 | # this next if/else is a bit redundant, but illustrates how the
495 | # timeout exception is setup
496 | if err == 'timed out':
497 | logger.debug('recv timed out, retry later')
498 | return b''
499 | else:
500 | raise
501 | else:
502 | if len(msg) == 0:
503 | raise Exception('orderly shutdown on server end')
504 | else:
505 | # got a message do something :)
506 | return msg
507 |
508 |
509 | class ConsoleReaderPipe(ConsoleReaderBase):
510 | def create_connection(self, timeout=-1, **kwargs):
511 | if timeout == -1:
512 | timeout = self.timeout
513 | if timeout is None:
514 | end_time = float('inf')
515 | else:
516 | end_time = time.time() + timeout
517 |
518 | pipe_name = 'wexpect_{}'.format(self.console_pid)
519 | pipe_full_path = r'\\.\pipe\{}'.format(pipe_name)
520 | logger.info('Start pipe server: %s', pipe_full_path)
521 | self.pipe = win32pipe.CreateNamedPipe(
522 | pipe_full_path,
523 | win32pipe.PIPE_ACCESS_DUPLEX,
524 | win32pipe.PIPE_TYPE_MESSAGE | win32pipe.PIPE_READMODE_MESSAGE | win32pipe.PIPE_NOWAIT,
525 | 1, 65536, 65536, 10000, None)
526 | logger.info("waiting for client")
527 | while True:
528 | if end_time < time.time():
529 | raise TIMEOUT('Connect to child has been timed out.')
530 | try:
531 | win32pipe.ConnectNamedPipe(self.pipe, None)
532 | break
533 | except Exception as e:
534 | logger.debug(e)
535 | time.sleep(0.2)
536 | logger.info('got client')
537 |
538 | def close_connection(self):
539 | if self.pipe:
540 | win32file.CloseHandle(self.pipe)
541 |
542 | def send_to_host(self, msg):
543 | # convert to bytes
544 | if isinstance(msg, str):
545 | msg = str.encode(msg)
546 | if msg:
547 | logger.debug(f'Sending msg: {msg}')
548 | else:
549 | logger.spam(f'Sending msg: {msg}')
550 | win32file.WriteFile(self.pipe, msg)
551 |
552 | def get_from_host(self):
553 | data, avail, bytes_left = win32pipe.PeekNamedPipe(self.pipe, 4096)
554 | logger.spam(f'data: {data} avail:{avail} bytes_left{bytes_left}')
555 | if avail > 0:
556 | resp = win32file.ReadFile(self.pipe, 4096)
557 | ret = resp[1]
558 | return ret
559 | else:
560 | return b''
561 |
--------------------------------------------------------------------------------
/wexpect/wexpect_util.py:
--------------------------------------------------------------------------------
1 | """Wexpect is a Windows variant of pexpect https://pexpect.readthedocs.io.
2 |
3 | Wexpect is a Python module for spawning child applications and controlling
4 | them automatically.
5 |
6 | wexpect util contains small functions, and classes, which are used in multiple classes.
7 | The command line argument parsers, and the Exceptions placed here.
8 |
9 | """
10 |
11 | import re
12 | import traceback
13 | import sys
14 | import os
15 | import logging
16 | import signal
17 |
18 | # platform does not define VEOF so assume CTRL-D
19 | EOF_CHAR = b'\x04'
20 |
21 | SIGNAL_CHARS = {
22 | signal.SIGTERM: b'\x011', # Device control 1
23 | signal.SIGINT: b'\x012', # Device control 2
24 | }
25 |
26 | SPAM = 5
27 | logging.addLevelName(SPAM, "SPAM")
28 |
29 |
30 | def str2bool(v):
31 | if isinstance(v, bool):
32 | return v
33 | if v.lower() in ('yes', 'true', 't', 'y', '1'):
34 | return True
35 | elif v.lower() in ('no', 'false', 'f', 'n', '0'):
36 | return False
37 | else: # pragma: no cover
38 | raise argparse.ArgumentTypeError('Boolean value expected.')
39 |
40 |
41 | def spam(self, message, *args, **kws): # pragma: no cover
42 | '''Very verbose debug dunction.
43 | '''
44 | if self.isEnabledFor(SPAM):
45 | # Yes, logger takes its '*args' as 'args'.
46 | self._log(SPAM, message, args, **kws)
47 |
48 |
49 | logging.Logger.spam = spam
50 |
51 |
52 | def init_logger(logger=None): # pragma: no cover
53 | '''Initializes the logger. I wont measure coverage for this debug method.
54 | '''
55 | if logger is None:
56 | logger = logging.getLogger('wexpect')
57 | try:
58 | logger_level = os.environ['WEXPECT_LOGGER_LEVEL']
59 | try:
60 | logger_filename = os.environ['WEXPECT_LOGGER_FILENAME']
61 | except KeyError:
62 | pid = os.getpid()
63 | logger_filename = f'./.wlog/wexpect_{pid}'
64 | logger.setLevel(logger_level)
65 | logger_filename = f'{logger_filename}.log'
66 | os.makedirs(os.path.dirname(logger_filename), exist_ok=True)
67 | fh = logging.FileHandler(logger_filename, 'a', 'utf-8')
68 | formatter = logging.Formatter(
69 | '%(asctime)s - %(filename)s:%(lineno)d - %(levelname)s - %(message)s')
70 | fh.setFormatter(formatter)
71 | logger.addHandler(fh)
72 | except KeyError:
73 | logger.setLevel(logging.ERROR)
74 |
75 |
76 | def split_command_line(command_line, escape_char='^'):
77 | """This splits a command line into a list of arguments. It splits arguments
78 | on spaces, but handles embedded quotes, doublequotes, and escaped
79 | characters. It's impossible to do this with a regular expression, so I
80 | wrote a little state machine to parse the command line. """
81 |
82 | arg_list = []
83 | arg = ''
84 |
85 | # Constants to name the states we can be in.
86 | state_basic = 0
87 | state_esc = 1
88 | state_singlequote = 2
89 | state_doublequote = 3
90 | state_whitespace = 4 # The state of consuming whitespace between commands.
91 | state = state_basic
92 |
93 | for c in command_line:
94 | if state == state_basic or state == state_whitespace:
95 | if c == escape_char: # Escape the next character
96 | state = state_esc
97 | elif c == r"'": # Handle single quote
98 | state = state_singlequote
99 | elif c == r'"': # Handle double quote
100 | state = state_doublequote
101 | elif c.isspace():
102 | # Add arg to arg_list if we aren't in the middle of whitespace.
103 | if state == state_whitespace:
104 | None # Do nothing.
105 | else:
106 | arg_list.append(arg)
107 | arg = ''
108 | state = state_whitespace
109 | else:
110 | arg = arg + c
111 | state = state_basic
112 | elif state == state_esc:
113 | arg = arg + c
114 | state = state_basic
115 | elif state == state_singlequote:
116 | if c == r"'":
117 | state = state_basic
118 | else:
119 | arg = arg + c
120 | elif state == state_doublequote:
121 | if c == r'"':
122 | state = state_basic
123 | else:
124 | arg = arg + c
125 |
126 | if arg != '':
127 | arg_list.append(arg)
128 | return arg_list
129 |
130 |
131 | def join_args(args):
132 | """Joins arguments a command line. It quotes all arguments that contain
133 | spaces or any of the characters ^!$%&()[]{}=;'+,`~"""
134 | commandline = []
135 | for arg in args:
136 | if re.search('[\\^!$%&()[\\]{}=;\'+,`~\\s]', arg):
137 | arg = '"%s"' % arg
138 | commandline.append(arg)
139 | return ' '.join(commandline)
140 |
141 |
142 | class ExceptionPexpect(Exception):
143 | """Base class for all exceptions raised by this module.
144 | """
145 |
146 | def __init__(self, value):
147 |
148 | self.value = value
149 |
150 | def __str__(self):
151 |
152 | return str(self.value)
153 |
154 | def get_trace(self):
155 | """This returns an abbreviated stack trace with lines that only concern
156 | the caller. In other words, the stack trace inside the Wexpect module
157 | is not included. """
158 |
159 | tblist = traceback.extract_tb(sys.exc_info()[2])
160 | tblist = [item for item in tblist if self.__filter_not_wexpect(item)]
161 | tblist = traceback.format_list(tblist)
162 | return ''.join(tblist)
163 |
164 | def __filter_not_wexpect(self, trace_list_item):
165 | """This returns True if list item 0 the string 'wexpect.py' in it. """
166 |
167 | if trace_list_item[0].find('host.py') == -1:
168 | return True
169 | else:
170 | return False
171 |
172 |
173 | class EOF(ExceptionPexpect):
174 | """Raised when EOF is read from a child. This usually means the child has exited.
175 | The user can wait to EOF, which means he waits the end of the execution of the child process."""
176 |
177 |
178 | class TIMEOUT(ExceptionPexpect):
179 | """Raised when a read time exceeds the timeout. """
180 |
181 | init_logger()
182 |
--------------------------------------------------------------------------------