# Monitor and control Apache web server workers from Python.
#
# Author: <NAME> <<EMAIL>>
# Last Change: November 27, 2019
# URL: https://apache-manager.readthedocs.io

"""
A ``top`` like interactive viewer for Apache web server metrics.

The :mod:`~apache_manager.interactive` module implements a ``top`` like
interactive viewer for Apache web server metrics using curses_. It can be
invoked from the command line using ``apache-manager --watch``.

Please note that the functions in this module are not included in the test
suite and are excluded from coverage calculations because:

1. For now this module is just an interesting experiment. It might disappear
   completely or I might change it significantly, it all depends on time and
   interest. For example it would be cool to have a tool like mytop_ or
   innotop_ for Apache workers, but it's going to take time to build something
   like that and I have 40+ open source projects and limited spare time, so I'm
   not going to commit to anything :-).

2. This is my first time working with Python's :mod:`curses` module (and
   curses_ interfaces in general) and it's not yet clear to me how feasible it
   is to test an interactive command line interface that's not line based.

.. _curses: https://en.wikipedia.org/wiki/Curses_(programming_library)
.. _innotop: https://github.com/innotop/innotop
.. _mytop: http://jeremy.zawodny.com/mysql/mytop/
"""

# Standard library modules.
import curses
import logging
import sys
import time

# External dependencies.
import coloredlogs
from humanfriendly.terminal import connected_to_terminal, warning


def watch_metrics(manager):
    """Watch Apache web server metrics in a ``top`` like interface."""
    if connected_to_terminal(sys.stdout):
        try:
            curses.wrapper(redraw_loop, manager)
        except KeyboardInterrupt:
            pass
    else:
        warning("Error: The 'apache-manager --watch' command requires an interactive terminal!")
        sys.exit(1)


def redraw_loop(screen, manager):
    """The main loop that continuously redraws Apache web server metrics."""
    # Ugly workaround to avoid circular import errors due to interdependencies
    # between the apache_manager.cli and apache_manager.interactive modules.
    from apache_manager.cli import report_metrics, line_is_heading
    # Hide warnings (they'll mess up the curses layout).
    coloredlogs.set_level(logging.ERROR)
    # Hide the text cursor.
    cursor_mode = curses.curs_set(0)
    # Make Control-C behave normally.
    curses.noraw()
    # Enable non-blocking getch().
    screen.nodelay(True)
    try:
        # Repeat until the user aborts.
        while True:
            lnum = 0
            for line in report_metrics(manager):
                attributes = 0
                if line_is_heading(line):
                    attributes |= curses.A_BOLD
                screen.addstr(lnum, 0, line, attributes)
                lnum += 1
            # Redraw screen.
            screen.refresh()
            # Wait a while before refreshing the screen, but enable the user to
            # quit in the mean time.
            for i in range(10):
                if screen.getch() == ord('q'):
                    return
                # Don't burn through CPU like crazy :-).
                time.sleep(0.1)
            # Update metrics in next iteration.
            manager.refresh()
            # Clear screen for next iteration.
            screen.erase()
    finally:
        # Restore cursor mode.
        curses.curs_set(cursor_mode)
        # Clean up the screen after we're done.
        screen.erase()

# Monitor and control Apache web server workers from Python.
#
# Author: <NAME> <<EMAIL>>
# Last Change: November 27, 2019
# URL: https://apache-manager.readthedocs.io

"""
A ``top`` like interactive viewer for Apache web server metrics.

The :mod:`~apache_manager.interactive` module implements a ``top`` like
interactive viewer for Apache web server metrics using curses_. It can be
invoked from the command line using ``apache-manager --watch``.

Please note that the functions in this module are not included in the test
suite and are excluded from coverage calculations because:

1. For now this module is just an interesting experiment. It might disappear
   completely or I might change it significantly, it all depends on time and
   interest. For example it would be cool to have a tool like mytop_ or
   innotop_ for Apache workers, but it's going to take time to build something
   like that and I have 40+ open source projects and limited spare time, so I'm
   not going to commit to anything :-).

2. This is my first time working with Python's :mod:`curses` module (and
   curses_ interfaces in general) and it's not yet clear to me how feasible it
   is to test an interactive command line interface that's not line based.

.. _curses: https://en.wikipedia.org/wiki/Curses_(programming_library)
.. _innotop: https://github.com/innotop/innotop
.. _mytop: http://jeremy.zawodny.com/mysql/mytop/
"""

