| .. _module-pw_cli: |
| |
| ------ |
| pw_cli |
| ------ |
| This directory contains the ``pw`` command line interface (CLI) that facilitates |
| working with Pigweed. The CLI module adds several subcommands prefixed with |
| ``pw``, and provides a mechanism for other Pigweed modules to behave as |
| "plugins" and register themselves as ``pw`` commands as well. After activating |
| the Pigweed environment, these commands will be available for use. |
| |
| ``pw`` includes the following commands by default: |
| |
| .. code-block:: text |
| |
| doctor Check that the environment is set up correctly for Pigweed. |
| format Check and fix formatting for source files. |
| help Display detailed information about pw commands. |
| ide Configure editors and IDEs to work best with Pigweed. |
| logdemo Show how logs look at various levels. |
| module Utilities for managing modules. |
| test Run Pigweed unit tests built using GN. |
| watch Watch files for changes and rebuild. |
| |
| To see an up-to-date list of ``pw`` subcommands, run ``pw --help``. |
| |
| Invoking ``pw`` |
| ================ |
| ``pw`` subcommands are invoked by providing the command name. Arguments prior to |
| the command are interpreted by ``pw`` itself; all arguments after the command |
| name are interpreted by the command. |
| |
| Here are some example invocations of ``pw``: |
| |
| .. code-block:: text |
| |
| # Run the doctor command |
| $ pw doctor |
| |
| # Run format --fix with debug-level logs |
| $ pw --loglevel debug format --fix |
| |
| # Display help for the pw command |
| $ pw -h watch |
| |
| # Display help for the watch command |
| $ pw watch -h |
| |
| Registering ``pw`` plugins |
| ========================== |
| Projects can register their own Python scripts as ``pw`` commands. ``pw`` |
| plugins are registered by providing the command name, module, and function in a |
| ``PW_PLUGINS`` file. ``PW_PLUGINS`` files can add new commands or override |
| built-in commands. Since they are accessed by module name, plugins must be |
| defined in Python packages that are installed in the Pigweed virtual |
| environment. |
| |
| Plugin registrations in a ``PW_PLUGINS`` file apply to the their directory and |
| all subdirectories, similarly to configuration files like ``.clang-format``. |
| Registered plugins appear as commands in the ``pw`` tool when ``pw`` is run from |
| those directories. |
| |
| Projects that wish to register commands might place a ``PW_PLUGINS`` file in the |
| root of their repo. Multiple ``PW_PLUGINS`` files may be applied, but the ``pw`` |
| tool gives precedence to a ``PW_PLUGINS`` file in the current working directory |
| or the nearest parent directory. |
| |
| PW_PLUGINS file format |
| ---------------------- |
| ``PW_PLUGINS`` contains one plugin entry per line in the following format: |
| |
| .. code-block:: python |
| |
| # Lines that start with a # are ignored. |
| <command name> <Python module> <function> |
| |
| The following example registers three commands: |
| |
| .. code-block:: python |
| |
| # Register the presubmit script as pw presubmit |
| presubmit my_cool_project.tools run_presubmit |
| |
| # Override the pw test command with a custom version |
| test my_cool_project.testing run_test |
| |
| # Add a custom command |
| flash my_cool_project.flash main |
| |
| Defining a plugin function |
| -------------------------- |
| Any function without required arguments may be used as a plugin function. The |
| function should return an int, which the ``pw`` uses as the exit code. The |
| ``pw`` tool uses the function docstring as the help string for the command. |
| |
| Typically, ``pw`` commands parse their arguments with the ``argparse`` module. |
| ``pw`` sets ``sys.argv`` so it contains only the arguments for the plugin, |
| so plugins can behave the same whether they are executed independently or |
| through ``pw``. |
| |
| Example |
| ^^^^^^^ |
| This example shows a function that is registered as a ``pw`` plugin. |
| |
| .. code-block:: python |
| |
| # my_package/my_module.py |
| |
| def _do_something(device): |
| ... |
| |
| def main() -> int: |
| """Do something to a connected device.""" |
| |
| parser = argparse.ArgumentParser(description=__doc__) |
| parser.add_argument('--device', help='Set which device to target') |
| return _do_something(**vars(parser.parse_args())) |
| |
| |
| if __name__ == '__main__': |
| logging.basicConfig(format='%(message)s', level=logging.INFO) |
| sys.exit(main()) |
| |
| This plugin is registered in a ``PW_PLUGINS`` file in the current working |
| directory or a parent of it. |
| |
| .. code-block:: python |
| |
| # Register my_commmand |
| my_command my_package.my_module main |
| |
| The function is now available through the ``pw`` command, and will be listed in |
| ``pw``'s help. Arguments after the command name are passed to the plugin. |
| |
| .. code-block:: text |
| |
| $ pw |
| |
| ▒█████▄ █▓ ▄███▒ ▒█ ▒█ ░▓████▒ ░▓████▒ ▒▓████▄ |
| ▒█░ █░ ░█▒ ██▒ ▀█▒ ▒█░ █ ▒█ ▒█ ▀ ▒█ ▀ ▒█ ▀█▌ |
| ▒█▄▄▄█░ ░█▒ █▓░ ▄▄░ ▒█░ █ ▒█ ▒███ ▒███ ░█ █▌ |
| ▒█▀ ░█░ ▓█ █▓ ░█░ █ ▒█ ▒█ ▄ ▒█ ▄ ░█ ▄█▌ |
| ▒█ ░█░ ░▓███▀ ▒█▓▀▓█░ ░▓████▒ ░▓████▒ ▒▓████▀ |
| |
| usage: pw [-h] [-C DIRECTORY] [-l LOGLEVEL] [--no-banner] [command] ... |
| |
| The Pigweed command line interface (CLI). |
| |
| ... |
| |
| supported commands: |
| doctor Check that the environment is set up correctly for Pigweed. |
| format Check and fix formatting for source files. |
| help Display detailed information about pw commands. |
| ... |
| my_command Do something to a connected device. |
| |
| $ pw my_command -h |
| |
| ▒█████▄ █▓ ▄███▒ ▒█ ▒█ ░▓████▒ ░▓████▒ ▒▓████▄ |
| ▒█░ █░ ░█▒ ██▒ ▀█▒ ▒█░ █ ▒█ ▒█ ▀ ▒█ ▀ ▒█ ▀█▌ |
| ▒█▄▄▄█░ ░█▒ █▓░ ▄▄░ ▒█░ █ ▒█ ▒███ ▒███ ░█ █▌ |
| ▒█▀ ░█░ ▓█ █▓ ░█░ █ ▒█ ▒█ ▄ ▒█ ▄ ░█ ▄█▌ |
| ▒█ ░█░ ░▓███▀ ▒█▓▀▓█░ ░▓████▒ ░▓████▒ ▒▓████▀ |
| |
| usage: pw my_command [-h] [--device DEVICE] |
| |
| Do something to a connected device. |
| |
| optional arguments: |
| -h, --help show this help message and exit |
| --device DEVICE Set which device to target |
| |
| Branding Pigweed's tooling |
| ========================== |
| An important part of starting a new project is picking a name, and in the case |
| of Pigweed, designing a banner for the project. Pigweed supports configuring |
| the banners by setting environment variables: |
| |
| * ``PW_BRANDING_BANNER`` - Absolute path to a filename containing a banner to |
| display when running the ``pw`` commands. See the example below. |
| * ``PW_BRANDING_BANNER_COLOR`` - Color of the banner. Possible values include: |
| ``red``, ``bold_red``, ``yellow``, ``bold_yellow``, ``green``, |
| ``bold_green``, ``blue``, ``cyan``, ``magenta``, ``bold_white``, |
| ``black_on_white``. See ``pw_cli.colors`` for details. |
| |
| The below example shows how to manually change the branding at the command |
| line. However, these environment variables should be set in the project root's |
| ``bootstrap.sh`` before delegating to Pigweed's upstream ``bootstrap.sh``. |
| |
| .. code-block:: text |
| |
| $ cat foo-banner.txt |
| |
| ▒██████ ░▓██▓░ ░▓██▓░ |
| ▒█░ ▒█ ▒█ ▒█ ▒█ |
| ▒█▄▄▄▄ ▒█ █ ▒█ ▒█ █ ▒█ |
| ▒█▀ ▒█ ▒█ ▒█ ▒█ |
| ▒█ ░▓██▓░ ░▓██▓░ |
| |
| $ export PW_BRANDING_BANNER="$(pwd)/foo-banner.txt" |
| $ export PW_BRANDING_BANNER_COLOR="bold_red" |
| $ pw logdemo |
| |
| ▒██████ ░▓██▓░ ░▓██▓░ |
| ▒█░ ▒█ ▒█ ▒█ ▒█ |
| ▒█▄▄▄▄ ▒█ █ ▒█ ▒█ █ ▒█ |
| ▒█▀ ▒█ ▒█ ▒█ ▒█ |
| ▒█ ░▓██▓░ ░▓██▓░ |
| |
| 20200610 12:03:44 CRT This is a critical message |
| 20200610 12:03:44 ERR There was an error on our last operation |
| 20200610 12:03:44 WRN Looks like something is amiss; consider investigating |
| 20200610 12:03:44 INF The operation went as expected |
| 20200610 12:03:44 OUT Standard output of subprocess |
| |
| The branding is not purely visual; it serves to make it clear which project an |
| engineer is working with. |
| |
| Making the ASCII / ANSI art |
| --------------------------- |
| The most direct way to make the ASCII art is to create it with a text editor. |
| However, there are some tools to make the process faster and easier. |
| |
| * `Patorjk's ASCII art generator <http://patorjk.com/software/taag/>`_ - A |
| great starting place, since you can copy and paste straight from the browser |
| into a file, and then point ``PW_BRANDING_BANNER`` at it. Most of the fonts |
| use normal ASCII characters; and fonts with extended ASCII characters use the |
| Unicode versions of them (needed for modern terminals). |
| |
| There are other options, but these require additional work to put into Pigweed |
| since they only export in the traditional ANS or ICE formats. The old ANS |
| formats do not have a converter (contributions welcome!). Here are some of the |
| options as of mid-2020: |
| |
| * `Playscii <http://vectorpoem.com/playscii/>`_ - Actively maintained. |
| * `Moebius <https://github.com/blocktronics/moebius>`_ - Actively maintained. |
| * `SyncDraw <http://syncdraw.bbsdev.net/>`_ - Actively maintained, in 2020, in |
| a CVS repository. |
| * `PabloDraw <http://picoe.ca/products/pablodraw/>`_ - Works on most desktop |
| machines thanks to being written in .NET. Not maintained, but works well. Has |
| an impresive brush system for organic style drawing. |
| * `TheDraw <https://en.wikipedia.org/wiki/TheDraw>`_ - One of the most popular |
| ANSI art editors back in the 90s. Requires DOSBox to run on modern machines, |
| but otherwise works. It has some of the most impressive capabilities, |
| including supporting full-color multi-character fonts. |
| |
| Future branding improvements |
| ---------------------------- |
| Branding the ``pw`` tool is a great start, but more changes are planned: |
| |
| - Supporting branding the ``bootstrap/activate`` banner, which for technical |
| reasons is not the same code as the banner printing from the Python tooling. |
| These will use the same ``PW_BRANDING_BANNER`` and |
| ``PW_BRANDING_BANNER_COLOR`` environment variables. |
| - Supporting renaming the ``pw`` command to something project specific, like |
| ``foo`` in this case. |
| - Re-coloring the log headers from the ``pw`` tool. |
| |
| pw_cli Python package |
| ===================== |
| The ``pw_cli`` Pigweed module includes the ``pw_cli`` Python package, which |
| provides utilities for creating command line tools with Pigweed. |
| |
| pw_cli.log |
| ---------- |
| .. automodule:: pw_cli.log |
| :members: |
| |
| pw_cli.plugins |
| -------------- |
| :py:mod:`pw_cli.plugins` provides general purpose plugin functionality. The |
| module can be used to create plugins for command line tools, interactive |
| consoles, or anything else. Pigweed's ``pw`` command uses this module for its |
| plugins. |
| |
| To use plugins, create a :py:class:`pw_cli.plugins.Registry`. The registry may |
| have an optional validator function that checks plugins before they are |
| registered (see :py:meth:`pw_cli.plugins.Registry.__init__`). |
| |
| Plugins may be registered in a few different ways. |
| |
| * **Direct function call.** Register plugins by calling |
| :py:meth:`pw_cli.plugins.Registry.register` or |
| :py:meth:`pw_cli.plugins.Registry.register_by_name`. |
| |
| .. code-block:: python |
| |
| registry = pw_cli.plugins.Registry() |
| |
| registry.register('plugin_name', my_plugin) |
| registry.register_by_name('plugin_name', 'module_name', 'function_name') |
| |
| * **Decorator.** Register using the :py:meth:`pw_cli.plugins.Registry.plugin` |
| decorator. |
| |
| .. code-block:: python |
| |
| _REGISTRY = pw_cli.plugins.Registry() |
| |
| # This function is registered as the "my_plugin" plugin. |
| @_REGISTRY.plugin |
| def my_plugin(): |
| pass |
| |
| # This function is registered as the "input" plugin. |
| @_REGISTRY.plugin(name='input') |
| def read_something(): |
| pass |
| |
| The decorator may be aliased to give a cleaner syntax (e.g. ``register = |
| my_registry.plugin``). |
| |
| * **Plugins files.** Plugins files use a simple format: |
| |
| .. code-block:: |
| |
| # Comments start with "#". Blank lines are ignored. |
| name_of_the_plugin module.name module_member |
| |
| another_plugin some_module some_function |
| |
| These files are placed in the file system and apply similarly to Git's |
| ``.gitignore`` files. From Python, these files are registered using |
| :py:meth:`pw_cli.plugins.Registry.register_file` and |
| :py:meth:`pw_cli.plugins.Registry.register_directory`. |
| |
| pw_cli.plugins module reference |
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ |
| .. automodule:: pw_cli.plugins |
| :members: |