Skip to content

Commit

Permalink
README: Add simple usage
Browse files Browse the repository at this point in the history
let's add a very rough but practical usage guide to our README.

Signed-off-by: Lukáš Doktor <ldoktor@redhat.com>
  • Loading branch information
ldoktor committed Dec 11, 2023
1 parent 9f8cde3 commit 3302b3e
Showing 1 changed file with 75 additions and 1 deletion.
76 changes: 75 additions & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,78 @@ Aexpect: Control your interactive applications

This module provides services similar to the `pexpect` python library,
so to speak, spawn and control interactive applications, such as `ssh`,
`sftp` and others.
`sftp` and others although it can be useful for pure streams as well.

It's enhanced of multi-pattern matching, convenience features for
not-only-linux terminals as well as support for terminals that "spit"
extra output from time to time (eg. kernel messages) over the output.

There are also extra classes to simplify executing parts of program
on a different location (distributed programming) or writing controls
executed on another host (metaprogramming).

Simple usage
------------

.. code-block:: python
>>> import aexpect
>>> import time
>>> dir(aexpect)
['Expect', 'ExpectError', 'ExpectProcessTerminatedError', 'ExpectTimeoutError', 'ShellCmdError', 'ShellError', 'ShellProcessTerminatedError', 'ShellSession', 'ShellStatusError', 'ShellTimeoutError', 'Spawn', 'Tail', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__path__', '__spec__', 'client', 'exceptions', 'kill_tail_threads', 'remote', 'rss_client', 'run_bg', 'run_fg', 'run_tail', 'shared', 'utils']
>>> session = aexpect.ShellSession("bash")
>>> session.cmd("ls /tmp/b")
'1 2\n'
>>> session.cmd("cat /tmp/b/1")
'Hello\n'
>>> session.cmd("cat /tmp/b/2")
'World\n'
>>> dir(session)
['_ShellSession__RE_STATUS', '__class__', '__del__', '__delattr__', '__dict__', '__dir__', '__doc__', '__enter__', '__eq__', '__exit__', '__format__', '__ge__', '__getattribute__', '__getinitargs__', '__getstate__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setstate__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_add_close_hook', '_add_reader', '_aexpect_helper', '_close_aexpect_helper', '_close_reader_fds', '_get_fd', '_join_thread', '_read_nonblocking', '_start_thread', '_tail', 'a_id', 'auto_close', 'close', 'close_hooks', 'closed', 'cmd', 'cmd_output', 'cmd_output_safe', 'cmd_status', 'cmd_status_output', 'command', 'ctrlpipe_filename', 'echo', 'encoding', 'get_command_output', 'get_command_status', 'get_command_status_output', 'get_id', 'get_output', 'get_pid', 'get_status', 'get_stripped_output', 'inpipe_filename', 'is_alive', 'is_defunct', 'is_responsive', 'kill', 'linesep', 'lock_client_starting_filename', 'lock_server_running_filename', 'log_file', 'log_file_fd', 'match_patterns', 'match_patterns_multiline', 'output_filename', 'output_func', 'output_params', 'output_prefix', 'prompt', 'read_nonblocking', 'read_until_any_line_matches', 'read_until_last_line_matches', 'read_until_last_word_matches', 'read_until_output_matches', 'read_up_to_prompt', 'reader_fds', 'reader_filenames', 'readers', 'remove_command_echo', 'remove_last_nonempty_line', 'send', 'send_ctrl', 'sendcontrol', 'sendline', 'server_log_filename', 'set_linesep', 'set_log_file', 'set_output_func', 'set_output_params', 'set_output_prefix', 'set_prompt', 'set_status_test_command', 'set_termination_func', 'set_termination_params', 'shell_pid_filename', 'status_filename', 'status_test_command', 'tail_thread', 'termination_func', 'termination_params', 'thread_name']
>>> session.sendline("for I in $(seq 10); do echo $I; sleep 1; done")
>>> session.read_nonblocking(0.1, 2)
>>> time.sleep(10)
>>> session.read_nonblocking(0.1, 2)
'2\n3\n4\n5\n6\n7\n8\n9\n10\n[medic@fedora ~ \x1b[1;31m\x1b[0m]$ '
>>> session.sendline("for I in $(seq 10); do echo $I; sleep 1; done")
>>> session.read_nonblocking(1.5, 2)
'1\n2\n3\n4\n'
>>> session.sendline("for I in $(seq 10); do echo $I; sleep 1; done")
>>> session.read_until_output_matches("3", timeout=10)
(0, '1\n2\n3\n')
>>> session.sendline("for I in $(seq 10); do echo $I; sleep 1; done")
>>> session.read_until_output_matches(["5", "7", "2", "foo"], timeout=10)
(2, '1\n2\n')
>>> session.cmd_status("true")
0
>>> session.cmd_status("false")
1
>>> session.is_alive()
True
>>> session.is_responsive()
True
>>> session.sendline("exit")
>>> session.is_alive()
False
>>> session.is_responsive()
False
Debugging
---------

Using this even for purely bash-like constructs is good as you can leverage
the python debugger to interactively walk your issues. Especially the
`pydevd` project is great as you can debug code from multiple machines on
in a single Eclipse to see concurency issues. Execution is as simple as
`pip install pydevd` on all machines, adding
`pydevd.settrace(eclipse_machine_ip, True, True)` directly into the code
replacing the `eclipse_machine_ip` with IP address of the machine where
Eclipse will be running (eg. 127.0.0.1 for localhost). The following `True`
are to redirect stdout/stderr which is usually useful but sometimes might
lead to issues. Then you need to start the python debugger inside Eclipse
by entering debug view and starting the pydev server
`pydev->Start debug server`. Now you are ready and simply execute your
application the way you are used to, once it reaches the `pydev.settrace`
line it will dial to the Eclipse server and show up in your debug view,
showing the code you are debugging even though it's on a different machine,
allowing you to single-step across multiple processes.

0 comments on commit 3302b3e

Please sign in to comment.