Skip to content

Commit

Permalink
doc: Update the docs to match with the latest (#393)
Browse files Browse the repository at this point in the history
  • Loading branch information
achimnol committed Dec 21, 2023
1 parent e5ef41a commit 1e40e63
Show file tree
Hide file tree
Showing 13 changed files with 266 additions and 218 deletions.
36 changes: 36 additions & 0 deletions aiomonitor/monitor.py
Expand Up @@ -87,6 +87,11 @@ async def cancel_task(task: "asyncio.Task[Any]") -> None:


class Monitor:
prompt: str
"""
The string that prompts you to enter a command, defaults to ``"monitor >>> "``
"""

_event_loop_thread_id: Optional[int] = None

console_locals: Dict[str, Any]
Expand Down Expand Up @@ -158,10 +163,16 @@ def __init__(

@property
def host(self) -> str:
"""
The current hostname to bind the monitor server.
"""
return self._host

@property
def port(self) -> int:
"""
The port number to bind the monitor server.
"""
return self._termui_port

def __repr__(self) -> str:
Expand All @@ -171,6 +182,9 @@ def __repr__(self) -> str:
)

def start(self) -> None:
"""
Starts monitoring thread, where telnet server is executed.
"""
assert not self._closed
assert not self._started
self._started = True
Expand All @@ -183,6 +197,11 @@ def start(self) -> None:

@property
def closed(self) -> bool:
"""
A flag indicates if monitor was closed, currntly instance of
:class:`Monitor` can not be reused. For new monitor, new instance
should be created.
"""
return self._closed

def __enter__(self) -> Monitor:
Expand All @@ -202,6 +221,9 @@ def __exit__(
self.close()

def close(self) -> None:
"""
Joins background thread, and cleans up resources.
"""
assert self._started, "The monitor must have been started to close it."
if not self._closed:
self._ui_loop.call_soon_threadsafe(
Expand Down Expand Up @@ -616,6 +638,20 @@ def start_monitor(
max_termination_history: Optional[int] = None,
locals: Optional[Dict[str, Any]] = None,
) -> Monitor:
"""
Factory function, creates instance of :class:`Monitor` and starts
monitoring thread.
:param Type[Monitor] monitor: Monitor class to use
:param str host: hostname to serve monitor telnet server
:param int port: monitor port (terminal UI), by default 20101
:param int webui_port: monitor port (web UI), by default 20102
:param int console_port: python REPL port, by default 20103
:param bool console_enabled: flag indicates if python REPL is requred
to start with instance of monitor.
:param dict locals: dictionary with variables exposed in python console
environment
"""
m = monitor_cls(
loop,
host=host,
Expand Down
3 changes: 3 additions & 0 deletions aiomonitor/termui/commands.py
Expand Up @@ -38,6 +38,9 @@
__all__ = (
"interact",
"monitor_cli",
"auto_command_done",
"auto_async_command_done",
"custom_help_option",
)


Expand Down
157 changes: 18 additions & 139 deletions docs/api.rst
@@ -1,5 +1,7 @@
API
===
.. _api-reference:

API Reference
=============

**aiomonitor** has tiny and simple to use API, just factory function and
class that support context management protocol. Starting a monitor is as
Expand All @@ -10,10 +12,13 @@ simple as opening a file.
import asyncio
import aiomonitor
loop = asyncio.get_event_loop()
with aiomonitor.start_monitor(loop):
print("Now you can connect with: nc localhost 20101")
loop.run_forever()
async def main():
loop = asyncio.get_event_loop()
with aiomonitor.start_monitor(loop):
print("Now you can connect with: telnet localhost 20101")
loop.run_forever()
asyncio.run(main())
Alternatively you can use more verbose try/finally approach but do not forget
call ``close()`` methods, to join thread and finalize resources:
Expand All @@ -27,138 +32,12 @@ call ``close()`` methods, to join thread and finalize resources:
finally:
m.close()
It is possible to subclass ``Monitor`` to add custom commands to it. These custom
commands are methods with names starting with `do_`. See examples.

.. _api-reference:

Reference
---------

.. module:: aiomonitor
.. currentmodule:: aiomonitor

.. data:: MONITOR_HOST = '127.0.0.1'

Specifies the default host to bind the services for the monitor

.. warning::

Since aiomonitor exposes the internal states of the traced process, never bind it to
publicly accessible address to prevent potential security breaches and denial of services!

.. data:: MONITOR_TERMUI_PORT = 20101

Specifies the default telnet port for teh monitor where you can connect using a telnet client

.. data:: MONITOR_WEBUI_PORT = 20102

Specifies the default HTTP port for the monitor where you can connect using a web browser

.. data:: CONSOLE_PORT = 20103

Specifies the default port for asynchronous python REPL

.. function:: start_monitor(loop, monitor=Monitor, host=None, port=MONITOR_TERMUI_PORT, webui_port=MONITOR_WEBUI_PORT, console_port=CONSOLE_PORT, console_enabled=True, locals=None)
:single-line-parameter-list:

Factory function, creates instance of :class:`Monitor` and starts
monitoring thread.

:param Type[Monitor] monitor: Monitor class to use
:param str host: hostname to serve monitor telnet server
:param int port: monitor port (terminal UI), by default 20101
:param int webui_port: monitor port (web UI), by default 20102
:param int console_port: python REPL port, by default 20103
:param bool console_enabled: flag indicates if python REPL is requred
to start with instance of monitor.
:param dict locals: dictionary with variables exposed in python console
environment

.. class:: Monitor

Class has same arguments as :func:`start_monitor`, except for the `monitor`
argument.

.. method:: start()

Starts monitoring thread, where telnet server is executed.

.. method:: close()

Joins background thread, and cleans up resources.

.. attribute:: Monitor.closed

Flag indicates if monitor was closed, currntly instance of
:class:`Monitor` can not be reused. For new monitor, new instance
should be created.

.. attribute:: Monitor.prompt

The string that prompts you to enter a command, defaults to 'monitor >>> '

.. attribute:: Monitor.intro

Template for the intro text you see when you connect to the running monitor.
Available fields to be filled in are:
- tasknum: Number of tasks in the event loop
- s: 's' if tasknum is >1 or 0

.. attribute:: Monitor.help_template

Template string that gets filled in and displayed when `help <COMMAND>` is
executed. Available fields to be filled in are:
- cmd_name: the commands name
- arg_list: the arguments it takes
- doc: the docstring of the command method
- doc_firstline: first line of the docstring

.. attribute:: Monitor.help_short_template

Like `help_template`, but gets called when `help` is executed, once per
available command

.. attribute:: Monitor.lastcmd

Stores the last entered command line, whether it properly executed or not.

.. method:: precmd(comm: str, args: Sequence[str]) -> Tuple[do_<COMMAND> function: Callable, args: Sequence[Any]]

Gets executed by the loop before the command is run, and is acutally
resonsible for resolving the entered command string to the method that gets
called. If you overwrite this method, you can use self._getcmd(comm) to
resolve it. Afterwards the default implementation uses
list(self._map_args(cmd_method, args)) to map the given arguments to the
methods type annotation.

.. method:: postcmd(comm: str, args: Sequence[Any], result: Any, exception: Optional[Exception]) -> None

Runs after the command unconditionally. It takes the entered command string,
the arguments (with type annotation applied, see above), the return value of
the command, and the exception the command raised.
If the command raised an exception, the result will be set to
`self._empty_result`; if no exception was raised, it will be set to None.
The default implementation does nothing.

.. method:: emptyline()

Gets executed when an empty line is entered. A line is considered empty
when `line.strip() == ''` is true.
By default takes the last run command (stored in `self.lastcmd`) and runs it
again, using `self._command_dispatch(self.lastcmd)`

.. method:: default(comm: str, args: Sequence[str]) -> None

Gets run when precmd cannot find a command method to execute.

.. method:: do_<COMMAND>(...)

Subclasses of the Monitor class can define their own commands available in
the REPL. See the tutorial :ref:`cust-commands`.
It is possible to add custom commands to the monitor's telnet CLI.
Check out :doc:`examples`.

.. decorator:: aiomonitor.utils.alt_names(names)
.. toctree::
:maxdepth: 2

A decorator for the custom commands to define aliases, like `h` and `?`
are aliases for the `help` command. `names` is a single string with a
space separated list of aliases.
reference/monitor
reference/termui
reference/webui
7 changes: 6 additions & 1 deletion docs/conf.py
Expand Up @@ -29,11 +29,13 @@
# needs_sphinx = '1.0'

import pathlib
import sys
from importlib.metadata import version as get_version
from typing import Mapping

_docs_path = pathlib.Path(__file__).parent

sys.path.insert(0, str(_docs_path.parent))

try:
_version_info = get_version("aiomonitor")
Expand All @@ -49,6 +51,8 @@
"sphinx.ext.todo",
"sphinx.ext.coverage",
"sphinx.ext.viewcode",
"sphinx_autodoc_typehints",
"sphinx_click",
]

# Add any paths that contain templates here, relative to this directory.
Expand All @@ -65,7 +69,7 @@

# General information about the project.
project = "aiomonitor"
copyright = "2016, Nikolay Novik"
copyright = "Nikolay Novik (2016-), Joongi Kim (2022-) and contributors"
author = "Nikolay Novik"

# The version info for the project you're documenting, acts as replacement for
Expand Down Expand Up @@ -188,4 +192,5 @@
# Example configuration for intersphinx: refer to the Python standard library.
intersphinx_mapping = {
"python": ("https://docs.python.org/3", None),
"click": ("https://click.palletsprojects.com/en/8.1.x/", None),
}
30 changes: 15 additions & 15 deletions docs/index.rst
Expand Up @@ -6,32 +6,33 @@
aiomonitor's documentation!
===========================

**aiomonitor** is Python 3.5+ module that adds monitor and cli capabilities
for asyncio_ application. Idea and code borrowed from curio_ project.
Task monitor that runs concurrently to the asyncio_ loop (or fast drop in
**aiomonitor** is Python 3.8+ module that adds monitor and cli capabilities
for :mod:`asyncio` application. Idea and code borrowed from curio_ project.
Task monitor that runs concurrently to the :mod:`asyncio` loop (or fast drop in
replacement uvloop_) in a separate thread. This can inspect the loop and
provide debugging capabilities.

Library provides an python console using aioconsole_ library, it is possible
aiomonitor provides an python console using aioconsole_ library, it is possible
to execute asynchronous command inside your running application.

+-------------------+
|.. image:: tty.gif |
+-------------------+
As of 0.6.0, it also provides a GUI to inspect and cancel asyncio tasks like below:

+----------------------------+
| .. image:: webui-demo1.gif |
+----------------------------+


Features
--------
* Telnet server that provides insides of operation of you app
* Telnet server that provides insides of operation of you app

* Supportes several commands that helps to list, cancel and trace running
asyncio_ tasks
* Supportes several commands that helps to list, cancel and trace running
:mod:`asyncio` tasks

* Provides python REPL capabilities, that is executed in the running event loop;
helps to inspect state of your ``asyncio`` application.
* Provides python REPL capabilities, that is executed in the running event loop;
helps to inspect state of your :mod:`asyncio` application.

* Extensible with you own commands, in the style of the standard library's cmd_
module
* Extensible with you own commands using :mod:`click`.

Contents
--------
Expand All @@ -57,7 +58,6 @@ Indices and tables
.. _Python: https://www.python.org
.. _aioconsole: https://github.com/vxgmichel/aioconsole
.. _aiohttp: https://github.com/KeepSafe/aiohttp
.. _asyncio: http://docs.python.org/3/library/asyncio.html
.. _curio: https://github.com/dabeaz/curio
.. _uvloop: https://github.com/MagicStack/uvloop
.. _cmd: http://docs.python.org/3/library/cmd.html

0 comments on commit 1e40e63

Please sign in to comment.