Wyatt Hepler | f9fb90f | 2020-09-30 18:59:33 -0700 | [diff] [blame] | 1 | .. _module-pw_cli: |
Armando Montanez | 0054a9b | 2020-03-13 13:06:24 -0700 | [diff] [blame] | 2 | |
| 3 | ------ |
| 4 | pw_cli |
| 5 | ------ |
Wyatt Hepler | 8a562a0 | 2020-04-16 15:28:13 -0700 | [diff] [blame] | 6 | This directory contains the ``pw`` command line interface (CLI) that facilitates |
Armando Montanez | 0054a9b | 2020-03-13 13:06:24 -0700 | [diff] [blame] | 7 | working with Pigweed. The CLI module adds several subcommands prefixed with |
Wyatt Hepler | 8a562a0 | 2020-04-16 15:28:13 -0700 | [diff] [blame] | 8 | ``pw``, and provides a mechanism for other Pigweed modules to behave as |
| 9 | "plugins" and register themselves as ``pw`` commands as well. After activating |
| 10 | the Pigweed environment, these commands will be available for use. |
Armando Montanez | 0054a9b | 2020-03-13 13:06:24 -0700 | [diff] [blame] | 11 | |
Wyatt Hepler | 8a562a0 | 2020-04-16 15:28:13 -0700 | [diff] [blame] | 12 | ``pw`` includes the following commands by default: |
Armando Montanez | 0054a9b | 2020-03-13 13:06:24 -0700 | [diff] [blame] | 13 | |
Wyatt Hepler | 8a562a0 | 2020-04-16 15:28:13 -0700 | [diff] [blame] | 14 | .. code-block:: text |
Armando Montanez | 0054a9b | 2020-03-13 13:06:24 -0700 | [diff] [blame] | 15 | |
Wyatt Hepler | 8a562a0 | 2020-04-16 15:28:13 -0700 | [diff] [blame] | 16 | doctor Check that the environment is set up correctly for Pigweed. |
| 17 | format Check and fix formatting for source files. |
| 18 | help Display detailed information about pw commands. |
| 19 | logdemo Show how logs look at various levels. |
| 20 | module-check Check that a module matches Pigweed's module guidelines. |
| 21 | test Run Pigweed unit tests built using GN. |
| 22 | watch Watch files for changes and rebuild. |
| 23 | |
| 24 | To see an up-to-date list of ``pw`` subcommands, run ``pw --help``. |
| 25 | |
| 26 | Invoking ``pw`` |
| 27 | ================ |
| 28 | ``pw`` subcommands are invoked by providing the command name. Arguments prior to |
| 29 | the command are interpreted by ``pw`` itself; all arguments after the command |
| 30 | name are interpreted by the command. |
| 31 | |
| 32 | Here are some example invocations of ``pw``: |
| 33 | |
| 34 | .. code-block:: text |
| 35 | |
| 36 | # Run the doctor command |
Armando Montanez | 0054a9b | 2020-03-13 13:06:24 -0700 | [diff] [blame] | 37 | $ pw doctor |
Armando Montanez | 0054a9b | 2020-03-13 13:06:24 -0700 | [diff] [blame] | 38 | |
Wyatt Hepler | 8a562a0 | 2020-04-16 15:28:13 -0700 | [diff] [blame] | 39 | # Run format --fix with debug-level logs |
| 40 | $ pw --loglevel debug format --fix |
Armando Montanez | 0054a9b | 2020-03-13 13:06:24 -0700 | [diff] [blame] | 41 | |
Wyatt Hepler | 8a562a0 | 2020-04-16 15:28:13 -0700 | [diff] [blame] | 42 | # Display help for the pw command |
| 43 | $ pw -h watch |
| 44 | |
| 45 | # Display help for the watch command |
| 46 | $ pw watch -h |
| 47 | |
| 48 | Registering ``pw`` plugins |
| 49 | ========================== |
| 50 | Projects can register their own Python scripts as ``pw`` commands. ``pw`` |
| 51 | plugins are registered by providing the command name, module, and function in a |
| 52 | ``PW_PLUGINS`` file. ``PW_PLUGINS`` files can add new commands or override |
| 53 | built-in commands. Since they are accessed by module name, plugins must be |
| 54 | defined in Python packages that are installed in the Pigweed virtual |
| 55 | environment. |
| 56 | |
Wyatt Hepler | 8a562a0 | 2020-04-16 15:28:13 -0700 | [diff] [blame] | 57 | Plugin registrations in a ``PW_PLUGINS`` file apply to the their directory and |
| 58 | all subdirectories, similarly to configuration files like ``.clang-format``. |
| 59 | Registered plugins appear as commands in the ``pw`` tool when ``pw`` is run from |
| 60 | those directories. |
| 61 | |
| 62 | Projects that wish to register commands might place a ``PW_PLUGINS`` file in the |
| 63 | root of their repo. Multiple ``PW_PLUGINS`` files may be applied, but the ``pw`` |
| 64 | tool gives precedence to a ``PW_PLUGINS`` file in the current working directory |
| 65 | or the nearest parent directory. |
| 66 | |
| 67 | PW_PLUGINS file format |
| 68 | ---------------------- |
| 69 | ``PW_PLUGINS`` contains one plugin entry per line in the following format: |
| 70 | |
| 71 | .. code-block:: python |
| 72 | |
| 73 | # Lines that start with a # are ignored. |
| 74 | <command name> <Python module> <function> |
| 75 | |
| 76 | The following example registers three commands: |
| 77 | |
| 78 | .. code-block:: python |
| 79 | |
| 80 | # Register the presubmit script as pw presubmit |
| 81 | presubmit my_cool_project.tools run_presubmit |
| 82 | |
| 83 | # Override the pw test command with a custom version |
| 84 | test my_cool_project.testing run_test |
| 85 | |
| 86 | # Add a custom command |
| 87 | flash my_cool_project.flash main |
| 88 | |
| 89 | Defining a plugin function |
| 90 | -------------------------- |
| 91 | Any function without required arguments may be used as a plugin function. The |
| 92 | function should return an int, which the ``pw`` uses as the exit code. The |
| 93 | ``pw`` tool uses the function docstring as the help string for the command. |
| 94 | |
| 95 | Typically, ``pw`` commands parse their arguments with the ``argparse`` module. |
| 96 | ``pw`` sets ``sys.argv`` so it contains only the arguments for the plugin, |
| 97 | so plugins can behave the same whether they are executed independently or |
| 98 | through ``pw``. |
| 99 | |
| 100 | Example |
| 101 | ^^^^^^^ |
| 102 | This example shows a function that is registered as a ``pw`` plugin. |
| 103 | |
| 104 | .. code-block:: python |
| 105 | |
| 106 | # my_package/my_module.py |
| 107 | |
| 108 | def _do_something(device): |
| 109 | ... |
| 110 | |
| 111 | def main() -> int: |
| 112 | """Do something to a connected device.""" |
| 113 | |
| 114 | parser = argparse.ArgumentParser(description=__doc__) |
| 115 | parser.add_argument('--device', help='Set which device to target') |
| 116 | return _do_something(**vars(parser.parse_args())) |
| 117 | |
| 118 | |
| 119 | if __name__ == '__main__': |
| 120 | logging.basicConfig(format='%(message)s', level=logging.INFO) |
| 121 | sys.exit(main()) |
| 122 | |
| 123 | This plugin is registered in a ``PW_PLUGINS`` file in the current working |
| 124 | directory or a parent of it. |
| 125 | |
| 126 | .. code-block:: python |
| 127 | |
| 128 | # Register my_commmand |
| 129 | my_command my_package.my_module main |
| 130 | |
| 131 | The function is now available through the ``pw`` command, and will be listed in |
| 132 | ``pw``'s help. Arguments after the command name are passed to the plugin. |
| 133 | |
| 134 | .. code-block:: text |
| 135 | |
| 136 | $ pw |
| 137 | |
| 138 | ▒█████▄ █▓ ▄███▒ ▒█ ▒█ ░▓████▒ ░▓████▒ ▒▓████▄ |
| 139 | ▒█░ █░ ░█▒ ██▒ ▀█▒ ▒█░ █ ▒█ ▒█ ▀ ▒█ ▀ ▒█ ▀█▌ |
| 140 | ▒█▄▄▄█░ ░█▒ █▓░ ▄▄░ ▒█░ █ ▒█ ▒███ ▒███ ░█ █▌ |
| 141 | ▒█▀ ░█░ ▓█ █▓ ░█░ █ ▒█ ▒█ ▄ ▒█ ▄ ░█ ▄█▌ |
| 142 | ▒█ ░█░ ░▓███▀ ▒█▓▀▓█░ ░▓████▒ ░▓████▒ ▒▓████▀ |
| 143 | |
| 144 | usage: pw [-h] [-C DIRECTORY] [-l LOGLEVEL] [--no-banner] [command] ... |
| 145 | |
| 146 | The Pigweed command line interface (CLI). |
| 147 | |
| 148 | ... |
| 149 | |
| 150 | supported commands: |
| 151 | doctor Check that the environment is set up correctly for Pigweed. |
| 152 | format Check and fix formatting for source files. |
| 153 | help Display detailed information about pw commands. |
| 154 | ... |
| 155 | my_command Do something to a connected device. |
| 156 | |
| 157 | $ pw my_command -h |
| 158 | |
| 159 | ▒█████▄ █▓ ▄███▒ ▒█ ▒█ ░▓████▒ ░▓████▒ ▒▓████▄ |
| 160 | ▒█░ █░ ░█▒ ██▒ ▀█▒ ▒█░ █ ▒█ ▒█ ▀ ▒█ ▀ ▒█ ▀█▌ |
| 161 | ▒█▄▄▄█░ ░█▒ █▓░ ▄▄░ ▒█░ █ ▒█ ▒███ ▒███ ░█ █▌ |
| 162 | ▒█▀ ░█░ ▓█ █▓ ░█░ █ ▒█ ▒█ ▄ ▒█ ▄ ░█ ▄█▌ |
| 163 | ▒█ ░█░ ░▓███▀ ▒█▓▀▓█░ ░▓████▒ ░▓████▒ ▒▓████▀ |
| 164 | |
| 165 | usage: pw my_command [-h] [--device DEVICE] |
| 166 | |
| 167 | Do something to a connected device. |
| 168 | |
| 169 | optional arguments: |
| 170 | -h, --help show this help message and exit |
| 171 | --device DEVICE Set which device to target |
Keir Mierle | 6ec3c3b | 2020-06-10 10:30:33 -0700 | [diff] [blame] | 172 | |
| 173 | Branding Pigweed's tooling |
| 174 | ========================== |
| 175 | An important part of starting a new project is picking a name, and in the case |
| 176 | of Pigweed, designing a banner for the project. Pigweed supports configuring |
| 177 | the banners by setting environment variables: |
| 178 | |
| 179 | * ``PW_BRANDING_BANNER`` - Absolute path to a filename containing a banner to |
| 180 | display when running the ``pw`` commands. See the example below. |
| 181 | * ``PW_BRANDING_BANNER_COLOR`` - Color of the banner. Possible values include: |
| 182 | ``red``, ``bold_red``, ``yellow``, ``bold_yellow``, ``green``, |
| 183 | ``bold_green``, ``blue``, ``cyan``, ``magenta``, ``bold_white``, |
| 184 | ``black_on_white``. See ``pw_cli.colors`` for details. |
| 185 | |
| 186 | The below example shows how to manually change the branding at the command |
| 187 | line. However, these environment variables should be set in the project root's |
| 188 | ``bootstrap.sh`` before delegating to Pigweed's upstream ``bootstrap.sh``. |
| 189 | |
| 190 | .. code-block:: text |
| 191 | |
| 192 | $ cat foo-banner.txt |
| 193 | |
| 194 | ▒██████ ░▓██▓░ ░▓██▓░ |
| 195 | ▒█░ ▒█ ▒█ ▒█ ▒█ |
| 196 | ▒█▄▄▄▄ ▒█ █ ▒█ ▒█ █ ▒█ |
| 197 | ▒█▀ ▒█ ▒█ ▒█ ▒█ |
| 198 | ▒█ ░▓██▓░ ░▓██▓░ |
| 199 | |
| 200 | $ export PW_BRANDING_BANNER="$(pwd)/foo-banner.txt" |
| 201 | $ export PW_BRANDING_BANNER_COLOR="bold_red" |
| 202 | $ pw logdemo |
| 203 | |
| 204 | ▒██████ ░▓██▓░ ░▓██▓░ |
| 205 | ▒█░ ▒█ ▒█ ▒█ ▒█ |
| 206 | ▒█▄▄▄▄ ▒█ █ ▒█ ▒█ █ ▒█ |
| 207 | ▒█▀ ▒█ ▒█ ▒█ ▒█ |
| 208 | ▒█ ░▓██▓░ ░▓██▓░ |
| 209 | |
| 210 | 20200610 12:03:44 CRT This is a critical message |
| 211 | 20200610 12:03:44 ERR There was an error on our last operation |
| 212 | 20200610 12:03:44 WRN Looks like something is amiss; consider investigating |
| 213 | 20200610 12:03:44 INF The operation went as expected |
| 214 | 20200610 12:03:44 OUT Standard output of subprocess |
| 215 | |
| 216 | The branding is not purely visual; it serves to make it clear which project an |
| 217 | engineer is working with. |
| 218 | |
| 219 | Making the ASCII / ANSI art |
| 220 | --------------------------- |
| 221 | The most direct way to make the ASCII art is to create it with a text editor. |
| 222 | However, there are some tools to make the process faster and easier. |
| 223 | |
| 224 | * `Patorjk's ASCII art generator <http://patorjk.com/software/taag/>`_ - A |
| 225 | great starting place, since you can copy and paste straight from the browser |
| 226 | into a file, and then point ``PW_BRANDING_BANNER`` at it. Most of the fonts |
| 227 | use normal ASCII characters; and fonts with extended ASCII characters use the |
| 228 | Unicode versions of them (needed for modern terminals). |
| 229 | * `Online ANSII Edit by Andy Herbert |
| 230 | <http://andyherbert.github.io/ansiedit/public/index.html>`_ - Browser based |
| 231 | editor that can export to mixed UTF-8 and ANSII color. It's also `open source |
| 232 | <https://github.com/andyherbert/ansiedit>`_. What's nice about this editor is |
| 233 | that you can create a multi-color banner, and save it with the ``File`` --> |
| 234 | ``Export as ANSi (UTF-8)`` option, and use it directly as a Pigweed banner. |
| 235 | One caveat is that the editor uses UTF-8 box drawing characters, which don't |
| 236 | work well with all terminals. However, the box drawing characters look so |
| 237 | slick on terminals that support them that we feel this is a worthwhile |
| 238 | tradeoff. |
| 239 | |
| 240 | There are other options, but these require additional work to put into Pigweed |
| 241 | since they only export in the traditional ANS or ICE formats. The old ANS |
| 242 | formats do not have a converter (contributions welcome!). Here are some of the |
| 243 | options as of mid-2020: |
| 244 | |
| 245 | * `Playscii <http://vectorpoem.com/playscii/>`_ - Actively maintained. |
| 246 | * `Moebius <https://github.com/blocktronics/moebius>`_ - Actively maintained. |
| 247 | * `SyncDraw <http://syncdraw.bbsdev.net/>`_ - Actively maintained, in 2020, in |
| 248 | a CVS repository. |
| 249 | * `PabloDraw <http://picoe.ca/products/pablodraw/>`_ - Works on most desktop |
| 250 | machines thanks to being written in .NET. Not maintained, but works well. Has |
| 251 | an impresive brush system for organic style drawing. |
| 252 | * `TheDraw <https://en.wikipedia.org/wiki/TheDraw>`_ - One of the most popular |
| 253 | ANSI art editors back in the 90s. Requires DOSBox to run on modern machines, |
| 254 | but otherwise works. It has some of the most impressive capabilities, |
| 255 | including supporting full-color multi-character fonts. |
| 256 | |
| 257 | Future branding improvements |
| 258 | ---------------------------- |
| 259 | Branding the ``pw`` tool is a great start, but more changes are planned: |
| 260 | |
| 261 | - Supporting branding the ``bootstrap/activate`` banner, which for technical |
| 262 | reasons is not the same code as the banner printing from the Python tooling. |
| 263 | These will use the same ``PW_BRANDING_BANNER`` and |
| 264 | ``PW_BRANDING_BANNER_COLOR`` environment variables. |
| 265 | - Supporting renaming the ``pw`` command to something project specific, like |
| 266 | ``foo`` in this case. |
| 267 | - Re-coloring the log headers from the ``pw`` tool. |
Wyatt Hepler | 91d2c50 | 2021-03-12 15:21:30 -0800 | [diff] [blame] | 268 | |
| 269 | pw_cli Python package |
| 270 | ===================== |
| 271 | The ``pw_cli`` Pigweed module includes the ``pw_cli`` Python package, which |
| 272 | provides utilities for creating command line tools with Pigweed. |
| 273 | |
Wyatt Hepler | bea166e | 2021-04-08 10:56:31 -0700 | [diff] [blame] | 274 | pw_cli.log |
Wyatt Hepler | 91d2c50 | 2021-03-12 15:21:30 -0800 | [diff] [blame] | 275 | ---------- |
| 276 | .. automodule:: pw_cli.log |
| 277 | :members: |
Wyatt Hepler | bea166e | 2021-04-08 10:56:31 -0700 | [diff] [blame] | 278 | |
| 279 | pw_cli.plugins |
| 280 | -------------- |
| 281 | :py:mod:`pw_cli.plugins` provides general purpose plugin functionality. The |
| 282 | module can be used to create plugins for command line tools, interactive |
| 283 | consoles, or anything else. Pigweed's ``pw`` command uses this module for its |
| 284 | plugins. |
| 285 | |
| 286 | To use plugins, create a :py:class:`pw_cli.plugins.Registry`. The registry may |
| 287 | have an optional validator function that checks plugins before they are |
| 288 | registered (see :py:meth:`pw_cli.plugins.Registry.__init__`). |
| 289 | |
| 290 | Plugins may be registered in a few different ways. |
| 291 | |
Wyatt Hepler | e923032 | 2021-04-13 16:47:26 -0700 | [diff] [blame] | 292 | * **Direct function call.** Register plugins by calling |
| 293 | :py:meth:`pw_cli.plugins.Registry.register` or |
Wyatt Hepler | bea166e | 2021-04-08 10:56:31 -0700 | [diff] [blame] | 294 | :py:meth:`pw_cli.plugins.Registry.register_by_name`. |
Wyatt Hepler | e923032 | 2021-04-13 16:47:26 -0700 | [diff] [blame] | 295 | |
| 296 | .. code-block:: python |
| 297 | |
| 298 | registry = pw_cli.plugins.Registry() |
| 299 | |
| 300 | registry.register('plugin_name', my_plugin) |
| 301 | registry.register_by_name('plugin_name', 'module_name', 'function_name') |
| 302 | |
| 303 | * **Decorator.** Register using the :py:meth:`pw_cli.plugins.Registry.plugin` |
| 304 | decorator. |
| 305 | |
| 306 | .. code-block:: python |
| 307 | |
| 308 | _REGISTRY = pw_cli.plugins.Registry() |
| 309 | |
| 310 | # This function is registered as the "my_plugin" plugin. |
| 311 | @_REGISTRY.plugin |
| 312 | def my_plugin(): |
| 313 | pass |
| 314 | |
| 315 | # This function is registered as the "input" plugin. |
| 316 | @_REGISTRY.plugin(name='input') |
| 317 | def read_something(): |
| 318 | pass |
| 319 | |
| 320 | The decorator may be aliased to give a cleaner syntax (e.g. ``register = |
| 321 | my_registry.plugin``). |
| 322 | |
| 323 | * **Plugins files.** Plugins files use a simple format: |
Wyatt Hepler | bea166e | 2021-04-08 10:56:31 -0700 | [diff] [blame] | 324 | |
| 325 | .. code-block:: |
| 326 | |
| 327 | # Comments start with "#". Blank lines are ignored. |
| 328 | name_of_the_plugin module.name module_member |
| 329 | |
| 330 | another_plugin some_module some_function |
| 331 | |
Wyatt Hepler | e923032 | 2021-04-13 16:47:26 -0700 | [diff] [blame] | 332 | These files are placed in the file system and apply similarly to Git's |
| 333 | ``.gitignore`` files. From Python, these files are registered using |
| 334 | :py:meth:`pw_cli.plugins.Registry.register_file` and |
| 335 | :py:meth:`pw_cli.plugins.Registry.register_directory`. |
| 336 | |
| 337 | pw_cli.plugins module reference |
| 338 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ |
Wyatt Hepler | bea166e | 2021-04-08 10:56:31 -0700 | [diff] [blame] | 339 | .. automodule:: pw_cli.plugins |
| 340 | :members: |