File: index.rst

package info (click to toggle)
aioconsole 0.8.1-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 328 kB
  • sloc: python: 2,010; makefile: 11; sh: 5
file content (325 lines) | stat: -rw-r--r-- 10,025 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
aioconsole
==========

Asynchronous console and interfaces for asyncio

aioconsole_ provides:

* asynchronous equivalents to `input`_, `exec`_ and `code.interact`_
* an interactive loop running the asynchronous python console
* a way to customize and run command line interface using `argparse`_
* `stream`_ support to serve interfaces instead of using standard streams
* the ``apython`` script to access asyncio code at runtime without modifying the sources


Requirements
------------

*  Python \>= 3.8


Installation
------------

aioconsole_ is available on PyPI_ and GitHub_.
Both of the following commands install the ``aioconsole`` package
and the ``apython`` script.

.. sourcecode:: console

    $ pip3 install aioconsole   # from PyPI
    $ python3 setup.py install  # or from the sources
    $ apython -h
    usage: apython [-h] [--serve [HOST:] PORT] [--no-readline]
                   [--banner BANNER] [--locals LOCALS]
                   [-m MODULE | FILE] ...


Asynchronous console
--------------------

The `example directory`_ includes a `slightly modified version`_ of the
`echo server from the asyncio documentation`_. It runs an echo server on
a given port and save the received messages in ``loop.history``.

It runs fine and doesn't use any ``aioconsole`` function:

.. sourcecode:: console

    $ python3 -m example.echo 8888
    The echo service is being served on 127.0.0.1:8888

In order to access the program while it’s running, simply replace
``python3`` with ``apython`` and redirect ``stdout`` so the console is
not polluted by ``print`` statements (``apython`` uses ``stderr``):

.. sourcecode:: console

    $ apython -m example.echo 8888 > echo.log
    Python 3.5.0 (default, Sep 7 2015, 14:12:03)
    [GCC 4.8.4] on linux
    Type "help", "copyright", "credits" or "license" for more information.
    ---
    This console is running in an asyncio event loop.
    It allows you to wait for coroutines using the 'await' syntax.
    Try: await asyncio.sleep(1, result=3, loop=loop)
    ---
    >>>

This looks like the standard python console, with an extra message. It
suggests using the ``await`` syntax (``yield from`` for python 3.4):

.. sourcecode:: python3

    >>> await asyncio.sleep(1, result=3, loop=loop)
    # Wait one second...
    3
    >>>

The ``locals`` contain a reference to the event loop:

.. sourcecode:: python3

    >>> dir()
    ['__doc__', '__name__', 'asyncio', 'loop']
    >>> loop
    <InteractiveEventLoop running=True closed=False debug=False>
    >>>

So we can access the ``history`` of received messages:

.. sourcecode:: python3

    >>> loop.history
    defaultdict(<class 'list'>, {})
    >>> sum(loop.history.values(), [])
    []

Let’s send a message to the server using a netcat_ client:

.. sourcecode:: console

    $ nc localhost 8888
    Hello!
    Hello!

The echo server behaves correctly. It is now possible to retrieve the
message:

.. sourcecode:: python3

    >>> sum(loop.history.values(), [])
    ['Hello!']

The console also supports ``Ctrl-C`` and ``Ctrl-D`` signals:

.. sourcecode:: python3

    >>> ^C
    KeyboardInterrupt
    >>> # Ctrl-D
    $

All this is implemented by setting ``InteractiveEventLoop`` as default
event loop. It simply is a selector loop that schedules
``aioconsole.interact()`` coroutine when it’s created.


Serving the console
-------------------

Moreover, ``aioconsole.interact()`` supports `stream objects`_ so it can be
used along with `asyncio.start\_server`_ to serve the python console.
The ``aioconsole.start_interactive_server`` coroutine does exactly that. A
backdoor can be introduced by simply adding the following line in the
program:

.. sourcecode:: python3

    server = await aioconsole.start_interactive_server(
        host='localhost', port=8000)

This is actually very similar to the `eventlet.backdoor module`_. It is
also possible to use the ``--serve`` option so it is not necessary to
modify the code:

.. sourcecode:: console

    $ apython --serve :8889 -m example.echo 8888
    The console is being served on 0.0.0.0:8889
    The echo service is being served on 127.0.0.1:8888

Then connect using netcat_ and optionally, rlwrap_:

.. sourcecode:: console

    $ rlwrap nc localhost 8889
    Python 3.5.0 (default, Sep 7 2015, 14:12:03)
    [GCC 4.8.4] on linux
    Type "help", "copyright", "credits" or "license" for more information.
    ---
    This console is running in an asyncio event loop.
    It allows you to wait for coroutines using the 'await' syntax.
    Try: await asyncio.sleep(1, result=3, loop=loop)
    ---
    >>>

Great! Anyone can now forkbomb your machine:

.. sourcecode:: python3

    >>> import os
    >>> os.system(':(){ :|:& };:')


Command line interfaces
-----------------------

The package also provides an ``AsychronousCli`` object. It is
initialized with a dictionary of commands and can be scheduled with the
coroutine ``async_cli.interact()``. A dedicated command line interface
to the echo server is defined in `example/cli.py`_. In this case, the
command dictionary is defined as:

