
.. _developing:

Developing a Plugin
===================

This section describes how you can start developing your own plugin to
extend the capabilities of this tool.

Prerequisites
-------------

#. Install the Configuration Tool using the Windows Installer. This
   installer does the following:

   - installs the version of Python that the application needs, if it
     isn't installed on the machine already.

   - creates a virtualenv in the installation folder (``C:\Program
     Files (x86)\QualcommDeviceConfigTool``). See this `reference
     document <https://docs.python.org/3/tutorial/venv.html>`_ for
     more information on Python virtualenvs.

   - installs into this virtualenv the application wheel and the plugin
     wheels that ship in this installer package. Currently these are:

     #. ``pyconfigtool-<version>-py3-none-any.whl``
     #. ``config_xml-<version>-py3-none-any.whl``
     #. ``config_yaml-<version>-py3-none-any.whl``
     #. ``pyconfigAdb-<version>-py3-none-any.whl``
     #. ``pyconfigQsap-<version>-py3-none-any.whl``

   where the ``<version>`` of each plugin can be different. Each of these
   Python wheels might have their own dependencies on other Python
   packages, and these are installed automatically by pulling them down
   from the `PyPI warehouse <https://pypi.org>`_.

#. Depending on your prefered Python development environment, you
   might want to have an IDE installed, too, such as PyCharm, PyDev in
   Eclipse, or Netbeans with its Python plugin. Configuring these IDEs is
   beyond the scope of this document; we will simply describe how to
   develop a plugin through the command line.

To use this virtualenv you need to activate it in any shell that is
used for developing the plugin. Activate it by running::

    C:\some\folder>  "C:\Program Files (x86)\QualcommDeviceConfigTool\pyenv36\Scripts\activate"
    (pyenv36) C:\some\folder>

for a Python 3.6 variant.


Code structure
--------------

In a clean folder somewhere else on your machine, some boiler-plate
code needs be created. Let's imagine that you are creating a plugin
called ``myconfigplugin``. Follow these instructions for creating the
scaffolding of this plugin.

#. Create a folder called ``myconfigplugin``.

#. Create a file in this folder called ``setup.py`` with the following
   contents::

     from setuptools import setup, find_packages

     VERSION = '0.1.0'
     DESCRIPTION = "A description of the plugin"
     LONG_DESCRIPTION = "A longer description of the plugin..."
     INSTALL_REQUIRES = [
         'pyconfigtool',
         'zope.interface',
         'zope.component',
         'zope.configuration',
         'typing',
      ]
      setup(
         name='myconfigplugin',
         version=VERSION,
         description=DESCRIPTION,
         long_description=LONG_DESCRIPTION,
         classifiers=[
            'Development Status :: 3 - Alpha',
            'Programming Language :: Python :: 3.6',
         ],
         install_requires=INSTALL_REQUIRES,
         entry_points={
             'config_tool_plugin': [
                 'config_tool_plugin=myconfigplugin:get_configuration_file',
             ],
         },
         packages=find_packages(exclude=['ez_setup']),
         include_package_data=True,
         package_data={'myconfigplugin': ['configure.zcml']},
         zip_safe=False
     )
     
   There are a two points to note at this stage. The first is that the
   ``name`` field must be the plugin name. The second is the
   definition of the ``entry_points``, which is the key requirement
   for this Python package to be recognized as a Configuration Tool
   plugin. The application will look for any installed Python packages
   which have an entry point called ``config_tool_plugin``, and this
   should point to a function which when called should return the path
   to a Zope configration file - more on this below.

#. Within the ``myconfigplugin`` folder, create another folder which
   also has the name ``myconfigplugin`` (this structure is typical for
   many Python packages). Within the inner ``myconfigplugin`` folder,
   add a file called ``__init__.py`` with the following contents::

     from pathlib import Path

     def get_configuration_file() -> str:
         """Return the path to the configure.zcml file. This is the function
         that is declared as the entry point for this plugin.
         """
         return str(Path(__file__).parent / 'configure.zcml')

   The ``get_configuration_file`` function returns the path of the
   ``configure.zcml`` file that should be placed in the same folder as
   the ``__init__.py`` file. For now, this file wont define any new
   adapters or utilities, but a valid XML file with the correct schema
   needs to be provided, as described next.

#. Create the file ``configure.zcml`` in the same folder with the
   following contents::

     <configure xmlns="http://namespaces.zope.org/zope"
                i18n_domain="zope">
       <!--
           This file is used to configure the interfaces and classes
           for the example plugin to the Qualcomm Device Configuration Tool.

           It is consumed by the zope.configuration package, and uses the
           zope.interface package to enable separation of interface and
           implementation in large Python projects.
       -->
       <include package="zope.component" file="meta.zcml" />

       <!-- Utilities - none so far
       -->

       <!-- Adapters - none so far
       -->

     </configure>
     
   This XML file uses the schema define by the Zope component model
   definition.

With this boiler plate in place, we are ready to install this package
into the Configuration Tool's virtualenv. Because the virtualenv is in
a folder in which only the Administrator has write-permissions, we
need to do a one-off install of this Python package with elevated
privileges. In a DOS shell which has been **granted Administrator
privileges** cd to the upper ``myconfigplugin`` folder, and then run the
following::

  C:\some\folder\myconfigplugin>  "C:\Program Files (x86)\QualcommDeviceConfigTool\pyenv36\Scripts\python" setup.py develop

This command will install a *reference* to the
package in this folder. This means you don't have to re-install it
after modifying it - simply re-run the application.

Return back to the non-elevated shell window which is activated (you
don't want to run as Administrator all the time!), and run the
Configuration Tool from the command line::

  (pyenv36) C:\some\folder\myconfigplugin> py -m pyconfigtool

The Configuration Tool should now start up. You can check that this
plugin has been recognized by clicking the ``Help`` -> ``About`` menu
item to see the application details: the myconfigplugin should be
listed in this dialog.

We now have a working development environment for developing a
Configuration Tool plugin! Of course this plugin doesn't do anything
yet, but the scaffolding is in place. Read on to learn how we can use
some of the APIs provided by the application to achieve a simple goal.
