:orphan:

Extension Example
=================

This example will cover creating an extension with a single command, found in
a new section at the root of the CLI. The full command will be::

 $ pulp-admin example demo --name Jay --show-date


Framework Hook
--------------

The first step is to create the method the framework will invoke when loading
the extension. This method should create the necessary objects to populate the
CLI with the extension's additions.

For this demo, the context is held in a global variable so it can be accessed
when the command is run.

.. code-block:: python

  CONTEXT = None
  def initialize(context):
      global CONTEXT
      CONTEXT = context

Sections
--------

There are two ways to add a new section to either the root of the CLI directly
or to another section. One approach is to manually instantiate the
``pulp.client.extensions.extensions.PulpCliSection`` class, optionally
subclassing it to add any needed enhancements. The instantiated object is then
added to the CLI using the ``add_section`` call or to a parent section with
the ``add_subsection`` call.

The other approach is to use the ``create_section`` helper method found in
the CLI instance itself or the ``create_subsection`` call in
other ``PulpCliSection`` instances. The demo will use the latter approach.

The ``create_section`` call takes the name of the section (i.e. the text that
will be used on the command line directly) and a description for help text
purposes. The ``PulpCliSection`` instance is returned from the call so it can
be further manipulated. The CLI instance is retrieved from the client context.

.. code-block:: python

  ex_section = context.cli.create_section('example', 'Example section')

At this point, if this extension was installed, the new section would appear
in the usage. Installation is covered later in this document.

::

 $ pulp-admin
 Usage: pulp-admin [SUB_SECTION, ..] COMMAND

  Available Sections:
    auth     - manage users, roles and permissions
    bindings - search consumer bindings
    event    - subscribe to event notifications
    example  - Example section
    orphan   - find and remove orphaned content units
    puppet   - manage Puppet-related content and features
    repo     - list repositories and manage repo groups
    rpm      - manage RPM-related content and features
    server   - display info about the server
    tasks    - list and cancel server-side tasks


Commands
--------

Commands associate the user request with the method that will handle it. Commands are added to
sections using a similar approach as with sections. The command can be manually
instantiated from the ``pulp.client.extensions.extensions.PulpCliCommand``
class and added to a section with the ``add_command`` method.

Alternatively, a helper method named ``create_command`` can be used to do both
the instantiation and add it to a section. This call accepts three parameters.
The first is the name of the command which is used to invoke it from the command
line. The second is the description, displayed when viewing the usage of the
command. The third is a reference to the method to run when the command is
executed.

The following snippet creates our demo command and ties it to the ``run_demo``
method.

.. code-block:: python

  demo_command = ex_section.create_command('demo', 'Demo command', run_demo)

The referenced ``run_demo`` must be defined as a method, otherwise the extension
will fail to load. We'll expand on this in the next section, but a simple
implementation is as follows.

.. code-block:: python

  def run_demo(**kwargs):
    CONTEXT.prompt.write('Hello World')


Options and Flags
-----------------

While some commands can simply be executed as is, many will need to accept
user input. These are referred to as *options* and *flags*. Both can be created
by running the appropriate ``create_*`` method on the command instance.

For the demo, we'll add an option that accepts the user's name and a flag that
toggles whether or not the date is printed.

.. code-block:: python

  demo_command.create_option('--name', 'Name of the user', required=True)
  demo_command.create_flag('--show-date', 'If specified, the date will be displayed')

The above snippet configures the ``--name`` option as required. The client
framework will enforce that, displaying the usage text to the user in the event it
is not specified.

::

  $ pulp-admin example demo
  Command: demo
  Description: Demo command

  Available Arguments:

    --name      - (required) Name of the user
    --show-date - If specified, the date will be displayed

  The following options are required but were not specified:
    --name

The client framework will capture the input and make it available to the command's
execution method in its ``kwargs`` argument. The name of the option/flag is used
as the key and the user input is the value (or ``True`` in the case of a flag).
Below is the ``run_demo`` method from above, enhanced to take advantage of our
newly added options and flags.

.. code-block:: python

  def run_demo(**kwargs):
      CONTEXT.prompt.write('Hello %s' % kwargs['name'])
      if kwargs['show-date']:
          CONTEXT.prompt.write(datetime.datetime.now())

Example usage:

::

  $ pulp-admin example demo --name Jay
  Hello Jay

  $ pulp-admin example demo --name Jay --show-date
  Hello Jay
  2013-02-07 14:54:14.587727


Installation
------------

Instructions on packaging and installing extensions for production deployment
can be found at :ref:`extensions_entry_points`.

For simplicity, this demo will install the extension using the
directory approach. More information can be found in the
:ref:`extensions_directory` section of this guide.

 * Create ``/usr/lib/pulp/admin/extensions/example``
 * Create an empty file in that directory named ``__init__.py``
 * Copy the file containing this demo code to that directory, naming it
   ``pulp_cli.py``

When the ``pulp-admin`` script is run, the usage text will show the
``example`` section created from this demo.


Full Example
------------

.. code-block:: python

  import datetime

  CONTEXT = None

  def initialize(context):
      global CONTEXT
      CONTEXT = context

      ex_section = context.cli.create_section('example', 'Example section')
      demo_command = ex_section.create_command('demo', 'Demo command', run_demo)
      demo_command.create_option('--name', 'Name of the user', required=True)
      demo_command.create_flag('--show-date', 'If specified, the date will be displayed')

  def run_demo(**kwargs):
      CONTEXT.prompt.write('Hello %s' % kwargs['name'])
      if kwargs['show-date']:
          CONTEXT.prompt.write(datetime.datetime.now())