.. sourcecode:: python3

    commands = {'history': (get_history, parser)}

where ``get_history`` is a coroutine and ``parser`` an `ArgumentParser`_
from the `argparse`_ module. The arguments of the parser will be passed
as keywords arguments to the coroutine.

Let’s run the command line interface:

.. sourcecode:: console

    $ python3 -m example.cli 8888 > cli.log
    Welcome to the CLI interface of echo!
    Try:
    * 'help' to display the help message
    * 'list' to display the command list.
    >>>

The ``help`` and ``list`` commands are generated automatically:

.. sourcecode:: console

    >>> help
    Type 'help' to display this message.
    Type 'list' to display the command list.
    Type '<command> -h' to display the help message of <command>.
    >>> list
    List of commands:
     * help [-h]
     * history [-h] [--pattern PATTERN]
     * list [-h]
    >>>

The ``history`` command defined earlier can be found in the list. Note
that it has an ``help`` option and a ``pattern`` argument:

.. sourcecode:: console

    >>> history -h
    usage: history [-h] [--pattern PATTERN]

    Display the message history

    optional arguments:
      -h, --help            show this help message and exit
      --pattern PATTERN, -p PATTERN
                            pattern to filter hostnames

Example usage of the ``history`` command:

.. sourcecode:: console

    >>> history
    No message in the history
    >>> # A few messages later
    >>> history
    Host 127.0.0.1:
      0. Hello!
      1. Bye!
    Host 192.168.0.3
      0. Sup!
    >>> history -p 127.*
    Host 127.0.0.1:
      0. Hello!
      1. Bye!


Serving interfaces
------------------

Just like ``asyncio.interact()``, ``AsynchronousCli`` can be initialized
with any pair of `streams`_. It can be used along with
`asyncio.start\_server`_ to serve the command line interface. The
previous `example`_ provides this functionality through the
``--serve-cli`` option:

.. sourcecode:: console

    $ python3 -m example.cli 8888 --serve-cli 8889
    The command line interface is being served on 127.0.0.1:8889
    The echo service is being served on 127.0.0.1:8888

It’s now possible to access the interface using netcat_:

.. sourcecode:: console

    $ rlwrap nc localhost 8889
    Welcome to the CLI interface of echo!
    Try:
     * 'help' to display the help message
     * 'list' to display the command list.
    >>>

It is also possible to combine the example with the ``apython`` script
to add an extra access for debugging:

.. sourcecode:: console

    $ apython --serve 8887 -m example.cli 8888 --serve-cli 8889
    The console is being served on 127.0.0.1:8887
    The command line interface is being served on 127.0.0.1:8889
    The echo service is being served on 127.0.0.1:8888


Limitations
-----------

The python console exposed by `aioconsole`_ is quite limited compared to modern consoles such as `IPython`_ or `ptpython`_. Luckily, those projects gained greater asyncio support over the years. In particular, the following use cases overlap with `aioconsole`_ capabilities:

- `Embedding a ptpython console in an asyncio program <https://github.com/prompt-toolkit/ptpython/blob/master/examples/asyncio-python-embed.py>`_
- `Using the await syntax in an IPython console <https://ipython.readthedocs.io/en/stable/whatsnew/version7.html#autowait-asynchronous-repl>`_

Contact
-------

Vincent Michel: vxgmichel@gmail.com

.. _aioconsole: https://pypi.python.org/pypi/aioconsole
.. _GitHub: https://github.com/vxgmichel/aioconsole
.. _input: https://docs.python.org/3/library/functions.html#input
.. _exec: https://docs.python.org/3/library/functions.html#exec
.. _code.interact: https://docs.python.org/2/library/code.html#code.interact
.. _argparse: https://docs.python.org/dev/library/argparse.html
.. _stream: https://docs.python.org/3.4/library/asyncio-stream.html
.. _example directory: https://github.com/vxgmichel/aioconsole/blob/master/example
.. _example/echo.py: https://github.com/vxgmichel/aioconsole/blob/master/example/echo.py
.. _echo server from the asyncio documentation: https://docs.python.org/3/library/asyncio-stream.html#tcp-echo-server-using-streams
.. _asyncio.start\_server: https://docs.python.org/3.4/library/asyncio-stream.html#asyncio.start_server
.. _eventlet.backdoor module: http://eventlet.net/doc/modules/backdoor.html#backdoor-python-interactive-interpreter-within-a-running-process
.. _example/cli.py: https://github.com/vxgmichel/aioconsole/blob/master/example/cli.py
.. _ArgumentParser: https://docs.python.org/dev/library/argparse.html#argparse.ArgumentParser

.. _streams: stream_
.. _stream objects: stream_
.. _slightly modified version: `example/echo.py`_
.. _example: `example/cli.py`_
.. _PyPI: aioconsole_
.. _netcat: https://linux.die.net/man/1/nc
.. _rlwrap: https://linux.die.net/man/1/rlwrap
.. _IPython: https://ipython.readthedocs.io
.. _ptpython: https://github.com/prompt-toolkit/ptpython