aboutsummaryrefslogtreecommitdiff
path: root/pw_console/internals.rst
blob: 8ce8efcf71952f52b03dff3d135016932821e47b (plain)
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
.. _module-pw_console-internals:

Internal Design
===============
.. pigweed-module-subpage::
   :name: pw_console
   :tagline: pw_console: Multi-purpose pluggable interactive console for dev & manufacturing

Threads and Event Loops
-----------------------

In `ptpython`_ and `IPython`_ all user repl code is run in the foreground. This
allows interrupts like ``Ctrl-C`` and functions like ``print()`` and
``time.sleep()`` to work as expected. Pigweed's Console doesn't use this
approach as it would hide or freeze the `prompt_toolkit`_ user interface while
running repl code.

To get around this issue all user repl code is run in a dedicated thread with
stdout and stderr patched to capture output. This lets the user interface stay
responsive and new log messages to continue to be displayed.

Here's a diagram showing how ``pw_console`` threads and `asyncio`_ tasks are
organized.

.. mermaid::

   flowchart LR
       classDef eventLoop fill:#e3f2fd,stroke:#90caf9,stroke-width:1px;
       classDef thread fill:#fffde7,stroke:#ffeb3b,stroke-width:1px;
       classDef plugin fill:#fce4ec,stroke:#f06292,stroke-width:1px;
       classDef builtinFeature fill:#e0f2f1,stroke:#4db6ac,stroke-width:1px;

       %% Subgraphs are drawn in reverse order.

       subgraph pluginThread [Plugin Thread 1]
           subgraph pluginLoop [Plugin Event Loop 1]
               toolbarFunc-->|"Refresh<br/>UI Tokens"| toolbarFunc
               toolbarFunc[Toolbar Update Function]
           end
           class pluginLoop eventLoop;
       end
       class pluginThread thread;

       subgraph pluginThread2 [Plugin Thread 2]
           subgraph pluginLoop2 [Plugin Event Loop 2]
               paneFunc-->|"Refresh<br/>UI Tokens"| paneFunc
               paneFunc[Pane Update Function]
           end
           class pluginLoop2 eventLoop;
       end
       class pluginThread2 thread;

       subgraph replThread [Repl Thread]
           subgraph replLoop [Repl Event Loop]
               Task1 -->|Finished| Task2 -->|Cancel with Ctrl-C| Task3
           end
           class replLoop eventLoop;
       end
       class replThread thread;

       subgraph main [Main Thread]
           subgraph mainLoop [User Interface Event Loop]
               log[[Log Pane]]
               repl[[Python Repl]]
               pluginToolbar([User Toolbar Plugin])
               pluginPane([User Pane Plugin])
               class log,repl builtinFeature;
               class pluginToolbar,pluginPane plugin;
           end
           class mainLoop eventLoop;
       end
       class main thread;

       repl-.->|Run Code| replThread
       pluginToolbar-.->|Register Plugin| pluginThread
       pluginPane-.->|Register Plugin| pluginThread2

.. _IPython: https://ipython.readthedocs.io/
.. _prompt_toolkit: https://python-prompt-toolkit.readthedocs.io/
.. _asyncio: https://docs.python.org/3/library/asyncio.html
.. _ptpython: https://github.com/prompt-toolkit/ptpython/