# Standard library modules.
import curses
import logging
import sys
import time

# External dependencies.
import coloredlogs
from humanfriendly.terminal import connected_to_terminal, warning


def watch_metrics(manager):
    """Watch Apache web server metrics in a ``top`` like interface."""
    if connected_to_terminal(sys.stdout):
        try:
            curses.wrapper(redraw_loop, manager)
        except KeyboardInterrupt:
            pass
    else:
        warning("Error: The 'apache-manager --watch' command requires an interactive terminal!")
        sys.exit(1)


def redraw_loop(screen, manager):
    """The main loop that continuously redraws Apache web server metrics."""
    # Ugly workaround to avoid circular import errors due to interdependencies
    # between the apache_manager.cli and apache_manager.interactive modules.
    from apache_manager.cli import report_metrics, line_is_heading
    # Hide warnings (they'll mess up the curses layout).
    coloredlogs.set_level(logging.ERROR)
    # Hide the text cursor.
    cursor_mode = curses.curs_set(0)
    # Make Control-C behave normally.
    curses.noraw()
    # Enable non-blocking getch().
    screen.nodelay(True)
    try:
        # Repeat until the user aborts.
        while True:
            lnum = 0
            for line in report_metrics(manager):
                attributes = 0
                if line_is_heading(line):
                    attributes |= curses.A_BOLD
                screen.addstr(lnum, 0, line, attributes)
                lnum += 1
            # Redraw screen.
            screen.refresh()
            # Wait a while before refreshing the screen, but enable the user to
            # quit in the mean time.
            for i in range(10):
                if screen.getch() == ord('q'):
                    return
                # Don't burn through CPU like crazy :-).
                time.sleep(0.1)
            # Update metrics in next iteration.
            manager.refresh()
            # Clear screen for next iteration.
            screen.erase()
    finally:
        # Restore cursor mode.
        curses.curs_set(cursor_mode)
        # Clean up the screen after we're done.
        screen.erase()

# Monitor and control Apache web server workers from Python. # # Author: <NAME> <<EMAIL>> # Last Change: November 27, 2019 # URL: https://apache-manager.readthedocs.io A ``top`` like interactive viewer for Apache web server metrics.  The :mod:`~apache_manager.interactive` module implements a ``top`` like interactive viewer for Apache web server metrics using curses_. It can be invoked from the command line using ``apache-manager --watch``.  Please note that the functions in this module are not included in the test suite and are excluded from coverage calculations because:  1. For now this module is just an interesting experiment. It might disappear    completely or I might change it significantly, it all depends on time and    interest. For example it would be cool to have a tool like mytop_ or    innotop_ for Apache workers, but it's going to take time to build something    like that and I have 40+ open source projects and limited spare time, so I'm    not going to commit to anything :-).  2. This is my first time working with Python's :mod:`curses` module (and    curses_ interfaces in general) and it's not yet clear to me how feasible it    is to test an interactive command line interface that's not line based.  .. _curses: https://en.wikipedia.org/wiki/Curses_(programming_library) .. _innotop: https://github.com/innotop/innotop .. _mytop: http://jeremy.zawodny.com/mysql/mytop/ # Standard library modules. # External dependencies. Watch Apache web server metrics in a ``top`` like interface. The main loop that continuously redraws Apache web server metrics. # Ugly workaround to avoid circular import errors due to interdependencies # between the apache_manager.cli and apache_manager.interactive modules. # Hide warnings (they'll mess up the curses layout). # Hide the text cursor. # Make Control-C behave normally. # Enable non-blocking getch(). # Repeat until the user aborts. # Redraw screen. # Wait a while before refreshing the screen, but enable the user to # quit in the mean time. # Don't burn through CPU like crazy :-). # Update metrics in next iteration. # Clear screen for next iteration. # Restore cursor mode. # Clean up the screen after we're done.