Skip to content

Commit 2bf557c

Browse files
committed
added external backend querying at runtime
this will use the entry_points feature of pkg_resources to query other installed packages for additional backends. - [doc] added page create_your_backend - prevent import errors from crashing the cli during load the module will be imported but if an ImportError is raised the cli crash. This will prevent the cli from crashing but will not expose the backend to the user. An error message will be logger to console - exclude except form from flake qa this form is the only one compatible between python2 and python3 but flake is also worried about the e variable which is not used
1 parent 11cfed1 commit 2bf557c

5 files changed

Lines changed: 121 additions & 9 deletions

File tree

bin/netjsonconfig

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
#!/usr/bin/env python
22

3+
import argparse
34
import os
45
import sys
6+
57
import six
6-
import argparse
8+
79
import netjsonconfig
810

911
description = """
@@ -56,7 +58,7 @@ output = parser.add_argument_group('output')
5658

5759
output.add_argument('--backend', '-b',
5860
required=True,
59-
choices=['openwrt', 'openwisp', 'openvpn'],
61+
choices=netjsonconfig.get_backends().keys(),
6062
action='store',
6163
type=str,
6264
help='Configuration backend')
@@ -166,13 +168,8 @@ context = dict(os.environ)
166168
method = args.method
167169
method_arguments = parse_method_arguments(args.args)
168170

169-
backends = {
170-
'openwrt': netjsonconfig.OpenWrt,
171-
'openwisp': netjsonconfig.OpenWisp,
172-
'openvpn': netjsonconfig.OpenVpn
173-
}
174171

175-
backend_class = backends[args.backend]
172+
backend_class = netjsonconfig.get_backends()[args.backend]
176173
try:
177174
options = dict(templates=templates, context=context)
178175
if args.config:
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
2+
===================
3+
Create your backend
4+
===================
5+
6+
.. include:: ../_github.rst
7+
8+
Every backend is based on the common ground of some elements provided by the
9+
netjsonconfig library. The `BaseBackend`, `BaseConverter`, `BaseParser` and
10+
`BaseRenderer` are a battle proven set of tools that can be extended when
11+
creating you backend.
12+
13+
But the netjsonconfig package is not a playground to experiment, your contributions
14+
to a new backend should start elsewhere, a different package, where you are in control
15+
and can make errors and experiment more.
16+
17+
Netjsonconfig can now discover packages that provides a custom backend using
18+
a feature available in the Python packaging ecosystem which is called `entry_points`.
19+
20+
To create a new backend start from scratch with a new folder and add this file to your
21+
project root directory.
22+
23+
.. code-block:: python
24+
25+
# setup.py
26+
from setuptools import setup, find_packages
27+
28+
setup(
29+
name='example_backend',
30+
version='0.0.0',
31+
description='an example to illustrate a netjsonconfig backend as an external module',
32+
packages=find_packages(),
33+
entry_points={
34+
'netjsonconfig.backends': [
35+
'example=example_backend.__init__:ExampleBackend',
36+
]
37+
}
38+
)
39+
40+
this file can be used to create a package that can be installed using pip or other tools
41+
in the python ecosystem. You can find more information about Python packaging
42+
`at packaging.python.org <https://packaging.python.org/>`_
43+
and `at the hitchhikers guide to packaging <https://the-hitchhikers-guide-to-packaging.readthedocs.io/en/latest/>`_.
44+
45+
The most important part is to give your package a good name, a well thought description and
46+
to add the `entry_points` keyword argument with the following code
47+
48+
.. code-block:: python
49+
50+
{
51+
# this is used by netjsonconfig
52+
# to find your backend
53+
'netjsonconfig.backends': [
54+
...
55+
]
56+
}
57+
58+
Now your package will be in the list of backends that netjsonconfig can use!
59+
60+
But we still have to give us a name to be unique! Netjsonconfig already
61+
defined the names `openwisp`, `openwrt` and `openvpn` but you can choose
62+
whatever you like most.
63+
64+
The name `netjsonconfig.backends` will be associated with a list of classes
65+
from your package that will be presented to netjconfig at runtime. To specify
66+
which classes you want to expose write the triple `name`, `path` and `class_name`
67+
using the format `name=path:class_name` as in the example below.
68+
69+
The `path` part is simply the path to the file that contains the class
70+
you want to expose and the `class_name` is the name of the class.
71+
72+
.. code-block:: python
73+
74+
{
75+
'netjsonconfig.backends': [
76+
# name=path:class_name
77+
'example=example_backend.__init__:ExampleBackend',
78+
]
79+
}
80+
81+
The previous example can be used with the following class definition
82+
83+
.. code-block:: python
84+
85+
# example_backend/__init__.py
86+
from netjsonconfig.backends.base.backend import BaseBackend
87+
from netjsonconfig.backends.base.renderer import BaseRenderer
88+
from netjsonconfig.backends.base.parser import BaseParser
89+
90+
from netjsonconfig.schema import schema as default_schema
91+
92+
class ExampleBackend(BaseBackend):
93+
schema = default_schema
94+
converter = []
95+
parser = BaseParser
96+
renderer = BaseRenderer

docs/source/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ Contents:
5555
/backends/openwrt
5656
/backends/openwisp
5757
/backends/openvpn
58+
/backends/create_your_backend
5859
/general/commandline_utility
5960
/general/running_tests
6061
/general/contributing

netjsonconfig/__init__.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,24 @@
1+
from pkg_resources import iter_entry_points
2+
import logging
3+
14
from .version import VERSION, __version__, get_version # noqa
25

36
from .backends.openwrt.openwrt import OpenWrt # noqa
47
from .backends.openwisp.openwisp import OpenWisp # noqa
58
from .backends.openvpn.openvpn import OpenVpn # noqa
9+
10+
11+
def get_backends():
12+
default = {
13+
'openwrt': OpenWrt,
14+
'openwisp': OpenWisp,
15+
'openvpn': OpenVpn,
16+
}
17+
logger = logging.getLogger(__name__)
18+
19+
for entry_point in iter_entry_points('netjsonconfig.backends'):
20+
try:
21+
default.update({entry_point.name.lower(): entry_point.load()})
22+
except ImportError as e: # noqa
23+
logger.error("Error loading backend {}".format(entry_point.name.lower()))
24+
return default

netjsonconfig/backends/openwrt/schema.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
from ..openvpn.schema import base_openvpn_schema
77
from .timezones import timezones
88

9-
109
default_radio_driver = "mac80211"
1110

1211

0 commit comments

Comments
 (0)