Create your backend =================== Every backend is based on the common ground of some elements provided by the netjsonconfig library. The ``BaseBackend``, ``BaseConverter``, ``BaseParser`` and ``BaseRenderer`` are a battle proven set of tools that can be extended when creating you backend. But the netjsonconfig package is not a playground to experiment, your contributions to a new backend should start elsewhere, a different package, where you are in control and can make errors and experiment more. Netjsonconfig can now discover packages that provides a custom backend using a feature available in the Python packaging ecosystem which is called `entry_points`. To create a new backend start from scratch with a new folder and add this file to your project root directory. .. code-block:: python # example_backend/setup.py from setuptools import setup, find_packages setup( name="example_backend", version="0.0.0", description="an example to illustrate a netjsonconfig backend as an external module", install_requires=["netjsonconfig>=0.6.3"], packages=find_packages(), entry_points={ "netjsonconfig.backends": [ "example=example_backend.__init__:ExampleBackend", ] }, ) this file can be used to create a package that can be installed using pip or other tools in the python ecosystem. You can find more information about Python packaging `at packaging.python.org `_ and `at the hitchhikers guide to packaging `_. The most important part is to give your package a good name, a well thought description and to add the ``entry_points`` keyword argument with the following code .. code-block:: python { # this is used by netjsonconfig # to find your backend "netjsonconfig.backends": [...] } Now your package will be in the list of backends that netjsonconfig can use! But we still have to give us a name to be unique! Netjsonconfig already defined the names ``openwisp``, ``openwrt`` and ``openvpn`` but you can choose whatever you like most. The name ``netjsonconfig.backends`` will be associated with a list of classes from your package that will be presented to netjconfig at runtime. To specify which classes you want to expose write the triple ``name``, ``path`` and ``class_name`` using the format ``name=path:class_name`` as in the example below. The ``path`` part is simply the path to the file that contains the class you want to expose and the ``class_name`` is the name of the class. .. code-block:: python { "netjsonconfig.backends": [ # name=path:class_name "example=example_backend.__init__:ExampleBackend", ] } The previous example can be used with the following class definition .. code-block:: python # example_backend/example_backend/__init__.py from netjsonconfig.backends.base.backend import BaseBackend from netjsonconfig.backends.base.renderer import BaseRenderer from netjsonconfig.backends.base.parser import BaseParser from netjsonconfig.schema import schema as default_schema class ExampleBackend(BaseBackend): schema = default_schema converter = [] parser = BaseParser renderer = BaseRenderer Once you have your python package configured with the correct entry points you should have a directory tree that looks like this. .. code-block:: bash $ tree example_backend example_backend ├── example_backend │   └── __init__.py └── setup.py And now you can install your package using ``pip install -e ./example_backend`` or ``python setup.py install``. As ``netjsonconfig`` is a dependency for ``example_backend`` you can use your backend directly from the command line, e.g. .. code-block:: bash $ netjsonconfig usage: netjsonconfig [-h] [--config CONFIG] [--templates [TEMPLATES [TEMPLATES ...]]] [--native NATIVE] --backend {openwrt,openwisp,openvpn,example} --method {render,generate,write,validate,json} [--args [ARGS [ARGS ...]]] [--verbose] [--version] netjsonconfig: error: the following arguments are required: --backend/-b, --method/-m Notice the *example* in ``{openwrt,openwisp,openvpn,example}``? That's your backend! The name exposed is the one chosen in the *name*, *path*, *class* triple from before .. code-block:: python # name=path:class "example=example_backend.__init__:ExampleBackend",