commands.rst 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. .. _mach_commands:
  2. =====================
  3. Implementing Commands
  4. =====================
  5. Mach commands are defined via Python decorators.
  6. All the relevant decorators are defined in the *mach.decorators* module.
  7. The important decorators are as follows:
  8. :py:func:`CommandProvider <mach.decorators.CommandProvider>`
  9. A class decorator that denotes that a class contains mach
  10. commands. The decorator takes no arguments.
  11. :py:func:`Command <mach.decorators.Command>`
  12. A method decorator that denotes that the method should be called when
  13. the specified command is requested. The decorator takes a command name
  14. as its first argument and a number of additional arguments to
  15. configure the behavior of the command.
  16. :py:func:`CommandArgument <mach.decorators.CommandArgument>`
  17. A method decorator that defines an argument to the command. Its
  18. arguments are essentially proxied to ArgumentParser.add_argument()
  19. :py:func:`SubCommand <mach.decorators.SubCommand>`
  20. A method decorator that denotes that the method should be a
  21. sub-command to an existing ``@Command``. The decorator takes the
  22. parent command name as its first argument and the sub-command name
  23. as its second argument.
  24. ``@CommandArgument`` can be used on ``@SubCommand`` instances just
  25. like they can on ``@Command`` instances.
  26. Classes with the ``@CommandProvider`` decorator **must** have an
  27. ``__init__`` method that accepts 1 or 2 arguments. If it accepts 2
  28. arguments, the 2nd argument will be a
  29. :py:class:`mach.base.CommandContext` instance.
  30. Here is a complete example:
  31. .. code-block:: python
  32. from mach.decorators import (
  33. CommandArgument,
  34. CommandProvider,
  35. Command,
  36. )
  37. @CommandProvider
  38. class MyClass(object):
  39. @Command('doit', help='Do ALL OF THE THINGS.')
  40. @CommandArgument('--force', '-f', action='store_true',
  41. help='Force doing it.')
  42. def doit(self, force=False):
  43. # Do stuff here.
  44. When the module is loaded, the decorators tell mach about all handlers.
  45. When mach runs, it takes the assembled metadata from these handlers and
  46. hooks it up to the command line driver. Under the hood, arguments passed
  47. to the decorators are being used to help mach parse command arguments,
  48. formulate arguments to the methods, etc. See the documentation in the
  49. :py:mod:`mach.base` module for more.
  50. The Python modules defining mach commands do not need to live inside the
  51. main mach source tree.
  52. Conditionally Filtering Commands
  53. ================================
  54. Sometimes it might only make sense to run a command given a certain
  55. context. For example, running tests only makes sense if the product
  56. they are testing has been built, and said build is available. To make
  57. sure a command is only runnable from within a correct context, you can
  58. define a series of conditions on the
  59. :py:func:`Command <mach.decorators.Command>` decorator.
  60. A condition is simply a function that takes an instance of the
  61. :py:func:`mach.decorators.CommandProvider` class as an argument, and
  62. returns ``True`` or ``False``. If any of the conditions defined on a
  63. command return ``False``, the command will not be runnable. The
  64. docstring of a condition function is used in error messages, to explain
  65. why the command cannot currently be run.
  66. Here is an example:
  67. .. code-block:: python
  68. from mach.decorators import (
  69. CommandProvider,
  70. Command,
  71. )
  72. def build_available(cls):
  73. """The build needs to be available."""
  74. return cls.build_path is not None
  75. @CommandProvider
  76. class MyClass(MachCommandBase):
  77. def __init__(self, build_path=None):
  78. self.build_path = build_path
  79. @Command('run_tests', conditions=[build_available])
  80. def run_tests(self):
  81. # Do stuff here.
  82. It is important to make sure that any state needed by the condition is
  83. available to instances of the command provider.
  84. By default all commands without any conditions applied will be runnable,
  85. but it is possible to change this behaviour by setting
  86. ``require_conditions`` to ``True``:
  87. .. code-block:: python
  88. m = mach.main.Mach()
  89. m.require_conditions = True
  90. Minimizing Code in Commands
  91. ===========================
  92. Mach command modules, classes, and methods work best when they are
  93. minimal dispatchers. The reason is import bloat. Currently, the mach
  94. core needs to import every Python file potentially containing mach
  95. commands for every command invocation. If you have dozens of commands or
  96. commands in modules that import a lot of Python code, these imports
  97. could slow mach down and waste memory.
  98. It is thus recommended that mach modules, classes, and methods do as
  99. little work as possible. Ideally the module should only import from
  100. the :py:mod:`mach` package. If you need external modules, you should
  101. import them from within the command method.
  102. To keep code size small, the body of a command method should be limited
  103. to:
  104. 1. Obtaining user input (parsing arguments, prompting, etc)
  105. 2. Calling into some other Python package
  106. 3. Formatting output
  107. Of course, these recommendations can be ignored if you want to risk
  108. slower performance.
  109. In the future, the mach driver may cache the dispatching information or
  110. have it intelligently loaded to facilitate lazy loading.