Skip to content

Commit

Permalink
Merge pull request #26 from robshakir/packaging
Browse files Browse the repository at this point in the history
Merge packaged pyangbind project - https://pypi.python.org/pypi/pyangbind/0.2.0
  • Loading branch information
robshakir committed Jan 11, 2016
2 parents fc87598 + 7cccf85 commit b99e92d
Show file tree
Hide file tree
Showing 70 changed files with 1,975 additions and 1,682 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,6 @@
*.pyc
*.swp
*.egg-info
build/*
dist/*
tests/pyvirtualenv
23 changes: 18 additions & 5 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,28 @@
## Changelog

25-05-2015 - rel.00:
25-05-2015 - tag: rel.00:

* Initial release of PyangBind outside of BT.

09-06-2015 - rel.01-alpha.01:
09-06-2015 - tag: rel.01-alpha.01:

* Merge of xpath-helper-04 into master.
* Support for leafref with XPath lookups.

04-09-2015 - rel.02:
04-09-2015 - tag: rel.02:

* Merge of serialiser-11 into master.
* Support for serialising to JSON, extensions, refactored xpathhelper, and a number of new types.
* Support for serialising to JSON, extensions, refactored xpathhelper, and a number of new types.

31-12-2015 - 0.1.0

* Adopt semantic versioning.
* First release packaged for PyPi

11-01-2016 - 0.1.3

* Final test release to PyPI's test repo.
* To be released as 0.2.0.

11-01-2016 - 0.2.0
* Released to PyPI.
12 changes: 12 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
## Contributing to PyangBind

Contributions to PyangBind are very welcome, either directly via pull requests, or as feature suggestions or bug reports.

A couple of requests:

* Code style is currently intended to be PEP-8 compliant, however, rules E111, E114, E127 and E128 are ignored. The standard indentation in PyangBind code is 2 spaces (**not** 4), and continued lines are made to be subjectively aesthetically pleasing/readable.
* Please run tests/run.sh and check that all the tests pass if you're changing code. New tests are much appreciated. If you'd like to use a different test framework, that's fine -- just please ensure that `TESTNAME/run.py` runs the tests.
* If you have an issue with generated code/odd errors during build -- please do just e-mail this over or open an issue. If you can't share the YANG itself, then anonymised YANG is very welcome.
* If you'd like to discuss the best design for a feature, or don't get how a feature fits in, please open an issue, or send e-mail.

And most of all, thanks for contributions :-)
3 changes: 3 additions & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
include pyangbind/plugin/*.py
include *.md
include LICENSE
29 changes: 18 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,27 @@ Development of **PyangBind** has been motivated by consumption of the [**OpenCon
**PyangBind** can be called by using the Pyang ```--plugindir``` argument:

```
pyang --plugindir /path/to/repo -f pybind <yang-files>
pyang --plugindir <path-to-pyangbind-plugindir> -f pybind <yang-files>
```

Output can be written in two different ways:
By default, the pyangbind plugin will be installed in the `site-packages/pyangbind/plugin` directory, this may be the Python system or user packages. It is possible to determine the plugin's path using

```
/usr/bin/env python -c "import pyangbind; import os; print "%s/plugin" % os.path.dirname(pyangbind.__file__)"
```

Output is written based on the following options:

* if no options given to pyang, it will write to ```stdout``` as per pyang's default behaviour.
* if ```-o``` is given to pyang, all output will be written to the file specified as the target fd.
* if ```--split-class-dir``` is specified, a hierarchy of modules is created such that each container corresponds to its own module. This allows statements like ```from openconfig import bgp.global.config``` to be used and means that significantly shorter files are generated for large modules. If this option is used, the argument specified after ```-o``` is ignored.

Other options can be used on the command-line:

* ```--use-xpath-helper``` - this currently uses the ```YANGPathHelper``` class in ```lib/xpathhelper.py``` to allow registration of objects into an XML tree, and hence allow XPATH references to be resolved ([see the relevant documentation](#leafref-helper)). In the future, arguments to this function will allow the user to load in their own helper function. See the documentation for ```PyangBindXpathHelper``` in ```lib/xpathhelper.py``` for the functions that this class should provide.
* ```--pybind-class-dir=DIR``` - by default PyangBind will assume that in the module directory of any bindings file, there exists a ```lib/``` directory. This provides PyangBind access the relevant YANG type classes. Using this argument specifies an alternate directory which is then appended to the Python path variable, such that the user does not need to create ```lib``` symlinks. If you use ```--split-class-dir``` you *will* want to use this option!
* ```--interesting-extension=EXTENSION-MODULE``` - PyangBind by default will do nothing with extension statements. If a module name (e.g., ```foo-extensions```) is specified on the command line, PyangBind will add entries to an extension dictionary where they are from an interesting module. Multiple modules may be specified. Such entries can then be accessed via ```yang_object.extensions()```.
* ```--use-xpath-helper``` - Use the `path_helper` argument to each PyangBind class. The `path_helper` is used to register objects into an XML tree, and hence allow XPATH references to be resolved ([see the relevant documentation](#leafref-helper)). Whilst a `YANGPathHelper` class (see `pyangbind/lib/xpathhelper.py`) is currently expected, it is possible for an alternative to be utilised. See the documentation for ```PyangBindXpathHelper``` in ```pyangbind/lib/xpathhelper.py``` for the methods that an alternative class should provide.
* ```--interesting-extension=EXTENSION-MODULE``` - PyangBind by default will do nothing with extension statements. If a module name (e.g., ```foo-extensions```) is specified on the command line, PyangBind will add entries to an extension dictionary where they are from an interesting module. Multiple modules may be specified. Such entries can then be accessed via ```yang_object._extensions()```.
* ```--build-rpcs``` - this switch causes pyangbind to read the `rpc` statements that are defined in each YANG module and compile these into a set of classes that are stored in a Python module called ```<yang-module-name>_rpc```. Each RPC is created with the relevant `input` and `output` statements. Where instances of these classes are created, they do not register against the supplied XPath helper module (due to the fact that RPC instances are not part of the 'standard' data tree). A supplied `path_helper` argument will still be used by the modules to resolve leaf references where required.
* ```--use-extmethods``` - this switches causes a dictionary of the form `{ "path": class }` to be referenced by each PyangBind object. It is then possible to call a set of extended method names (specified [here](https://github.com/robshakir/pyangbind/blob/master/lib/yangtypes.py#L917-L930)) against each object. When these methods are called, the object will look its path up in the supplied dictionary, and if an entry exists, call the corresponding method of the supplied class. This can be used to implement actions against certain classes without needing to change the generated code. Currently, the method names are static - a possible future extension is to make the set of extmethod names dynamic.

Once bindings have been generated, each YANG module is included as a top-level class within the output bindings file. Using the following module as an example:

Expand Down Expand Up @@ -69,7 +76,7 @@ bindings = pyangbind_example()
bindings.parent.integer = 12
```

Note, that as of release.01, pyangbind expects a ```lib``` directory locally to the ```bindings.py``` file referenced above. This contains the ```xpathhelper``` and ```yangtypes``` modules - such that this code does not need to be duplicated across each ```bindings.py``` file. In the future, it is intended that these functions be provided as an installable module, but this is is still *TODO*. See the commentary relating to ```--pybind-class-dir``` if this sounds irritating :-).
As of version 0.2.0, pyangbind is now packaged such that the helper modules can be installed through ```pip```. This means that all modules expect to be able to import ```pyangbind.lib.<modname>```. This version therefore **removes** those switches that were used to hack around the fact that pyangbind was not installable.

Each data leaf can be referred to via the path described in the YANG module. Each leaf is represented by the base class that is described in the [type support](#type-support) section of this document.

Expand All @@ -92,7 +99,7 @@ Each native type is wrapped in a YANGDynClass dynamic type - which provides help
* ```default()``` - returns the default value of the YANG leaf.
* ```changed()``` - returns ```True``` if the value has been changed from the default, ```False``` otherwise.
* ```yang_name()``` - returns the YANG data element's name (since not all data elements name are safe to be used in Python).
* ```extensions()``` - returns the dictionary of interesting extensions.
* ```_extensions()``` - returns the dictionary of interesting extensions.

### YANG Container Methods

Expand All @@ -103,7 +110,7 @@ In addition, a YANG container provides a set of methods to determine properties
* ```elements()``` - which provides a list of the elements of the YANG-described tree which branch from this container.
* ```get(filter=False)``` - providing a means to return a Python dictionary hiearchy showing the tree branching from the container node. Where the ```filter``` argument is set to ```True``` only elements that have changed from their default values due to manipulation of the model are returned - when filter is not specified the default values of all leaves are returned.

As of a recent release, iteration through container objects will return a key,value list which can be used to walk through leaf or container objects within a particular container.
Iteration through container objects will return a key,value list which can be used to walk through leaf or container objects within a particular container.

### YANG List Methods

Expand Down Expand Up @@ -146,10 +153,10 @@ AttributeError: can't set attribute

The ```YANGPathHelper``` class in the xpathhelper module provides a lightweight means to be able to establish a tree structure against which pyangbind modules register themselves. In order to enable this behaviour use the ```---with-xpathhelper``` flag during code generation.

When ```--with-xpathhelper``` modules will automatically register themselves against the tree structure that is provided using the ```path_helper``` keyword-argument. ```path_helper``` is expected to be set to an instance of ```lib.xpathhelper.YANGPathHelper``` - which is created independently to the root YANG module, as shown below:
When ```--with-xpathhelper``` modules will automatically register themselves against the tree structure that is provided using the ```path_helper``` keyword-argument. ```path_helper``` is expected to be set to an instance of ```pyangbind.lib.xpathhelper.YANGPathHelper``` - which is created independently to the root YANG module, as shown below:

```python
from lib.xpathhelper import YANGPathHelper
from pyangbind.lib.xpathhelper import YANGPathHelper
from oc_bgp import bgp
from oc_routing_policy import routing_policy as rpol

Expand Down Expand Up @@ -247,7 +254,7 @@ As of September 2015 (no current release-tag), PyangBind also provides means to
In order to use the JSON serialisation, the custom encoder needs to be imported:

```python
from lib.serialise import pybindJSONEncoder
from pyangbind.lib.serialise import pybindJSONEncoder
print json.dumps(pyangbind_obj.get(filter=True), \
cls=pybindJSONEncoder, indent=4)
```
Expand Down
38 changes: 38 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
PyangBind
=========

PyangBind is a plugin for pyang which converts YANG data models into a Python class hierarchy, such that Python can be used to manipulate data that conforms with a YANG model.

This module provides the supporting classes and functions that PyangBind modules utilise, particularly:

* pyangbind.base.PybindBase - which is the parent class inherited by all container or module YANG objects.

* pyangbind.pybindJSON - which containers wrapper functions which can be used to help with serialisation of YANG to JSON.

* pyangbind.serialise.pybindJSONEncoder - a class that can be used as a custom encoder for the JSON module to serialise PyangBind class hierarchies to JSON.

* pyangbind.serialise.pybindJSONDecoder - a class that can be used as a custom decoder to load JSON-encoded instances of YANG models into a PyangBind class hierarchy.

* pyangbind.xpathhelper.YANGPathHelper - a class which can have objects registered against it, and subsequently retrieved from it using XPATH expressions. This module also includes parent classes that can be used to implement other helper modules of this nature.

* pyangbind.yangtypes: The various functions which generate python types that are used to represent YANG types, and some helper methods.

- pyangbind.yangtypes.is_yang_list and is_yang_leaflist are self explainatory, but may be useful.

- pyangbind.yangtypes.safe_name is used throughout PyangBind to determine how to map YANG element names into Python attribute names safely.

- pyangbind.yangtypes.RestrictedPrecisionDecimalType - generates wrapped Decimal types that has a restricted set of decimal digits - i.e., can deal with fraction-digits arguments in YANG.

- pyangbind.yangtypes.RestrictedClassType - generates types which wrap a 'base' type (e.g., integer) with particular restrictions. The restrictions are supplied as a dictionary, or with specific arguments if single restrictions are required. Currently, the restrictions supported are regexp matches, ranges, lengths, and restrictions to a set of values (provided as keys to a dict).

- pyangbind.yangtypes.TypedListType - generates types which wrap a list to restrict the objects that it may contain.

- pyangbind.yangtypes.YANGListType - generates types which wrap a class representing a container, such that it acts as a YANG list.

- pyangbind.yangtypes.YANGBool - a boolean class.

- pyangbind.yangtypes.YANGDynClass - generates types which consist of a wrapper (YANGDynClass) and a wrapped object which may be any other class. YANGDynClass is a meta-class that provides additional data on top of the attributes and functions of the wrapped class.

- pyangbind.yangtypes.ReferenceType - generates types which can use a pyangbind.xpathhelper.PybindXpathHelper instance to look up values - particularly to support leafrefs in YANG.

Usage documentation for PyangBind itself can be found on GitHub: https://github.com/robshakir/pyangbind
1 change: 1 addition & 0 deletions lib
File renamed without changes.
1 change: 1 addition & 0 deletions pyangbind/lib/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
__version__ = "0.1.1"
File renamed without changes.
File renamed without changes.
10 changes: 5 additions & 5 deletions lib/serialise.py → pyangbind/lib/serialise.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
import numpy
from collections import OrderedDict
from decimal import Decimal
from yangtypes import safe_name
from pyangbind.lib.yangtypes import safe_name
from types import ModuleType
import copy

Expand Down Expand Up @@ -69,20 +69,20 @@ def encode(self, obj):

def default(self, obj):
def jsonmap(obj, map_val):
if map_val in ["lib.yangtypes.RestrictedClass"]:
if map_val in ["pyangbind.lib.yangtypes.RestrictedClass"]:
map_val = getattr(obj, "_restricted_class_base")[0]

if map_val in ["numpy.uint8", "numpy.uint16", "numpy.uint32",
"numpy.uint64", "numpy.int8", "numpy.int16", "numpy.int32",
"numpy.int64"]:
return int(obj)
elif map_val in ["lib.yangtypes.ReferencePathType"]:
elif map_val in ["pyangbind.lib.yangtypes.ReferencePathType"]:
return self.default(obj._get())
elif map_val in ["lib.yangtypes.RestrictedPrecisionDecimal"]:
elif map_val in ["pyangbind.lib.yangtypes.RestrictedPrecisionDecimal"]:
return float(obj)
elif map_val in ["bitarray.bitarray"]:
return obj.to01()
elif map_val in ["lib.yangtypes.YANGBool"]:
elif map_val in ["pyangbind.lib.yangtypes.YANGBool"]:
if obj:
return True
else:
Expand Down
File renamed without changes.
File renamed without changes.
Loading

0 comments on commit b99e92d

Please sign in to comment.