[
  {
    "path": ".coveragerc",
    "content": "# .coveragerc to control coverage.py\n[run]\nbranch = True\nomit =\n    */.tox/*\n    */.virtualenvs/*\n    */tests/*\n    */env/*\n    # We don't currently have a way to track code coverage of the plugin itself.\n    pyangbind/plugin/*\nsource = pyangbind\n\n[report]\n# Regexes for lines to exclude from consideration\nexclude_lines =\n    # Have to re-enable the standard pragma\n    pragma: no cover\n\n    # Don't complain about missing debug-only code:\n    def __repr__\n    if self\\.debug\n\n    # Don't complain if tests don't hit defensive assertion code:\n    raise AssertionError\n    raise NotImplementedError\n\n    # Don't complain if non-runnable code isn't run:\n    if 0:\n    if __name__ == .__main__.:\n\n\nignore_errors = True\n"
  },
  {
    "path": ".editorconfig",
    "content": "# http://editorconfig.org\n\nroot = true\n\n[*]\nindent_style = space\nindent_size = 2\ninsert_final_newline = true\ntrim_trailing_whitespace = true\nend_of_line = lf\ncharset = utf-8\ntab_width = 2\nmax_line_length = 119\n\n# The JSON files contain newlines inconsistently\n[*.json]\ninsert_final_newline = ignore\n\n# Minified JavaScript files shouldn't be changed\n[**.min.js]\nindent_style = ignore\ninsert_final_newline = ignore\n\n[*.md]\ntrim_trailing_whitespace = false\n"
  },
  {
    "path": ".github/workflows/pypi.yml",
    "content": "\nname: Python package and publish\n\non:\n  release:\n    types: [published]\n\njobs:  \n  pypi-publish:\n    name: Package and upload release to PyPI\n    runs-on: ubuntu-latest\n    environment: release\n    permissions:\n      id-token: write\n    steps:\n      - uses: actions/checkout@v3\n      - name: Set up Python\n        uses: actions/setup-python@v4\n        with:\n          python-version: \"3.12\"\n      - name: Install building dependencies\n        run: pip -q install build\n      - name: Build package\n        run: python -m build --outdir dist/ .\n      - name: Install package\n        run: pip -q install dist/pyangbind-*.whl\n      - name: Test bind with pyang\n        run: |\n          export PYBINDPLUGIN=`/usr/bin/env python -c 'import pyangbind; import os; print (\"{}/plugin\".format(os.path.dirname(pyangbind.__file__)))'`\n          pyang -V --plugindir $PYBINDPLUGIN -f pybind tests/base-test.yang\n      - name: Publish package distributions to PyPI\n        uses: pypa/gh-action-pypi-publish@release/v1\n    \n    \n"
  },
  {
    "path": ".github/workflows/python-test.yml",
    "content": "# This workflow will install Python dependencies, run tests and lint with a variety of Python versions\n# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python\n\nname: Run TOX tests\n\non:\n  push:\n    branches: [ \"master\" ]\n  pull_request:\n    branches: [ \"master\" ]\n\njobs:\n  lint:\n    runs-on: ubuntu-latest\n    steps:\n    - uses: actions/checkout@v3\n    - name: Set up Python\n      uses: actions/setup-python@v3\n    - name: Install testing dependency\n      run: pip install tox\n    - name: Lint with black\n      run: tox -e black\n  unit-tests:\n    runs-on: ubuntu-latest\n    strategy:\n      fail-fast: false\n      matrix:\n        python-version: [\"3.8\", \"3.9\", \"3.10\", \"3.11\", \"3.12\", \"3.13\"]\n    steps:\n    - uses: actions/checkout@v3\n    - name: Set up Python ${{ matrix.python-version }}\n      uses: actions/setup-python@v3\n      with:\n        python-version: ${{ matrix.python-version }}\n    - name: Install testing dependencies\n      run: pip install tox\n    - name: Run tox for ${{ matrix.python-version }}\n      # Run tox using the version of Python in `PATH`\n      run: tox -e py\n"
  },
  {
    "path": ".gitignore",
    "content": "# Byte-compiled / optimized / DLL files\n__pycache__/\n*.py[cod]\n\n\n# Distribution / packaging\n.Python\nbuild/\ndevelop-eggs/\ndist/\ndownloads/\neggs/\n.eggs/\nlib64/\nparts/\nsdist/\nvar/\n*.egg-info/\n.installed.cfg\n*.egg\n\n# Virtual environments\ntests/pyvirtualenv\nenv/\n\n# Editor-specific files\n*.swp\n/.project\n/.pydevproject\n.vscode\n.idea/\n\n# Unit test / coverage reports\nhtmlcov/\n.tox/\n.coverage\n.coverage.*\n.cache\nnosetests.xml\ncoverage.xml\ncoverage-*.xml\njunit-*.xml\n*,cover\nreports/\n*.PEP8-ERRORS\n.python-version\n\n# Installer logs\npip-log.txt\npip-delete-this-directory.txt\n\n# Integration/split-class test files\ntests/integration/openconfig-bgp/include/\ntests/integration/openconfig-bgp/ocbind/\ntests/integration/openconfig-bgp/openconfig/\ntests/integration/openconfig-interfaces/include/\ntests/integration/openconfig-interfaces/ocbind/\ntests/integration/openconfig-interfaces/openconfig/\ntests/notification/bindings/\ntests/serialise/juniper-json-examples/include/\ntests/serialise/juniper-json-examples/ocbind/\ntests/serialise/juniper-json-examples/openconfig/\ntests/serialise/openconfig-serialise/include/\ntests/serialise/openconfig-serialise/ocbind/\ntests/serialise/openconfig-serialise/openconfig/\ntests/split-classes/bindings/\ntests/base-binding-out.py\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "## Contributing to PyangBind\n\nContributions to PyangBind are very welcome, either directly via pull requests, or as feature suggestions or bug reports.\n\n### Code Style\n\nTo avoid unnecessary discussions about coding style we are currently enforcing it with [black](https://github.com/ambv/black). Before pushing code:\n\n* make sure you are running the correct version of `black` as per `requirements.DEVELOPER.txt`.\n* reformat your code with `black` passing the option `--line-length 119`.\n\n### Testing\n\n * New code should be covered by tests, and run under both Python 2 and 3.\n * To ease the testing of generated bindings, there is the `tests.base.PyangBindTestCase` class which\n you may subclass in order to automate the process. A simple example of its usage can be seen in\n `tests/strings/run.py`.\n * Tests can be run via the `tox` command, or `python3 -m pytest -q tests`.\n\n### Other Issues\n\n * If you have an issue with generated code/odd errors during build -- please do just e-mail this over or open an issue.\n   If you can't share the YANG itself, then anonymised YANG is very welcome.\n * 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,\n   send an e-mail, or join us in the #pyangbind channel on the\n   [NetworkToCode Slack](https://networktocode.slack.com/).\n\nAnd most of all, thanks for contributions :-)\n"
  },
  {
    "path": "CONTRIBUTORS",
    "content": "Rob Shakir <robjs@google.com>\nDavid Barrosso <dbarrosop@dravetech.com>\nKirk Byers (GitHub: ktbyers)\nTim Martin (GitHub: timmartin)\nMark Paine (mpainenz@gmail.com)\nMark Sutherland (GitHub: marksutherland)\nDavid Lamparter (equinox@opensourcerouting.org)\nDaniam Henriques (GitHub: dhenza)\nAdam Sloboda (GitHub: asloboda-cisco)\nVikas Kumar (GitHub: kvikas)\nLluis Gifre (lluis.gifre@uam.es)\n"
  },
  {
    "path": "LICENSE",
    "content": "Copyright 2015  Rob Shakir, Jive Communications, Inc.\n                rjs@jive.com, rjs@rob.sh\n\nModifications copyright 2016, Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n"
  },
  {
    "path": "MANIFEST.in",
    "content": "include *.md\ninclude *.txt\ninclude LICENSE\n"
  },
  {
    "path": "README.md",
    "content": "[![#PyangBind][img-pyangbind]][pyangbind-docs]\n\n[![PyPI][img-pypi]][pypi-project]\n[![PyPI - License][img-license]][license]\n[![PyPI - Python Version][img-pyversion]][pypi-project]\n\n\n**PyangBind** is a plugin for [Pyang][pyang] that generates a Python class hierarchy from a YANG data model. The resulting classes can be directly interacted with in Python. Particularly, **PyangBind** will allow you to:\n\n * Create new data instances - through setting values in the Python class hierarchy.\n * Load data instances from external sources - taking input data from an external source and allowing it to be addressed through the Python classes.\n * Serialise populated objects into formats that can be stored, or sent to another system (e.g., a network element).\n\nDevelopment of **PyangBind** has been motivated by consuming the  [OpenConfig][openconfig] data models; and is intended to be well-tested against these models. The Python classes generated, and serialisation methods are intended to provide network operators with a starting point for loading data instances from network elements, manipulating them, and sending them to a network device. **PyangBind** classes also have functionality which allows additional methods to be associated with the classes, such that it can be used for the foundation of a NMS.\n\n## Contents\n\n* [Getting Started](#getting-started)\n\t* [Generating a Set of Classes](#generating-classes)\n\t* [Using the Classes in a Python Program](#using-in-python)\n\t* [Creating a Data Instance](#create-instance)\n\t* [Serialising a Data Instance](#serialising)\n\t* [Deserialising a Data Instance to Classes](#deserialising)\n  * [Example Code](#example-code)\n* [Further Documentation](#documentation)\n* [Licensing](#licensing)\n* [Acknowledgements](#acks)\n* [Test Status](#tests)\n\n\n## Getting Started <a name=\"getting-started\"></a>\n\n**PyangBind** is distributed through [PyPI][pypi], it can be installed by simply running:\n\n```\n$ pip install pyangbind\n```\n\nThe `pyangbind` module installs both the Pyang plugin (`pyangbind.plugin.*`), as well as a set of library modules (`pyangbind.lib.*`) that are used to provide the Python representation of YANG types.\n\n### Generating a Set of Classes <a name=\"generating-classes\"></a>\n\nTo generate your first set of classes, you will need a YANG module, and its dependencies. A number of simple modules can be found in the `tests` directory (e.g., `tests/base-test.yang`).\n\nTo generate a set of Python classes, Pyang needs to be provided a pointer to where PyangBind's plugin is installed. This location can be found by running:\n\n```\n$ export PYBINDPLUGIN=`/usr/bin/env python -c \\\n'import pyangbind; import os; print (\"{}/plugin\".format(os.path.dirname(pyangbind.__file__)))'`\n$ echo $PYBINDPLUGIN\n```\n\nOnce this path is known, it can be provided to the `--plugin-dir` argument to Pyang. In the simplest form the command used is:\n\n```\n$ pyang --plugindir $PYBINDPLUGIN -f pybind -o binding.py tests/base-test.yang\n```\nwhere:\n\n* `$PYBINDPLUGIN` is the location that was exported from the above command.\n* `binding.py` is the desired output file.\n* `doc/base-test.yang` is the path to the YANG module that bindings are to be generated for.\n\nThere are a number of other options for **PyangBind**, which are discussed further in the `docs/` directory.\n\n### Using the Classes in a Python Program <a name=\"using-in-python\"></a>\n\n**PyangBind** generates a (set of) Python modules. The top level module is named after the YANG module - with the name made Python safe. In general this appends underscores to reserved names, and replaces hyphens with underscores - such that `openconfig-network-instance.yang` becomes `openconfig_network_instance` as a module name.\n\nPrimarily, we need to generate a set of classes for the model(s) that we are interested in. The OpenConfig network instance module will be used as an example for this walkthrough.\n\nThe bindings can be simply generated by running the `docs/example/oc-network-instance/generate_bindings.sh` script. This script clones the openconfig/public repo to retrieve the modules, and subsequently, builds the bindings to a `binding.py` file as expressed above.\n\nThe simplest program using a PyangBind set of classes will look like:\n\n```python\n# Using the binding file generated by the `generate_bindings.sh` script\n# Note that CWD is the file containing the binding.py file.\n# Alternatively, you can use sys.path.append to add the CWD to the PYTHONPATH\nfrom binding import openconfig_network_instance\n\nocni = openconfig_network_instance()\n```\n\n### Creating a Data Instance <a name=\"create-instance\"></a>\n\nAt this point, the `ocni` object can be used to manipulate the YANG data tree that is expressed by the module.\n\nA subset of `openconfig-network-instance` looks like the following tree:\n\n```\nmodule: openconfig-network-instance\n  +--rw network-instances\n     +--rw network-instance* [name]\n        +--rw name                       -> ../config/name\n        +--rw config\n        |  +--rw name?                        string\n        ...\n        +--rw protocols\n           +--rw protocol* [identifier name]\n              +--rw identifier          -> ../config/identifier\n              +--rw name                -> ../config/name\n              +--rw config\n              |  +--rw identifier?       identityref\n              |  +--rw name?             string\n              |  +--rw enabled?          boolean\n              |  +--rw default-metric?   uint32\n              +--ro state\n              |  +--ro identifier?       identityref\n              |  +--ro name?             string\n              |  +--ro enabled?          boolean\n              |  +--ro default-metric?   uint32\n              +--rw static-routes\n              |  +--rw static* [prefix]\n              |     +--rw prefix       -> ../config/prefix\n              |     +--rw config\n              |     |  +--rw prefix?        inet:ip-prefix\n              |     |  +--rw set-tag?       oc-pt:tag-type\n              |     |  +--rw description?   string\n              ...\n```\n\nTo add an entry to the `network-instance` list, the `add` method of the `network-instance` object is used to create instance `a`.  Similarly a protocol of type `STATIC` and name of `DEFAULT` is added.  Finally, a `static` route is added:\n\n```python\nocni.network_instances.network_instance.add('a')\nocni.network_instances.network_instance['a'].protocols\nocni.network_instances.network_instance['a'].protocols.protocol.add(identifier='STATIC', name='DEFAULT')\n\nrt = ocni.network_instances.network_instance['a'].protocols.protocol['STATIC DEFAULT'].static_routes.static.add(\"192.0.2.1/32\")\n```\n\nThe `static` list is addressed exactly as per the path that it has within the YANG module - such that it is a member of the `static-routes` container (whose name has been made Python-safe), which itself is a member of the `protocols` container, which is a member of the `network-instances` container.\n\nThe `add` method returns a reference to the newly created list object - such that we can use the `rt` object to change characteristics of the newly created list entry. For example, a tag can be set on the route:\n\n```python\nrt.config.set_tag = 42\n```\n\nThe tag value can then be accessed directly via the `rt` object, or via the original `ocni` object (which both refer to the same object in memory):\n\n```python\n# Retrieve the tag value\nprint(rt.config.set_tag)\n# output: 42\n\n# Retrieve the tag value through the original object\nprint(ocni.network_instances.network_instance['a'].protocols.protocol['STATIC DEFAULT'].static_routes.static[\"192.0.2.1/32\"].config.set_tag)\n# output: 42\n```\n\nIn addition, PyangBind classes which represent `container` or `list` objects have a special `get()` method. This dumps a dictionary representation of the object for printing or debug purposes (see the sections on serialisation for outputting data instances for interaction with other devices). For example:\n\n```python\nprint(ocni.network_instances.network_instance['a'].protocols.protocol['STATIC DEFAULT'].static_routes.static[\"192.0.2.1/32\"].get(filter=True))\n# returns {'prefix': '192.0.2.1/32', 'config': {'set-tag': 42}}\n```\n\nThe `filter` keyword allows only the elements within the class that have changed (are not empty or their default) to be output - rather than all possible elements.\n\nThe `next-hop` element in this model is another list. This keyed data structure acts like a Python dictionary, and has the special method `add` to add items to it. YANG `leaf-list` types use the standard Python list `append` method to add items to it. Equally, a `list` can be iterated through using the same methods as a dictionary, for example, using `items()`:\n\n```python\n# Add a set of next_hops\nfor nhop in [(0, \"192.168.0.1\"), (1, \"10.0.0.1\")]:\n  nh = rt.next_hops.next_hop.add(nhop[0])\n  nh.config.next_hop = nhop[1]\n\n# Iterate through the next-hops added\nfor index, nh in rt.next_hops.next_hop.items():\n    print(\"{}: {}\".format(index, nh.config.next_hop))\n```\n\nWhere (type or value) restrictions exist. PyangBind generated classes will result in a Python `ValueError` being raised. For example, if we attempt to set the `set_tag` leaf to an invalid value:\n\n```python\n# Try and set an invalid tag type\ntry:\n  rt.config.set_tag = \"INVALID-TAG\"\nexcept ValueError as m:\n  print(\"Cannot set tag: {}\".format(m))\n```\n\n### Serialising a Data Instance <a name=\"serialising\"></a>\n\nClearly, populated PyangBind classes are not useful in and of themselves - the common use case is that they are sent to a external system (e.g., a router, or other NMS component). To achieve this the class hierarchy needs to be serialised into a format that can be sent to the remote entity. There are currently multiple ways to do this:\n\n * **XML** - the rules for this mapping are defined in [RFC 7950][rfc7950] - supported.\n * **OpenConfig-suggested JSON** - the rules for this mapping are currently being written into a formal specification. This is the standard (`default`) format used by PyangBind. Some network equipment vendors utilise this serialisation format.\n * **IETF JSON** - the rules for this mapping are defined in [RFC 7951][rfc7951] - some network equipment vendors use this format.\n\nAny PyangBind class can be serialised into any of the supported formats. Using the static route example above, the entire `local-routing` module can be serialised into OC-JSON using the following code:\n\n```python\nfrom pyangbind.lib.serialise import pybindIETFXMLEncoder\n# Dump the entire instance as RFC 7950 XML\nprint(pybindIETFXMLEncoder.serialise(ocni))\n```\n\nThis outputs the following XML fragment:\n\n```xml\n<openconfig-network-instance xmlns=\"http://openconfig.net/yang/network-instance\">\n  <network-instances>\n    <network-instance>\n      <name>a</name>\n      <protocols>\n        <protocol>\n          <identifier>STATIC</identifier>\n          <name>DEFAULT</name>\n          <static-routes>\n            <static>\n              <prefix>192.0.2.1/32</prefix>\n              <config>\n                <set-tag>42</set-tag>\n              </config>\n              <next-hops>\n                <next-hop>\n                  <index>0</index>\n                  <config>\n                    <next-hop>192.168.0.1</next-hop>\n                  </config>\n                </next-hop>\n                <next-hop>\n                  <index>1</index>\n                  <config>\n                    <next-hop>10.0.0.1</next-hop>\n                  </config>\n                </next-hop>\n              </next-hops>\n            </static>\n          </static-routes>\n        </protocol>\n      </protocols>\n    </network-instance>\n  </network-instances>\n</openconfig-network-instance>\n```\n\nOr, similarly, using OpenConfig-suggested JSON:\n\n```python\nimport pyangbind.lib.pybindJSON as pybindJSON\nprint(pybindJSON.dumps(ocni, indent=2))\n```\n\nThis outputs the following JSON structured text:\n\n```json\n{\n  \"network-instances\": {\n    \"network-instance\": {\n      \"a\": {\n        \"name\": \"a\",\n        \"protocols\": {\n          \"protocol\": {\n            \"STATIC DEFAULT\": {\n              \"identifier\": \"STATIC\",\n              \"name\": \"DEFAULT\",\n              \"static-routes\": {\n                \"static\": {\n                  \"192.0.2.1/32\": {\n                    \"prefix\": \"192.0.2.1/32\",\n                    \"config\": {\n                      \"set-tag\": 42\n                    },\n                    \"next-hops\": {\n                      \"next-hop\": {\n                        \"0\": {\n                          \"index\": \"0\",\n                          \"config\": {\n                            \"next-hop\": \"192.168.0.1\"\n                          }\n                        },\n                        \"1\": {\n                          \"index\": \"1\",\n                          \"config\": {\n                            \"next-hop\": \"10.0.0.1\"\n                          }\n                        }\n                      }\n                    }\n                  }\n                }\n              }\n            }\n          }\n        }\n      }\n    }\n  }\n}\n```\n\nNote here that the `static` list and all parents are represented as a JSON object (such that if this JSON is loaded elsewhere, a prefix can be referenced using `obj['network-instances']['network-instance']['a']['protocols']['protocol']['STATIC DEFAULT']['static-routes']['static']['192.0.2.1/32']`).\n\nIt is also possible to serialise a subset of the data, e.g., only one `list` or `container` within the class hierarchy. This is done as follows (into IETF-JSON):\n\n```\n# Dump the static-routes instance as JSON in IETF format\nprint(pybindJSON.dumps(ocni.network_instances.network_instance['a'].protocols.protocol['STATIC DEFAULT'], mode='ietf', indent=2))\n```\n\nAnd the corresponding output:\n\n```json\n{\n  \"openconfig-network-instance:identifier\": \"STATIC\",\n  \"openconfig-network-instance:name\": \"DEFAULT\",\n  \"openconfig-network-instance:static-routes\": {\n    \"static\": [\n      {\n        \"prefix\": \"192.0.2.1/32\",\n        \"config\": {\n          \"set-tag\": 42\n        },\n        \"next-hops\": {\n          \"next-hop\": [\n            {\n              \"index\": \"0\",\n              \"config\": {\n                \"next-hop\": \"192.168.0.1\"\n              }\n            },\n            {\n              \"index\": \"1\",\n              \"config\": {\n                \"next-hop\": \"10.0.0.1\"\n              }\n            }\n          ]\n        }\n      }\n    ]\n  }\n}\n```\n\nHere, note that the list is represented as a JSON array, as per the IETF specification; and that only the `static-routes` children of the object have been serialised.\n\n### Deserialising a Data Instance <a name=\"deserialising\"></a>\n\nPyangBind also supports taking data instances from a remote system (or locally saved documents) and loading them into either a new, or existing set of classes. This is useful for when a remote system sends a data instance in response to a query - and the programmer wishes to ingest this response such that further logic can be performed based on it.\n\nInstances can be deserialised from any of the supported serialisation formats (see above) into the classes.\n\nTo de-serialise into a new object, the `load` method of the serialise module can be used:\n\n```python\nimport binding\nnew_ocni = pybindJSON.load(os.path.join(\"json\", \"oc-ni.json\"), binding, \"openconfig_network_instance\")\n```\n\nThis creates a new instance of the `openconfig_network_instance` class that is within the `binding` module, and loads the data from `json/oc-ni.json` into it. The `new_ocni` object can then be manipulated as per any other class:\n\n```python\n# Manipulate the data loaded\nprint(\"Current tag: %d\" % new_ocni.network_instances.network_instance['a'].protocols.protocol['STATIC DEFAULT'].static_routes.static['192.0.2.1/32'].config.set_tag)\n# Outputs: 'Current tag: 42'\n\nnew_ocni.network_instances.network_instance['a'].protocols.protocol['STATIC DEFAULT'].static_routes.static['192.0.2.1/32'].config.set_tag += 1\nprint(\"New tag: %d\" % new_ocni.network_instances.network_instance['a'].protocols.protocol['STATIC DEFAULT'].static_routes.static['192.0.2.1/32'].config.set_tag)\n# Outputs: 'Current tag: 43'\n```\n\nEqually, a JSON instance can be loaded into an existing set of classes - this is done by directly calling the relevant deserialisation class -- in this case `pybindJSONDecoder`:\n\n```python\n# Load JSON into an existing class structure\nfrom pyangbind.lib.serialise import pybindJSONDecoder\nimport json\n\nietf_json = json.load(open(os.path.join(\"json\", \"oc-ni_ietf.json\"), 'r'))\npybindJSONDecoder.load_ietf_json(ietf_json, None, None, obj=new_ocni.network_instances.network_instance['a'].protocols.protocol['STATIC DEFAULT'])\n```\n\nThe direct `load_ietf_json` method is handed a JSON object - and no longer requires the arguments for the module and class name (hence they are both set to `None`), rather the optional `obj=` argument is used to specify the object that corresponds to the JSON that is being loaded.\n\nFollowing this load, the classes can be iterated through - showing both the original loaded route (`192.0.2.1/32`) and the one in the IETF JSON encoded file (`192.0.2.2/32`) exist in the data instance:\n\n```python\n# Iterate through the classes - both the 192.0.2.1/32 prefix and 192.0.2.2/32\n# prefix are now in the objects\nfor prefix, route in new_ocni.network_instances.network_instance['a'].protocols.protocol['STATIC DEFAULT'].static_routes.static.items():\n  print(\"Prefix: {}, tag: {}\".format(prefix, route.config.set_tag))\n\n# Output:\n#\t\tPrefix: 192.0.2.2/32, tag: 256\n#\t\tPrefix: 192.0.2.1/32, tag: 42\n```\n\n### Example Code <a name=\"example-code\"></a>\nThis worked example can be found in the `docs/example/oc-network-instance` directory.\n\n## Further Documentation <a name=\"documentation\"></a>\n\nFurther information as to the implementation and usage of PyangBind can be found in the `docs/` directory -- the [README](docs/README.md) provides a list of documents and examples container therein.\n\n## Licensing <a name=\"licensing\"></a>\n```\nCopyright 2015, Rob Shakir (rjs@rob.sh)\nModifications copyright, the Pyangbind contributors.\n\nThis project has been supported by:\n          * Jive Communications, Inc.\n          * BT plc.\n          * Google, Inc.\n          * GoDaddy, LLC.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n```\n\n## Acknowledgements <a name=\"acks\"></a>\n* This project was initiated as part of BT plc. Network Architecture 'future network management' projects.\n* Additional development efforts were supported by [Jive Communications, Inc][jive].\n* Current maintenance is supported by [Google][google].\n* Key contributions have been made to this project by the following developers, and companies. Many thanks\n  are extended to them:\n  * [GoDaddy][godaddy], particularly Joey Wilhelm's herculean efforts to refactor test code to use the `unittest` framework.\n  * David Barroso, who initiated efforts to address Python 3 compatibility, and a number of other enhancements.\n* Design, debugging, example code, and ideas have been contributed by:\n  * Members of the [OpenConfig][openconfig] working group.\n  * The managability team at Juniper Networks.\n\n\n[img-pyangbind]: https://cdn.rob.sh/img/pyblogo_gh.png\n[img-travis]: https://img.shields.io/travis/robshakir/pyangbind.svg\n[img-codecov]: https://img.shields.io/codecov/c/github/robshakir/pyangbind.svg\n[img-pypi]: https://img.shields.io/pypi/v/pyangbind.svg\n[img-license]: https://img.shields.io/pypi/l/pyangbind.svg\n[img-pyversion]: https://img.shields.io/pypi/pyversions/pyangbind.svg\n\n[pyangbind-docs]: https://github.com/robshakir/pyangbind/tree/master/docs\n[travis]: https://travis-ci.org/robshakir/pyangbind\n[codecov]: https://codecov.io/gh/robshakir/pyangbind\n[pypi-project]: https://pypi.org/project/pyangbind/\n[license]: http://www.apache.org/licenses/LICENSE-2.0\n[pyang]: https://github.com/mbj4668/pyang\n[openconfig]: http://www.openconfig.net\n[pypi]: https://pypi.org/\n[rfc7950]: https://tools.ietf.org/html/rfc7950\n[rfc7951]: https://tools.ietf.org/html/rfc7951\n[jive]: https://www.jive.com/\n[google]: https://www.google.com/\n[godaddy]: https://www.godaddy.com/\n"
  },
  {
    "path": "README.rst",
    "content": "PyangBind\n=========\n\nPyangBind 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.\n\nThis module provides the supporting classes and functions that PyangBind modules utilise, particularly:\n\n* pyangbind.base.PybindBase - which is the parent class inherited by all container or module YANG objects.\n\n* pyangbind.pybindJSON - which containers wrapper functions which can be used to help with serialisation of YANG to JSON.\n\n* pyangbind.serialise.pybindJSONEncoder - a class that can be used as a custom encoder for the JSON module to serialise PyangBind class hierarchies to JSON.\n\n* 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.\n\n* 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.\n\n* pyangbind.yangtypes: The various functions which generate python types that are used to represent YANG types, and some helper methods.\n\n  - pyangbind.yangtypes.is_yang_list and is_yang_leaflist are self explainatory, but may be useful.\n\n  - pyangbind.yangtypes.safe_name is used throughout PyangBind to determine how to map YANG element names into Python attribute names safely.\n\n  - 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.\n\n  - 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).\n\n  - pyangbind.yangtypes.TypedListType - generates types which wrap a list to restrict the objects that it may contain.\n\n  - pyangbind.yangtypes.YANGListType - generates types which wrap a class representing a container, such that it acts as a YANG list.\n\n  - pyangbind.yangtypes.YANGBool - a boolean class.\n\n  - 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.\n\n  - pyangbind.yangtypes.ReferenceType - generates types which can use a pyangbind.xpathhelper.PybindXpathHelper instance to look up values - particularly to support leafrefs in YANG.\n\nUsage documentation for PyangBind itself can be found on GitHub: https://github.com/robshakir/pyangbind"
  },
  {
    "path": "docs/README.md",
    "content": "![#PyangBind](http://rob.sh/img/pyblogo_gh.png)\n\n# PyangBind Documentation\n\nIf you haven't already - it's best to start with the README.md file in the main repository. This provides a quick-start guide to PyangBind, including a walk-through of how to use PyangBind to manipulate an OpenConfig model. Reading this will give some context around where you might want to start reading in this documentation.\n\n## Documentation\n\nThe `docs` directory contains the following documents:\n\n  * [Errors](errors.md) -- explains the errors that PyangBind classes will throw.\n  * [Extension Methods](extmethods.md) -- usage and intention of the `extmethods` functionality in PyangBind\n  * [Generic Methods](generic_methods.md) -- the methods that the PyangBind meta-class defines, as well as methods that are added for YANG-specific types such as `container` and `list`.\n  * [RPC](rpc.md) -- explains PyangBind's support for the YANG `rpc` statement, and how one may use this functionality.\n  * [Serialisation and Deserialisation](serialisation.md) -- covers how PyangBind's `lib.serialise` and `lib.pybindJSON` classes can be used to output and load instances of data that have been created with a program using PyangBind's classes.\n  * [Usage](usage.md) -- documents the command-line switches that PyangBind uses.\n  * [XPathHelper](xpathhelper.md) -- provides information relating to PyangBind's optional `XPathHelper` classes which are used to resolve XPATH expressions and can be used to traverse a data tree consisting on multiple models.\n  * [YANG](yang.md) -- gives an overview of how various YANG language features are supported in PyangBind.\n\n## Examples\n\nIn order to allow new users to quickly see how PyangBind might work for them, some examples are included in this directory:\n\n  * [`example/oc-network-instance`](example/oc-network-instance) uses the OpenConfig `network-instance` module as an example and shows how one can build static routes using this module. The main directory's README.md provides a worked example of this.\n  * [`example/simple-rpc`](example/simple-rpc) shows how a YANG `rpc` definition can be manipulated when PyangBind classes are generated for it. The RPC document provides further explanation of this example.\n  * [`example/simple-serialise`](example/simple-serialise) shows how PyangBind's serialisation and deserialisation capabilities work. The serialisation document walks through this example.\n\nIn order to understand some of the internals of PyangBind a bit better, the `tests` directory may also be useful - this provides numerous test cases intended to ensure PyangBind keeps working the way one would expect, but can be a valuable source of pointers as to how things might work.\n\n## If you're stuck...\n\nPlease open an issue. The author (singular for the moment!) tries to help out where he can.\n"
  },
  {
    "path": "docs/errors.md",
    "content": "# Errors thrown by PyangBind\n\n**Note**: the functionality specified in this document is currently subject to some change. Feedback as to useful functionality is appreciated. Please open an issue.\n\nPyangBind re-uses Python error types where possible, particularly:\n\n * `KeyError` will be raised when an element does not have a particular key. The arguments to this error are a string.\n * `ValueError` is raised when the supplied data does not match the YANG data type. This value is only raised where the input cannot be cast to the type that is stored in the data. For example, a YANG integer type (`int8`, `int16`, ... etc.) will accept `True` as an input but store the value `1`. This intentional and aims to provide a balance between duck-typing in Python, and the more strict typing in YANG:\n \t* As of [docs@21/03/2016](https://github.com/robshakir/pyangbind/commit/0c28c057eeb7034c23c94a4e7ec09a9fd2ae00d0), the argument passed by PyangBind setters to ValueError is a dictionary - this can be accessed using code such as:\n \t\n \t```python\ntry:\n    pybindobj.value = \"anInvalidValue\"\nexcept ValueError as e:\n    # Check the args and types, just in case we have\n    # old bindings\n    if len(e.args) and isinstance(e.args[0], dict):\n        print \"Hit a PyangBind ValueError\"\n        for k, v in e.args[0].iteritems():\n            print \"%s->%s\" % (k, v)\n    else:\n        print unicode(e)\n \t\t\t\t\n \t```\n \t* The keys of the dictionary are:\n \t\t- `error-string`: a simple error string that provides some insight (but not all information) as to the type that was not matched. It will currently capture the original YANG type that was specified, but no additional restrictions.\n \t\t- `generated-type`: the dynamic class specification that PyangBind tried to generate for this type - this is often unwieldy, but tends to be useful for debugging.\n \t\t- `defined-type`: the simple defined type, resolved to module if it is not native that the leaf is. Again this does not include all information.\n * `AttributeError` will be raised when an invalid member of a YANG object is specified, or a method does not exist. In some cases, since PyangBind's meta-class defines some methods which are used to modify mutable objects in place (to capture changes) then `dir(...)` for the object may show methods that return `AttributeError` when they are passed to the super-class. "
  },
  {
    "path": "docs/example/.gitignore",
    "content": "binding.py\n"
  },
  {
    "path": "docs/example/oc-network-instance/generate_bindings.sh",
    "content": "#!/bin/bash\nSDIR=\"$(cd -P \"$(dirname \"$[0]\")\" && pwd)\"\nDDIR=$SDIR/models\nmkdir $DDIR\n\ngit clone https://github.com/openconfig/public $DDIR\nexport PYBINDPLUGIN=`/usr/bin/env python3 -c 'import pyangbind; import os; print (\"{}/plugin\".format(os.path.dirname(pyangbind.__file__)))'`\npyang --plugindir $PYBINDPLUGIN -f pybind -o $SDIR/binding.py -p $DDIR/ $DDIR/release/models/network-instance/openconfig-network-instance.yang\necho \"bindings.py successfully generated on current directory!\"\nrm -rf $DDIR\n"
  },
  {
    "path": "docs/example/oc-network-instance/json/oc-ni.json",
    "content": "{\n  \"network-instances\": {\n    \"network-instance\": {\n      \"a\": {\n        \"name\": \"a\",\n        \"protocols\": {\n          \"protocol\": {\n            \"STATIC DEFAULT\": {\n              \"identifier\": \"STATIC\",\n              \"name\": \"DEFAULT\",\n              \"static-routes\": {\n                \"static\": {\n                  \"192.0.2.1/32\": {\n                    \"prefix\": \"192.0.2.1/32\",\n                    \"config\": {\n                      \"set-tag\": 42\n                    },\n                    \"next-hops\": {\n                      \"next-hop\": {\n                        \"0\": {\n                          \"index\": \"0\",\n                          \"config\": {\n                            \"next-hop\": \"192.168.0.1\"\n                          }\n                        },\n                        \"1\": {\n                          \"index\": \"1\",\n                          \"config\": {\n                            \"next-hop\": \"10.0.0.1\"\n                          }\n                        }\n                      }\n                    }\n                  }\n                }\n              }\n            }\n          }\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "docs/example/oc-network-instance/json/oc-ni_ietf.json",
    "content": "{\n  \"openconfig-network-instance:static-routes\": {\n    \"static\": [\n      {\n        \"prefix\": \"192.0.2.1/32\",\n        \"config\": {\n          \"set-tag\": 42\n        },\n        \"next-hops\": {\n          \"next-hop\": [\n            {\n              \"index\": \"0\",\n              \"config\": {\n                \"next-hop\": \"192.168.0.1\"\n              }\n            },\n            {\n              \"index\": \"1\",\n              \"config\": {\n                \"next-hop\": \"10.0.0.1\"\n              }\n            }\n          ]\n        }\n      },\n      {\n        \"prefix\": \"192.0.2.2/32\",\n        \"config\": {\n          \"set-tag\": 256\n        },\n        \"next-hops\": {\n          \"next-hop\": [\n            {\n              \"index\": \"0\",\n              \"config\": {\n                \"next-hop\": \"192.168.0.1\"\n              }\n            },\n            {\n              \"index\": \"1\",\n              \"config\": {\n                \"next-hop\": \"10.0.0.1\"\n              }\n            }\n          ]\n        }\n      }\n    ]\n  }\n}\n"
  },
  {
    "path": "docs/example/oc-network-instance/static_route_example.py",
    "content": "#!/usr/bin/env python\n\nfrom __future__ import print_function, unicode_literals\nfrom binding import openconfig_network_instance\nimport pyangbind.lib.pybindJSON as pybindJSON\nimport os\n\n# Instantiate a copy of the pyangbind-kettle module and add a network instance.\nocni = openconfig_network_instance()\nocni.network_instances.network_instance.add(\"a\")\nocni.network_instances.network_instance[\"a\"].protocols.protocol.add(identifier=\"STATIC\", name=\"DEFAULT\")\n\n# Add an entry to the static route list\nrt = (\n    ocni.network_instances.network_instance[\"a\"]\n    .protocols.protocol[\"STATIC DEFAULT\"]\n    .static_routes.static.add(\"192.0.2.1/32\")\n)\n\n# Set a tag for the route\nrt.config.set_tag = 42\n\n# Retrieve the tag value\nprint(rt.config.set_tag)\n\n# Retrieve the tag value through the original object\nprint(\n    ocni.network_instances.network_instance[\"a\"]\n    .protocols.protocol[\"STATIC DEFAULT\"]\n    .static_routes.static[\"192.0.2.1/32\"]\n    .config.set_tag\n)\n\n# Use the get() method to see the content of the classes\n# using the filter=True keyword to get only elements that\n# are not empty or the default\nprint(\n    ocni.network_instances.network_instance[\"a\"]\n    .protocols.protocol[\"STATIC DEFAULT\"]\n    .static_routes.static[\"192.0.2.1/32\"]\n    .get(filter=True)\n)\n\n# Add a set of next_hops\nfor nhop in [(0, \"192.168.0.1\"), (1, \"10.0.0.1\")]:\n    nh = rt.next_hops.next_hop.add(nhop[0])\n    nh.config.next_hop = nhop[1]\n\n# Iterate through the next-hops added\nfor index, nh in rt.next_hops.next_hop.items():\n    print(\"%s: %s\" % (index, nh.config.next_hop))\n\n# Try and set an invalid tag type\ntry:\n    rt.config.set_tag = \"INVALID-TAG\"\nexcept ValueError as m:\n    print(\"Cannot set tag: %s\" % m)\n\n# Dump the entire instance as JSON in PyangBind format\nprint(pybindJSON.dumps(ocni, indent=2))\n\n# Dump the static routes instance as JSON in IETF format\nprint(\n    pybindJSON.dumps(\n        ocni.network_instances.network_instance[\"a\"].protocols.protocol[\"STATIC DEFAULT\"], mode=\"ietf\", indent=2\n    )\n)\n\n# Load the \"json/oc-ni.json\" file into a new instance of\n# \"openconfig_network_instance\". We import the module here, such that a new\n# instance of the class can be created by the deserialisation code.\n# Note that you may need to provide the absolute path to oc-ni.json.\nimport binding\n\nnew_ocni = pybindJSON.load(os.path.join(\"json\", \"oc-ni.json\"), binding, \"openconfig_network_instance\")\n\n# Manipulate the data loaded\nprint(\n    \"Current tag: %d\"\n    % new_ocni.network_instances.network_instance[\"a\"]\n    .protocols.protocol[\"STATIC DEFAULT\"]\n    .static_routes.static[\"192.0.2.1/32\"]\n    .config.set_tag\n)\nnew_ocni.network_instances.network_instance[\"a\"].protocols.protocol[\"STATIC DEFAULT\"].static_routes.static[\n    \"192.0.2.1/32\"\n].config.set_tag += 1\nprint(\n    \"New tag: %d\"\n    % new_ocni.network_instances.network_instance[\"a\"]\n    .protocols.protocol[\"STATIC DEFAULT\"]\n    .static_routes.static[\"192.0.2.1/32\"]\n    .config.set_tag\n)\n\n# Load JSON into an existing class structure\nfrom pyangbind.lib.serialise import pybindJSONDecoder\nimport json\n\n# Provide absolute path to oc-ni_ietf.json if needed.\nietf_json = json.load(open(os.path.join(\"json\", \"oc-ni_ietf.json\"), \"r\"))\npybindJSONDecoder.load_ietf_json(\n    ietf_json, None, None, obj=new_ocni.network_instances.network_instance[\"a\"].protocols.protocol[\"STATIC DEFAULT\"]\n)\n\n# Iterate through the classes - both the 192.0.2.1/32 prefix and 192.0.2.2/32\n# prefix are now in the objects\nfor prefix, route in (\n    new_ocni.network_instances.network_instance[\"a\"].protocols.protocol[\"STATIC DEFAULT\"].static_routes.static.items()\n):\n    print(\"Prefix: %s, tag: %d\" % (prefix, route.config.set_tag))\n"
  },
  {
    "path": "docs/example/simple-rpc/.gitignore",
    "content": "rbindings\nrbindings/*\n"
  },
  {
    "path": "docs/example/simple-rpc/generate_bindings.sh",
    "content": "#!/bin/bash\nSDIR=\"$(cd -P \"$(dirname \"${BASH_SOURCE[0]}\")\" && pwd)\"\n\nPYBINDPLUGIN=`/usr/bin/env python -c 'import pyangbind; import os; print \"%s/plugin\" % os.path.dirname(pyangbind.__file__)'`\npyang --plugindir $PYBINDPLUGIN -f pybind --build-rpcs --split-class-dir $SDIR/rbindings simple_rpc.yang\n\necho \"Bindings successfully generated!\"\n"
  },
  {
    "path": "docs/example/simple-rpc/json/rpc-output.json",
    "content": "{\n  \"simple_rpc:response-id\": 32,\n  \"elements\": [\n    { \"response-value\": \"return-one\" },\n    { \"response-value\": \"return-two\" }\n  ]\n}"
  },
  {
    "path": "docs/example/simple-rpc/simple-rpc.py",
    "content": "#!/usr/bin/env python\n\nfrom __future__ import print_function, unicode_literals\nfrom rbindings.simple_rpc_rpc.test.input import input\nfrom rbindings.simple_rpc_rpc.test.output import output\nfrom pyangbind.lib.serialise import pybindJSONDecoder\nfrom pyangbind.lib.pybindJSON import dumps\nimport pprint\nimport os\nimport json\n\npp = pprint.PrettyPrinter(indent=4)\n\n# Create an input instance\nrpc_input = input()\nrpc_input.input_container.argument_one = \"test_call\"\nrpc_input.input_container.argument_two = 32\nprint(dumps(rpc_input, mode=\"ietf\"))\n\n# Load an output from IETF JSON\nrpc_output = output()\nfn = os.path.join(\"json\", \"rpc-output.json\")\njson_obj = json.load(open(fn, \"r\"))\npybindJSONDecoder.load_ietf_json(json_obj, None, None, obj=rpc_output)\nprint(rpc_output.response_id)\n\npp.pprint(rpc_output.get(filter=True))\n"
  },
  {
    "path": "docs/example/simple-rpc/simple_rpc.yang",
    "content": "module simple_rpc {\n    yang-version \"1\";\n    namespace \"http://rob.sh/yang/examples/rpc\";\n    prefix \"srpc\";\n\n    rpc test {\n        input {\n            container input-container {\n                leaf argument-one {\n                    type string;\n                }\n\n                leaf argument-two {\n                    type uint8;\n                }\n            }\n        }\n\n        output {\n            leaf response-id {\n                type uint32;\n            }\n\n            list elements {\n                leaf response-value {\n                    type string;\n                }\n            }\n\n        }\n    }\n}\n"
  },
  {
    "path": "docs/example/simple-serialise/.gitignore",
    "content": "sbindings.*\n"
  },
  {
    "path": "docs/example/simple-serialise/generate_bindings.sh",
    "content": "#!/bin/bash\nSDIR=\"$(cd -P \"$(dirname \"${BASH_SOURCE[0]}\")\" && pwd)\"\n\nPYBINDPLUGIN=`/usr/bin/env python -c 'import pyangbind; import os; print \"%s/plugin\" % os.path.dirname(pyangbind.__file__)'`\npyang --plugindir $PYBINDPLUGIN -f pybind -o $SDIR/sbindings.py simple_serialise.yang\n\necho \"Bindings successfully generated!\"\n"
  },
  {
    "path": "docs/example/simple-serialise/json/simple-instance-additional.json",
    "content": "{\n    \"a-list\": {\n        \"entry-three\": {\n            \"the-key\": \"entry-three\"\n        }\n    }\n}\n"
  },
  {
    "path": "docs/example/simple-serialise/json/simple-instance-ietf.json",
    "content": "{\n    \"simple_serialise:a-container\": {\n        \"a-value\": 8\n    },\n    \"simple_serialise:a-list\": [\n        {\"the-key\": \"entry-one\"},\n        {\"the-key\": \"entry-two\"}\n    ]\n}\n"
  },
  {
    "path": "docs/example/simple-serialise/json/simple-instance.json",
    "content": "{\n    \"a-container\": {\n        \"a-value\": 8\n    },\n    \"a-list\": {\n        \"entry-one\": {\n            \"the-key\": \"entry-one\"\n        },\n        \"entry-two\": {\n            \"the-key\": \"entry-two\"\n        }\n    }\n}\n"
  },
  {
    "path": "docs/example/simple-serialise/simple-serialise.py",
    "content": "#!/usr/bin/env python\n\nfrom __future__ import unicode_literals, print_function\nimport pprint\nimport pyangbind.lib.pybindJSON as pbJ\nimport sbindings\nimport os\n\npp = pprint.PrettyPrinter(indent=4)\n\n# Load an instance from file using PyBind's native JSON format\nloaded_object = pbJ.load(os.path.join(\"json\", \"simple-instance.json\"), sbindings, \"simple_serialise\")\npp.pprint(loaded_object.get(filter=True))\n\n# Load an instance from a corresponding string using the native JSON format\nstring_to_load = open(os.path.join(\"json\", \"simple-instance.json\"), \"r\")\nstring_to_load = string_to_load.read().replace(\"\\n\", \"\")\nloaded_object_two = pbJ.loads(string_to_load, sbindings, \"simple_serialise\")\npp.pprint(loaded_object_two.get(filter=True))\n\n# Load an instance from an IETF-JSON file\nloaded_ietf_obj = pbJ.load_ietf(os.path.join(\"json\", \"simple-instance-ietf.json\"), sbindings, \"simple_serialise\")\npp.pprint(loaded_ietf_obj.get(filter=True))\n\n# Load an instance from an IETF-JSON string\nstring_to_load = open(os.path.join(\"json\", \"simple-instance-ietf.json\"), \"r\")\nstring_to_load = string_to_load.read().replace(\"\\n\", \"\")\n\nloaded_ietf_obj_two = pbJ.loads_ietf(string_to_load, sbindings, \"simple_serialise\")\npp.pprint(loaded_ietf_obj_two.get(filter=True))\n\n# Load into an existing instance\nfrom pyangbind.lib.serialise import pybindJSONDecoder\nimport json\n\n# Create a new instance\nexisting_instance = sbindings.simple_serialise()\nexisting_instance.a_list.add(\"entry-one\")\nexisting_instance.a_list.add(\"entry-two\")\n\nfn = os.path.join(\"json\", \"simple-instance-additional.json\")\ndata_to_load = json.load(open(fn, \"r\"))\npybindJSONDecoder.load_json(data_to_load, None, None, obj=existing_instance)\n\npp.pprint(existing_instance.a_list.keys())\npp.pprint(existing_instance.get(filter=True))\n\n# Serialise objects to JSON\nprint(pbJ.dumps(existing_instance, filter=True))\nprint(pbJ.dumps(existing_instance, filter=True, mode=\"ietf\"))\nprint(pbJ.dumps(existing_instance.a_list, select={\"the-key\": \"entry-one\"}))\nprint(pbJ.dumps(existing_instance.a_list, select={\"the-key\": \"entry-one\"}, mode=\"ietf\"))\n"
  },
  {
    "path": "docs/example/simple-serialise/simple_serialise.yang",
    "content": "module simple_serialise {\n    yang-version \"1\";\n    namespace \"http://rob.sh/yang/examples/ss\";\n    prefix \"ss\";\n\n    container a-container {\n        leaf a-value {\n            type int8;\n        }\n    }\n\n    list a-list {\n        key \"the-key\";\n\n        leaf the-key {\n            type string;\n        }\n    }\n}\n"
  },
  {
    "path": "docs/extmethods.md",
    "content": "# Extension Methods in PyangBind\n\nPyangBind is designed both as a means to generate data instances for YANG modules, but also as a software component that can be used in an  NMS implementation. To that end, there can be requirements to tie methods to particular data instances in the tree.\n\nThe extension methods (`extmethods`) functionality provides a means to tie arbitrary methods to a particular path in the tree.\n\n## Contents\n\n * [Initialisation of classes with `extmethods`](#initialisation)\n * [Example calls with `extmethods`](#example-calls)\n\n## Initialising Classes with `extmethods` <a name=\"initialisation\"></a>\n\nTo use extension methods, the PyangBind bindings must be generated with `--use-extmethods`. This ensures that the `extmethods` dictionary is propagated from parent to child objects as they are instantiated.\n\nThe `extmethods` dictionary is of the form:\n\n```python\n  {\n    \"/path/to/object/one\": <Class Instance>,\n    \"/path/to/object/two\": <Class instance>\n  }\n```\n\nWhere `/path/to/object/one` is defined as the XPATH to the object that the method is to be bound to *without* any filtering attributes. That is to say, for `/bgp/global/config/as` the path specified is simply `/bgp/global/config/as` whereas for `/bgp/neighbors/neighbor[peer-addr='192.0.2.1']/config/peer-as` then the path specified is `/bgp/neighbors/neighbor/config/peer-as`. It is not possible to bind an extension method to a single instance.\n\nEach object, as it is instantiated, then consults the `extmethods` dictionary, if it finds an entry which corresponds to its exact path, it inherits all methods of the class instance provided - and will proxy any calls to itself to that class. The names of the methods are prefixed by an underscore in order to avoid collisions between actual data element names and method names.\n\n## Example Calls with `extmethods` <a name=\"example-calls\"></a>\n\nIf one has the following class definition:\n\n```python\n\nfrom openconfig import openconfig_bgp\n\nclass BgpNeighborHelper(object):\n  def soft_reset(self, *args, **kwargs):\n    # Do a soft reset of the neighbor\n    pass\n\n  def hard_reset(self, *args, **kwargs):\n    # Do a hard reset of the neighbor\n    pass\n```\n\nA set of PyangBind classes (e.g., OpenConfig BGP) can be initialised with an `extmethods` dictionary that provides a mapping between an instance of the `BGPNeighborHelper` class and an XPATH expression. For example, between the `config/enabled` leaf of each BGP neighbor and this class:\n\n```python\nbgp_helper =  BGPNeighborHelper()\nextmethods = {\n      '/bgp/neighbors/neighbor/neighbor/config/enabled': bgp_helper\n}\n\nocbgp = openconfig_bgp(extmethods=extmethods)\n```\n\nEach entry within the `/bgp/neighbors/neighbor` list would have methods named `soft_reset` and `hard_reset` bound to their `config/enabled` leaf.\n\ni.e., a hard reset or soft reset could be initiated by calling:\n\n```python\nocbgp.bgp.neighbors.neighbor[\"192.0.2.1\"].config.enabled._hard_reset()\nocbgp.bgp.neighbors.neighbor[\"192.0.2.1\"].config.enabled._soft_reset()\n```\n\nWhen this call is made, the instance of the `BgpNeighborHelper` class named `bgp_helper` (which was supplied in the `extmethods` dictionary) will receive a call to the `soft_reset` or `hard_reset` method).\n\nIn addition to the arguments and keyword arguments that are supplied to the function (which are directly proxied through), two additional `kwargs` are added:\n\n * `caller` - this is a list which provides the components of the path of the actual object that the method was called against. For example in the above case this would correspond to `/bgp/neighbors/neighbor[peer-addr=\"192.0.2.1\"].config.enabled` - and hence be `['bgp', 'neighbors', 'neighbor[peer-addr='192.0.2.1'], 'config', 'enabled']`. This allows disambiguation of calls that may come from multiple sources.\n * `path_helper` which provides a reference to the `path_helper` class that is being used by the classes. This can allow the `extmethod` to retrieve the data instance that called it if required. \n"
  },
  {
    "path": "docs/generic_methods.md",
    "content": "# Generic Methods Provided through PyangBind\n\nPyangBind's `YANGDynClass` function generates meta-classes around the class that is defined as the `base_type`/`base` when the class is generated. The wrapper that is provided gives a set of functions and variables that allow YANG-specific information to be stored alongside the value of the class.\n\nSome of these methods are generically useful when handling the classes, as well as internally to PyangBind.\n\nIn general, methods are defined as `_<methodname>` such that clashes with the elements within YANG containers can be avoided - although for historical reasons, in some cases the `_` is omitted.\n\n## Contents\n\n * [YANGDynClass Methods](#ydcmethods) - generic to all PyangBind wrapped objects other than those corresponding to YANG modules.\n * [YANG Container Methods](#containermethods) - methods defined for PyangBind objects corresponding to YANG `container` items.\n * [YANG List Methods](#listmethods) - methods defined for PyangBind objects corresponding to YANG `list` items.\n\n\n## Methods/Variables Defined in YANGDynClass <a name=\"#ydcmethods\"></a>\n\n### `default()`\n\nWhere a YANG type has a default value specified, the default method returns this value. The actual class' value is set to the null value of the base class (e.g., `unicode` objets return `''`, `int` objects return 0), whereas the default value is stored in `_default`. If there is no default defined, then `_default` is set to False.\n\n### `_changed()`\n\nReturns `True` when the class (or a child of the class if it represents a container) has been set. This allows subsets of the data tree that have been manipulated to be retrieved as opposed to all elements.\n\n### `yang_name()`\n\nReturns the name of the data element as defined in the YANG module rather than the `safe_name` returned value.\n\n### `_add_metadata()` & `_metadata`\n\nThe `_metadata` element is a dictionary which can stores any meta-data that was provided as part of the data instance. For example, the Juniper example JSON instance for BGP global configuration may be akin to:\n\n```python\n\"config\" : {\n   \"@router-id\" : {\n      \"inactive\" : true\n   },\n   \"router-id\" : \"10.10.10.10\"\n}\n```\n\nIn this case, the `router_id` member of the `config` class will have a dictionary of the form `{\"inactive\": True}` stored with it.\n\nThe `_add_metadata()` method allows new metadata to be added. The arguments expected are a key, followed by a metadata value, e.g.:\n\n```python\nconfig.router_id._add_metadata(\"inactive\", True)\n```\n\nwill add the metadata shown above.\n\n### `_register_path()` & `_path()`\n\nBoth of these methods currently return the same data: a list defining the elements of the path to the data element in the tree. For example, the entry for `/bgp/neighbors/neighbor[peer-address='192.0.2.1']/config/peer-as` will return `['bgp', 'neighbors', 'neighbor[peer-address='192.0.2.1']', 'config', 'peer-as']`.\n\n### `_yang_path()`\n\nReturns the path to the YANG object as a string.\n\n### `_namespace`\n\nReturns the namespace (from the `namespace` statement) of the YANG module that defined the element.\n\n### `_defining_module`\n\nReturns the module name from the `module` statement of the YANG module that defined the element. This is used in a number of cases within IETF JSON serialisation/deserialisation.\n\n### `_choice`\n\nIf the element is defined within a `choice` statement, then this value carries the name of the `case` that it is a part of. This is used to call `_unset_X` where X is the element's `safe_name` when a member of another `case` is set (since two `case` elements of a choice cannot co-exist).\n\n### `_parent`\n\nReturns a reference to the element's parent class - for example, if one has a list entry at `/bgp/neighbors/neighbor[peer-address='2001:DB8::1']/config/peer-as` then `_parent` of the `peer-as` object refers to the `config` object, and the corresponding `_parent` of the `config` object refers to the list entry for the neighbor.\n\nThis is generally useful in the cases where one has a `leafref` value that refers to the key of a list and the application requires the list entry itself (i.e., in this case one can do: `leafref_leaf._parent` to get to the list entry of a list).\n\n### `_is_keyval`\n\nSet to `True` if this value is the key of a list.\n\n### `_is_config`\n\nSet to True if the node is configurable within the YANG schema - reflecting the YANG `config` statement.\n\n## YANG Container (`PybindBase`) defined Methods <a name=\"containermethods\"></a>\n\n### `elements()`\n\nReturns a list of the names of the elements of the container. This can be used when iterating, although `for child in container` can also be used.\n\n### `get(filter=<bool>)`\n\nReturns a nested set of dictionaries that represent the current container. The filter argument provides a means to get only those elements that have changed during the current manipulation of the data tree (including being deserialised from a data instance):\n\n```python\n# Setup of data omitted\n>> import pprint\n>> pp = pprint.PrettyPrinter(indent=4)\n>> pp.pprint(r.tables.get(filter=True))\n{   'table': {   'AGGREGATE': {   'config': {   'table-name': u'AGGREGATE'},\n                                  'table-name': u'AGGREGATE'},\n                 'BGP': {   'config': {   'table-name': u'BGP'},\n                            'table-name': u'BGP'},\n                 'STATIC': {   'config': {   'table-name': u'STATIC'},\n                               'table-name': u'STATIC'}}}\n```\n\nThis may be used as an alternative to serialising/deserialising instances especially for debugging. In general, the serailisation classes will use this format as a intermediary to be able to retrieve data from the classes.\n\n## YANG List Methods <a name=\"listmethods\"></a>\n\nPyangBind provides two special methods for YANG `list` objects:\n\n### `add(<keyspec>)`\n\nAdds a new entry to the list. In the case that the list is a keyed list - then the value returned is a reference to the newly created list entry. In the case that the list is not keyed, the value returned is the key value (a UUID) that has been defined internally by PyangBind.\n\nThe key specification can be of three forms:\n  * A value representing the key - in the case of a list with a single key, then the entire value is used as a key.\n  * A space-separated string representing multiple keys. In this case, the key ordering is as specified in the `key` leaf in the YANG module, and the string is split at each space. For example, a list two with a key specification of `key \"srcip index\"` supplies with `.add(\"192.0.2.1 1\")` would set `srcip=192.0.2.1` and `index=1`. The key values will cast the split string into the relevant type for storage in the corresponding list entry.\n  * A set of keyword arguments for each key. For example, if the same list as above were called with `.add(index=1, srcip=\"192.0.2.1\")` then the keyword arguments for each key would be extracted. In this case, order does not matter.\n\n### `delete(<keyspec>)`\n\nRemoves the key value specified by `keyspec` from the list. The logic for the format of `keyspec` is the same as `add`.\n\n### `_new_item()`\n\nIn some cases it is preferable to create a valid object outside of the context of it being added to the list (for example, in cases where there is some action performed around the `add()` call by the program consuming PyangBind's classes). To this end, the `_new_item()` method (called as `yang_list._new_item())` returns an empty instance of a member of the list, which can be populated.\n\n### `append(object)`\n\nWhere an item has been created without being added to the list, it can be added using the `append()` function. The object supplied as the `obj` argument is used to extract the list key which is to be used for the item. As per a standard Python `list` item's `append()` method, there is no return from this function.\n\n"
  },
  {
    "path": "docs/rpc.md",
    "content": "# RPCs in PyangBind\n\nPyangBind generates bindings for RPCs that are specified within a YANG module. The assumption is made that an RPC is not bound to any particular location within the data tree (the YANG 1.1 `action` statement is intended to meet this requirement). To this end, bindings are generated within a module named `<yang module name>_rpcs`.\n\nAll RPC bindings have the property `register_paths` set to `False`. This results in them never using a `path_helper` object that is handed to them for `register()` or `unregister()` purposes. A `path_helper` class will still be used to resolve `leafref` values (and other XPATH expressions) if required.\n\nAn class generated for an RPC has two member containers - `input` and `output` as per the specification provided in RFC6020. The corresponding data definitions are within these two elements (which act as per YANG containers).\n\n## Contents\n\n* [Example RPC](#examplerpc)  \n\t* [Generating an RPC `input`](#exinput)\n\t* [Parsing an RPC `output`](#exoutput)\n\t* [Example Code](#excode)\n\n## Example RPC <a name=\"examplerpc\"></a>\n\nAn example simple RPC could be defined as:\n\n```yang\n    rpc test {\n        input {\n            container input-container {\n                leaf argument-one {\n                    type string;\n                }\n\n                leaf argument-two {\n                    type uint8;\n                }\n            }\n        }\n\n        output {\n            leaf response-id {\n                type uint32;\n            }\n\n            list elements {\n                leaf response-value {\n                    type string;\n                }\n            }\n\n        }\n    }\n```\n\nIn this definition, the RPC `test` has an input that takes a `container` with two arguments (`argument-one` and `argument-two`) specified within it. It outputs an object that has a single `response-id` and a list of `elements` wtihin the reply. This list is not keyed (RPC outputs are defined to be `config false`).\n\n### Generating an RPC Input <a name=\"exinput\"></a>\nTo generate an input for an RPC, the `input` container can be directly imported. If the example module above is generated with `--split-class-dir` into a module directory named `rbindings` then, for example:\n\n```python\nfrom rbindings.simple_rpc_rpc.test.input import input\n```\n\nThe `input` class can be instantiated and populated as per any other PyangBind class that represents a container:\n\n```python\nrpc_input = input()\nrpc_input.input_container.argument_one = \"test_call\"\nrpc_input.input_container.argument_two = 32\n```\n\nThe object generated can be serialised to IETF JSON as per any other container using `dumps` from `pyangbind.lib.pybindJSON`:\n\n```python\n>> print(dumps(rpc_input, mode=\"ietf\"))\n{\n    \"simple_rpc:input-container\": {\n        \"argument-two\": 32, \n        \"argument-one\": \"test_call\"\n    }\n}\n```\n\n### Parsing an RPC Output <a name=\"exoutput\"></a>\n\nIn a similar manner, an RPC output can be read back into the corresponding `output` class using the standard deserialisation functionality in PyangBind. For example:\n\n```python\nfrom rbindings.simple_rpc_rpc.test.output import output\nrpc_output = output()\nfn = os.path.join(\"json\", \"rpc-output.json\")\njson_obj = json.load(open(fn, 'r'))\npybindJSONDecoder.load_ietf_json(json_obj, None, None, obj=rpc_output)\n```\n\nThe output class can then be manipulated and/or read in Python:\n\n```python\n>>> print(rpc_output.response_id)\n32\n```\n\n### Example Code\n\nThe RPC example here can be found in `docs/example/simple-rpc`.\n\n "
  },
  {
    "path": "docs/serialisation.md",
    "content": "# Serialisation and Deserialisation of YANG-modelled Data\n\n\nPyangBind provides a set of helper classes which allow data to be loaded from, or serialised to a loaded data format. At the time of writing, the supported formats are:\n\n * XML - defined in [RFC 7950](https://tools.ietf.org/html/rfc7950)\n * IETF JSON - defined in `draft-ietf-netmod-yang-json-08`.\n * OpenConfig/PyangBind JSON - which does not currently have a published specification.\n\nIn the future, it is expected that an XML serialisation module may be required, given the current bias of devices towards this serialisation format.\n\n## Contents\n * [Loading from JSON - Entire Module](#load-json-module)\n   - [Load Functions](#load-functions)\n     - [`loads` and `loads_ietf`](#json-loads)\n     - [`load` and `load_ietf`](#json-load)\n   - [Example Loading (Deserialisation)](#example-load)\n     - [Loading OpenConfig/PyangBind JSON-encoded Data](#example-load-oc)\n     - [Loading IETF-JSON encoded Data](#example-load-ietf)\n * [Loading Data into an Existing Instance](#load-json-existing)\n   - [Example of Loading to Existing Instances](#load-json-existing-example)\n * [Serialising Data into XML](#serialising-xml)\n * [Serialising Data into JSON](#serialising-json)\n   - [Example Serialisation](#example-serialisation)\n * [Example Code](#example-code)\n\n## Loading from JSON - Entire Module <a name=\"load-json-module\"></a>\n\nThe module `pyangbind.lib.pyangbindJSON` provides a wrapper around the encoder and decoder classes that are defined in `pyangbind.lib.serialise`. These methods are heavily biased towards loading an entire module into a new instance of the data tree.\n\nThe functions `loads`, `load`, `loads_ietf` and `load_ietf` are analagous to the Python `json` module's functions. The `loads.*` functions load from a string which is expected to be valid JSON, whereas the `load.*` functions load from a file name that is specified to the modules.\n\n### Load Functions <a name=\"load-json-module\"></a>\n\n#### `loads(data, python_module, class_name)` and `loads_ietf(data, python_module, class_name)` <a name=\"json-loads\"></a>\n\nThe arguments to the `loads` functions are expected to be:\n * `data` - a JSON-encoded string that can be loaded using Python's `json.load()` function.\n * `python_module` - the module within which the class that is to be instantiated is defined. In the case that `-o <filename>` is used, then this is `filename` as this code is a Python module that can be loaded with `import`. In the case that `--split-class-dir <directory>` has been used then it is `directory`.\n  * `class_name` - the name of the Python class that is found within `module`. This is the safe name (i.e., Python-safe - using no reserved Python keywords and substituting hyphens for underscores) of the YANG module that has been compiled - e.g., `openconfig_bgp`.\n\nThese functions return a PyangBind class instance with the data from `data` loaded into it.\n\n#### `load(filename, python_module, class_name)` and `load_ietf(filename, python_module, class_name)` <a name=\"json-load\"></a>\n\nThe arguments to the `load` and `load_ietf` functions are identitical to those of the `loads` functions, other than the `filename` argument is a path to a file that can be loaded using `open(filename, 'r')`.\n\nThese functions return a PyangBind class instance with the data from `filename` loaded.\n\n### Example Loading (Deserialisation) <a name=\"example-load\"></a>\n\nWith a simple module - such as the following:\n\n```yang\nmodule simple_serialise {\n    yang-version \"1\";\n    namespace \"http://rob.sh/yang/test/ss\";\n    prefix \"ss\";\n\n    container a-container {\n        leaf a-value {\n            type int8;\n        }\n    }\n\n    list a-list {\n        key \"the-key\";\n\n        leaf the-key {\n            type string;\n        }\n    }\n}\n```\n\nAnd bindings generated using:\n\n```\n$ pyang --plugindir $PYBINDPLUGIN -f pybind -o sbindings.py simple_serialise.yang\n```\n\n#### Loading OpenConfig/PyangBind JSON-encoded Data <a name=\"example-load-oc\"></a>\n\nAn example instance of data for the above module encoded as PyangBind JSON looks like the following:\n\n```json\n{\n    \"a-container\": {\n        \"a-value\": 8\n    },\n    \"a-list\": {\n        \"entry-one\": {\n            \"the-key\": \"entry-one\"\n        },\n        \"entry-two\": {\n            \"the-key\": \"entry-two\"\n        }\n    }\n}\n```\n\nTo load this, using `load` and `loads` the following code can be used:\n\n```python\n#!/usr/bin/env python\n\nimport pprint\nimport pyangbind.lib.pybindJSON as pbJ\nimport sbindings\n\npp = pprint.PrettyPrinter(indent=4)\n\nloaded_object = pbJ.load(\"json/simple-instance.json\", sbindings, \"simple_serialise\")\npp.pprint(loaded_object.get(filter=True))\n\nstring_to_load = open('json/simple-instance.json', 'r').read().replace('\\n', '')\nloaded_object_two = pbJ.loads(string_to_load, sbindings, \"simple_serialise\")\npp.pprint(loaded_object_two.get(filter=True))\n```\n\nIn both cases, the `python_module` name used is the name of the bindings file generated (`sbindings`) and the modulename is as specified in the YANG module (i.e., `simple_serialise`).\n\nBoth loading methods will return the same output:\n\n``python\n{   'a-container': {   'a-value': 8},\n    'a-list': {   u'entry-one': {   'the-key': u'entry-one'},\n                  u'entry-two': {   'the-key': u'entry-two'}}}\n```\n\n#### Loading IETF JSON-encoded Data <a name=\"example-load-ietf\"></a>\n\nLoading IETF encoded data is almost identical, other than using the `loads_ietf` and `load_ietf` functions in place of `loads` and `load` respectively.\n\nThe corresponding example IETF-encoded JSON object for the above data is:\n\n```json\n{\n    \"simple_serialise:a-container\": {\n        \"a-value\": 8\n    },\n    \"simple_serialise:a-list\": [\n        {\"the-key\": \"entry-one\"},\n        {\"the-key\": \"entry-two\"}\n    ]\n}\n```\n\nThis can be loaded in the same way using the following Python:\n\n```python\n# Load an instance from an IETF-JSON file\nloaded_ietf_obj = pbJ.load_ietf(\"simple-instance-ietf.json\", sbindings,\n                                  \"simple_serialise\")\npp.pprint(loaded_ietf_obj.get(filter=True))\n\n# Load an instance from an IETF-JSON string\nstring_to_load = open('json/simple-instance-ietf.json', 'r').read().replace('\\n',\n                                                                         '')\nloaded_ietf_obj_two = pbJ.loads_ietf(string_to_load, sbindings,\n                                                \"simple_serialise\")\npp.pprint(loaded_ietf_obj_two.get(filter=True))\n```\n\nAgain, the data loaded is idential to that shown above.\n\n## Loading Data into an Existing Instance <a name=\"load-json-existing\"></a>\n\nIn a number of cases, it is desirable to load data from a serialised JSON input into an existing set of PyangBind classes - such as when accepting input from an external API. Doing this requires direct access of the `pyangbind.lib.serialise` classes, rather than the `pyangbind.lib.pybindJSON` helper functions. The relevant functions are those within `pybindJSONDecoder` - particularly `load_ietf_json` and `load_json`.\n\nIn order to use these functions (which are generally directly-called by the corresponding `pyangbind.lib.pybindJSON` functions - then there is a requirement to specify an already existing object, and skip the class instantiation stage of the loading functions.\n\nWhen calling the load functions, the following format is expected to load into an existing object:\n\n```python\nload_json(input_data, None, None, obj=existing_object, path_helper=path_helper,\n                extmethods=extmethods, overwrite=overwrite):\nload_ietf_json(input_data, None, None, obj=existing_object, path_helper=path_helper,\n\t\t\t\t\textmethods=extmethods, overwrite=overwrite)\n```\n\nWhere:\n  * `input_data` is a iterable object that corresponds to loaded JSON data.\n  * `existing_object` is the object that should be loaded into - i.e., an instantiated set of PyangBind classes.\n  * `path_helper`, `extmethods` - are the corresponding XPathHelper and extension methods that are to be used if required. In the case that these do not differ from the parent, they will be inherited.\n  * `overwrite` determines whether the existing instance's data should be overwritten by the loaded data.\n\n#### Example of Loading to Existing Instances <a name=\"load-json-existing-example\"></a>\n\nUsing the same module as above, with the loaded instance in question (defining `/a-list[the-key='entry-one']` and `/a-list[the-key='entry-two']` then data can be loaded using the following `load_json` call:\n\n```python\ndata_to_load = json.load(open('json/simple-instance-additional.json','r'))\npybindJSONDecoder.load_json(data_to_load, None, None, obj=existing_instance)\n```\n\nThe `existing_instance` object is modified in-place, and hence the list acquires the additional `entry-three` data defined in the `simple-instance-additional.json` file:\n\n```python\npp.pprint(existing_instance.a_list.keys())\n# Outputs:\n# [u'entry-two', u'entry-three', u'entry-one']\n```\n\n## Serialising Data into XML <a name=\"serialising-xml\"></a>\n\nIn order to serialise a PyangBind class instance JSON, the `pybindIETFXMLEncoder` class defined in `pyangbind.lib.serialise` can be used. There are two relevant class methods `pybindIETFXMLEncoder.serialise` and `pybindIETFXMLEncoder.encode`,\nwhich emit a string or an [`lxml.objectify`](https://lxml.de/objectify.html) instance, respectively.\n\n ```\n pybindIETFXMLEncoder.serialise(obj, filter=<bool>, pretty_print=<bool>)\n ```\n\n  * `obj` - which is a PyangBind class instance that is to be dumped. It is expected to have a `get` method, hence be a list or a container.\n  * `filter` - analagous to the `filter` argument to a PyangBind class' `get` method (see the documentation relating to generic methods), which determines whether the entire tree, or just the changed elements are to be dumped.\n  * `pretty_print` - determines whether to apply pretty-printing to the emitted XML string (e.g. newlines and 2-space indentation).\n\n ```\n pybindIETFXMLEncoder.encode(obj, filter=<bool>, pretty_print=<bool>)\n ```\n\n  * The `obj` and `filter` arguments are as per `pybindIETFXMLEncoder.serialise`.\n\n## Serialising Data into JSON <a name=\"serialising-json\"></a>\n\nIn order to serialise a PyangBind class instance into JSON, the `dump`, `dumps` functions defined in `pyangbind.lib.pybindJSON` are used. These functions take a `mode` keyword argument which determines whether they dump IETF-specified JSON or PyangBind JSON. \n\n ```\n dump(obj, filename, indent=<int>, filter=<bool>, skip_subtrees=<list>, mode=\"default\")\n ```\n  \n  * `obj` - which is a PyangBind class instance that is to be dumped. It is expected to have a `get` method, hence be a list or a container.\n  * `filename` - the file to which the JSON should be written.\n  * `indent` - the number of spaces to use to indent the JSON.\n  * `filter` - analagous to the `filter` argument to a PyangBind class' `get` method (see the documentation relating to generic methods), which determines whether the entire tree, or just the changed elements are to be dumped.\n  * `skip_subtrees` - a list of paths (absolute rather than relative) that should be pruned from the output JSON. This is useful if multiple output files are used to save data instances.\n  * `mode` - a string specifying the JSON encoding to be output -- currently either \"default\" or \"ietf\".\n\n ```\n dumps(obj, indent=4, filter=True, skip_subtrees=[], select=False, mode=\"default\")\n ```\n * The `obj`, `indent`, `filter`, `skip_subtrees` and `mode` arguments of dumps are as per `dump`.\n * `select` - when provided is expected to be a dictionary of the form `{ \"element_name\": value }`. When this is specified only elements where `obj.element_name == value` are output. This is useful when using query parameters to select subsets of an object, or list.\n\n### Example Serialisation <a name=\"example-serialisation\"></a>\n\nIn order to serialise an instance of the `simple_serialise` module used above - the following call is used:\n\n```python\npyangbindJSON.dumps(existing_instance, filter=True)\n```\n\nThis outputs the entire module as JSON:\n\n```json\n{\n    \"a-list\": {\n        \"entry-two\": {\n            \"the-key\": \"entry-two\"\n        }, \n        \"entry-three\": {\n            \"the-key\": \"entry-three\"\n        }, \n        \"entry-one\": {\n            \"the-key\": \"entry-one\"\n        }\n    }\n}\n```\n\nThe JSON format can be switched to IETF-encoded JSON by using the `mode=\"ietf\"` argument to dumps.\n\nTo select an entry from the list where the `the-key` leaf is equal to \"entry-one\" (although this is a gratiutous example), the `select` dictionary can be used:\n\n```python\npbJ.dumps(existing_instance.a_list, select={'the-key': 'entry-one'}, mode=\"ietf\")\n```\n\nThis outputs only the `entry-one` output of the list being shown:\n\n```json\n[\n    {\n        \"simple_serialise:the-key\": \"entry-one\"\n    }\n]\n```\n\n## Example Code <a name=\"example-code\"></a>\n\nThe example used throughout this document is included under `docs/example/simple-serialise`.\n\n"
  },
  {
    "path": "docs/usage.md",
    "content": "# PyangBind CLI usage\n\nPyangBind adds a number of command-line options to Pyang:\n\n * [Output options](#output-options) - `-o`, `--split-class-dir`\n * [XPathHelper options](#xpathhelper) - `--use-xpathhelper`\n * [Extensions options](#extensions) - `--interesting-extension`\n * [RPC options](#rpcs) -- `--build-rpcs`\n * [Extended Methods](#extmethods) -- `--use-extmethods`\n * [YANG Module Arguments](#yangmods)\n\n## Output Options <a name=\"output-options\"></a>\n\nPyangBind has three output modes:\n  * A file for all generated classes:\n    * Written to `stdout`\n    * Written to a single .py file\n  * A Python module hierarchy.\n\n### stdout\n\nWhen no options are specified, PyangBind will write a single file to `stdout`, this is considered to be self-contained and can be redirected to a particular location by the shell.\n\n### -o <filename>\n\nWhen `-o <filename>` is specified (a standard Pyang option), then the single file output is redirected to the filename specified. Within this single file, to ensure that class names remain unique then the class naming used for all non-top-level classes is of the form `yc_<lastcontainername>_<modulename>__<object path, replacing \"/\" with \"_\">`. Clearly, this results in relatively complex class names such as `yc_config_openconfig_bgp__bgp_global_config` corresponding to the `/bgp/global/config` container in the OpenConfig BGP module.\n\n### --split-class-dir <directory>\n\nWhen `--split-class-dir <directory>` is specified then PyangBind will create a Python module hierarchy in `directory`. This will result in each level of hierarchy in the YANG module becoming its own sub-module.\n\nFor example, the OpenConfig BGP module has the following hierarchy:\n\n```\nmodule: openconfig-bgp\n   +--rw bgp!\n      +--rw global\n      |  +--rw config\n      |  |  +--rw as           inet:as-number\n      |  |  +--rw router-id?   inet:ipv4-address\n      |  +--ro state\n      |  |  +--ro as                inet:as-number\n      |  |  +--ro router-id?        inet:ipv4-address\n      |  |  +--ro total-paths?      uint32\n      |  |  +--ro total-prefixes?   uint32\n```\n\nWhen `--split-class-dir openconfig` is specified, then the class corresponding to the `bgp` container will be output in a Python module named `openconfig`. This module can then be imported via:\n\n```python\nfrom openconfig import bgp\n\nocbgp = bgp()\n```\n\nAt deeper levels of hierarchy a class is output within a sub-module of the same name, such that to import the BGP `global` container class, then the `global_` module is imported, with the `global_` class within it being instantiated:\n\n```python\nfrom openconfig.bgp import global_    # global is a reserved word\nfrom openconfig.bgp.global_ import config\n\nbgp_global = global_.global_()\nbgp_global_config = config.config()\n```\n\n## XPathHelper Options <a name=\"xpathhelper\"></a>\n\nIf `--use-xpathhelper` is _not_ specified, then all XPATH references throughout the classes generated will act as strings - such that any element that relies in XPATH (`when`/`leaf-ref` statements etc.) will simply take on any value that they are set to.\n\nWhen` --use-xpathhelper` is specified, then references to the `path_helper` object that is supplied at the time of class instantiation will be passed to all of the classes children. This object is then used to provide lookup capabilities for XPATH expressions. This behaviour is further documented in (the XPathHelper documentation](xpathhelper.md).\n\n## Extension Options\n\nWhen `--interesting-extension <modulename>` is specified then PyangBind will look for extensions from the module name provided that are included in the YANG module. These extensions are then placed in a dictionary that is accessible through each class' `_extensions()` method.\n\nFor example, with the following YANG:\n\n```yang\nimport example-extension { prefix \"egx\"; }\n\nleaf description {\n  egx:descr-flag \"d\";\n  egx:descr-order 100;\n  type string;\n  description\n    \"A human-readable text description\";\n}\n```\n\nIf `--interesting-extension example-extension` is specified, then the `description` object's `_extensions()` method will return a dictionary:\n\n```python\n>>> print(cls.description._extensions())\n{u'example-extensions': {u'descr-flag': u'd', u'descr-order': u'100'}}\n```\n\nThese extensions can then be consumed by the program manipulating the classes.\n\n## RPC Options <a name=\"rpcs\"></a>\n\nBy default, PyangBind will ignore all RPC definitions within a YANG file. When `--build-rpcs` is specified, then each RPC, with its corresponding `input` and `output` containers will be generated into a class which corresponds to `<modulename>_rpc` at the root of the data tree.\n\nSee the [RPC documentation](rpc.md) for more detail as to the usage of generated RPC classes.\n\n## Extended Methods <a name=\"extmethods\"></a>\n\nWhen the `--use-extmethods` command-line option is specified, PyangBind will propagate the dictionary that is provided as the option `extmethods=` argument during class initialisation to the children objects. If this option is not specified, this option is always `False`.\n\nSee the [Extension Methods](extmethods.md) documentation for detail of this functionality.\n\n## YANG Module Arguments <a name=\"yangmods\"></a>\n\nAs per Pyang - when using the PyangBind plugin, the YANG modules to be compiled are specified on the command line, along with `-p <path>` to specify where Pyang should look for other modules that are included. However, unlike Pyang, PyangBind needs to be able to resolve all base typedefs - in some cases this may involve specifying additional modules to be compiled if they included `identity` or `typedef` statements. In the case that a definition cannot be resolved, PyangBind will not generate bindings and will return a list of the known definitions at the time of the error. The current error language is not particularly user friendly - if PyangBind is unable to resolve a type definition or identity statement, please open a bug with the YANG modules being used such that this can be examined.\n"
  },
  {
    "path": "docs/xpathhelper.md",
    "content": "# XPATH and Data Tree Structure in PyangBind\n\n * [Overview](#overview)\n * [PyangBind's XPathHelper Classes](#xpathhelpercls)\n * [Usage of YANGPathHelper](#yangpathhelper)\n\n## Overview <a name=\"overview\"></a>\n\nYANG data models describe a tree structure - where there is a single root for all modules, and each module creates schema nodes (e.g., containers or leaves) at that root. Essentially (based on YANG's historical ties to XML) this tree structure is conceptually an XML document - and hence XPATH expressions are used to provide references between different elements in the tree.\n\nFor example:\n\n```yang\n\nleaf reference {\n  type leafref {\n    path \"/path/to/another/node\";\n  }\n}\n\naugment \"/bgp\" {\n  uses some-new-grouping;\n}\n```\n\nBoth the augment and leafref statements here utilise XPATH expressions to refer to a remote node. When the value of a leafref is set then there is a requirement to check the value it is set to against the path that it refers to.\n\n## PyangBind's XPathHelper Classes <a name=\"xpathhelpercls\"></a>\n\nTo allow such references to be looked up, all PyangBind classes take an argument of `path_helper` which points to an object that they can use to resolve an XPATH expression into the data instances that that path refers to.\n\nGenerically, this helper class is described in `pyangbind.lib.xpathhelper` as the `PyangbindXpathHelper` class. This is a skeleton class (or interface, essentially) - that specifies the methods that PyangBind classes expect of this helper module. These are:\n\n* `register(self, path, object_ptr, caller=False)` - this method is called when a PyangBind object is created such that a pointer between the `path` argument and the object referred to by `object_ptr` can be maintained by the XPathHelper class. The `caller` argument specifies the path to the object that is making the `register()` call - with the logic that the `path` argument may be relative in some cases.\n* `unregister(self, path, caller=False)` - this function is the partner to `register()` and is called when a PyangBind object is removed to remove the mapping for its path.\n* `get(self, path, caller=False)` - this function is used by PyangBind to retrieve all data nodes that correspond to a certain path.\n\nIt is intended that there can be multiple implementations of the XPathHelper interface such that one can use it to provide database backing if required (e.g., the XPathHelper class' `register` method could be used to serialise the corresponding data instances and insert them into a database).\n\n## PyangBind's YANGPathHelper Class\n\n`pyangbind.lib.xpathhelper` provides an implementation of an XPathHelper class named `YANGPathHelper`. This class implements an in-memory mapping between paths and the corresponding PyangBind object. It does this by constructing a lightweight XML document of the form:\n\n```xml\n<root>\n  <bgp object_ptr='(str)'>\n    <neighbors object_ptr='(str)'>\n      <neighbor peer-addr='192.0.2.1' object_ptr='(str)'>\n        ...\n      </neighbor>\n    </neighbors>\n  </bgp>\n</root>\n```\n\nThe `object_ptr` attribute of each XML Element provides a reference to an entry in `_library` dictionary which stores references to the PyangBind classes. The contents of the document can be viewed using the `tostring()` method of any YANGPathHelper instance.\n\nThe YANGPathHelper provides a `get()` and `get_unique()` method - the latter raises an exception if there is >1 object corresponding to the path that is specified.\n\n## Usage of YANGPathHelper <a name=\"yangpathhelper\"></a>\n\nTo initialise a YANGPathHelper class and use it with PyangBind-generated classes, the bindings must have been specified with the `--use-xpathhelper` argument. This ensures that the bindings are configured to pass the `path_helper` reference to one another as new classes are instantiated.\n\nIn order to then use the YANGPathHelper, an instance should be created and then handed to the PyangBind class as it is created through the `path_helper` kwarg:\n\n```python\n>>>\n>>> from openconfig import openconfig_bgp\n>>> from pyangbind.lib.xpathhelper import YANGPathHelper\n>>>\n>>> ph = YANGPathHelper()\n>>> ob = openconfig_bgp(path_helper=ph)\n>>>\n```\n\nFollowing this initialisation, the classes are then utilised as per the normal operation:\n\n```python\n>>> peer = ob.bgp.neighbors.neighbor.add(\"192.0.2.1\")\n>>> peer.config.peer_as = 15169\n```\n\nAfter creating an entry such as this, it is then possible to view the data using the standard `.get()` method, and see the XML document that has been created by the `YANGPathHelper` `ph` object:\n\n```python\n>>> print peer.get(filter=True)\n{'neighbor-address': u'192.0.2.1', 'config': {'neighbor-address': u'192.0.2.1', 'peer-as': 15169}}\n>>> print ph.tostring(pretty_print=True)\n<root>\n  <bgp obj_ptr=\"cedd3b87-edf1-11e5-92c1-acbc32aad1a5\">\n    <neighbors obj_ptr=\"cee14035-edf1-11e5-a7be-acbc32aad1a5\">\n      <neighbor obj_ptr=\"d329332b-edf1-11e5-9868-acbc32aad1a5\" neighbor-address=\"192.0.2.1\">\n        <route-reflector obj_ptr=\"d3294b63-edf1-11e5-b14d-acbc32aad1a5\">\n...\n```\n\nIt is rare that there is a requirement to interact directly with this XML. Rather the `get()` method of the `ph` object can now be utilised to retrieve values by their path.\n\nFor instance, if another peer is added (`10.0.0.1`, `config.peer_as = 6643`), then the following XPATH expressions retrieve the possible neighbors, and a particular neighbor's AS number:\n\n```\n>>> ph.get(\"/bgp/neighbors/neighbor/neighbor-address\")\n[u'192.0.2.1', u'10.0.0.1']\n>>> ph.get(\"/bgp/neighbors/neighbor[neighbor-address='192.0.2.1']/config/peer-as\")\n[15169]\n>>> ph.get_unique(\"/bgp/neighbors/neighbor[neighbor-address='10.0.0.1']/config/peer-as\")\n6643\n```\n\nThe `get()` method will return a list of all objects that match the expression, whereas `get_unique` will return an individual object.\n\nWhen looking up a certain neighbor, it is possible to utilise the `_parent` attribute of a particular object to traverse up the tree to retrieve a wider object. For instance:\n\n```python\n>>> peer_as = ph.get_unique(\"/bgp/neighbors/neighbor[neighbor-address='10.0.0.1']/config/peer-as\")\n>>> peer_as._parent._parent.get(filter=True)\n{'neighbor-address': u'10.0.0.1', 'config': {'neighbor-address': u'10.0.0.1', 'peer-as': 6643}}\n```\n\nNo action is required  to ensure that objects unregister themselves as they are removed from the data tree:\n\n```python\n>>> ob.bgp.neighbors.neighbor.delete(\"10.0.0.1\")\n>>> ph.get(\"/bgp/neighbors/neighbor/neighbor-address\")\n[u'192.0.2.1']\n```\n"
  },
  {
    "path": "docs/yang.md",
    "content": "# Python to YANG mapping\n\nPyangBind makes a number of design decisions about how to map YANG data types of Python types, and how to carry the additional attributes that are required for serialisation and deserialisation; and other interaction with YANG-modelled data in Python.\n\n * [High Level Design for Mapped Classes](#hld) - how PyangBind handles YANG classes\n * [Items With No Direct Type Mapping](#nodirect) - Classes defined by PyangBind to represent YANG types.\n * [config false](#configfalse) - Special properties of `config false` items.\n * [YANG Data Types and Feature Support](#yangfeature) - An overview of the YANG types and features supported in PyangBind\n\n\n## High-Level Design for Mapped Classes <a name=\"hld\"></a>\nWhere possible, a built-in Python type is utilised for each class - for example, mapping a YANG `string` to Python `unicode`. All methods of `unicode` are kept such that the YANG `string` can be manipulated as per a standard Python string.\n\nHowever, running `type(yang_string)` will yield that the type of the string is a `<class 'pyangbind.lib.yangtypes.YANGBaseClass'>`. PyangBind wraps each class in a meta-class which provides a number of other attributes. Particularly, elements such as storing the original YANG name of the object, tracking its path in the data tree, determining whether it has changed from its default, its namespace etc. These are defined in `pyangbind.lib.yangtypes` as a part of the `YANGBaseClass`.\n\nEach type is dynamically generated at instantiation time using the `YANGDynClass` function. This function takes the relevant arguments and generates a dynamic type which can be used to represent the YANG data type.\n\n## Items with no Direct Type Mapping <a name=\"nodirect\"></a>\n\nSome YANG types, e.g., `leaf-list`, do not have a direct analogue in Python (a `leaf-list` is a Python `list` which restricts the type/values of items that can be added to it). To this end, `pyangbind.lib.yangtypes` defines a number of classes which provide restrictions such as those that are required for a `leaf-list`. The types defined are:\n\n - `RestrictedPrecisionDecimalType` - used for YANG's `decimal64` - this class represents a Decimal that can only have a restricted number of fractional digits (as specified by YANG's `fraction-digits` statement).\n - `RestrictedClassType` - used as a flexible wrapper around a number of native types to restrict their values. A dictionary of restrictions is provided to the class. Made up of entries specifying:\n   * `pattern` - used for any type that can be restricted using a regular expression.\n   * `range`  - used for any type that can be restricted using a range of possible numeric values.\n   * `length` - used for any type where the length of the input can be restricted.\n   * `dict_key` - used for `enumeration` and `identityref` types - a dictionary is provided and the valid values are restricted to keys of the supplied dictionary. Each dictionary value can hold metadata elements such as `@namespace` and `@defining_module` which are used in serialisation.\n - `TypedListType` - which is used for leaf-list values where the only values in the list must correspond to the types allowed in the `leaf-list` `type` statement.\n - `YANGListType` - implements a YANG list as a keyed data structure (internally a Python `dict`), which can only hold instances of a particular class (which represents the list's children), and the key value must be valid within the enclosed object.\n -  `YANGBool` - a boolean type. This is generally required because `bool` is not extensible in Python.\n - `ReferenceType` - a type represeting a `leafref` which provides a lookup against the data tree for valid values, and allows pointer `leafref` items.\n\n## `config false` Elements <a name=\"configfalse\"></a>\n\nPyangBind objects are generically set through simply defining the value using `container.leaf = value`, however, for `config false` elements, PyangBind restricts how these can be set by the progammer. The intention of this behaviour is to ensure that a user is specifically aware that they are setting a value that is not intended to be writable in the module. However, back-end code may need to populate such values.\n\nTo set a `config false` leaf, the setter method must be accessed directly, this can be done through the method `_set_X` where X is the Python-safe name of the element to be set (e.g., `total-paths` becomes `total_paths` in the `openconfig_bgp.global.state` container). Attempting to set the `total_paths` value directly will result in an `AttributeError` being raised. For example:\n\n```python\n>>>ocbgp.bgp.global_.state.total_paths = 500\nTraceback (most recent call last):\n  File \"<stdin>\", line 1, in <module>\nAttributeError: can't set attribute\n>>> ocbgp.bgp.global_.state._set_total_paths(500)\n>>> ocbgp.bgp.global_.state.total_paths\n500\n```\n\n## YANG Data Types and Feature Support <a name=\"yangfeature\"></a>\n\nPyangBind does not currently try and be feature complete against the YANG language. Contributions to add new features are appreciated. The table below attempts to map the design choices that PyangBind makes, and provide pointers to tests which demonstrate how this functionality can be used:\n\n **Type**            | **Sub-Statement**   | **Supported Type**      | **Unit Tests**\n --------------------|--------------------|--------------------------|---------------\n **binary**          | -                   | [bytes](https://docs.python.org/3/library/stdtypes.html?#bytes)           | tests/binary\n \\-                  | length              | Supported           | tests/binary\n **bits**            | -                   | Set\\[str\\]              | tests/bits\n \\-                  | position            | Supported               |\n **boolean**         | -                   | YANGBool                | tests/boolean-empty\n **empty**           | -                   | YANGBool                | tests/boolean-empty\n **decimal64**       | -                   | [Decimal](https://docs.python.org/2/library/decimal.html) | tests/decimal64\n \\-                   | fraction-digits     | Supported               | tests/decimal64\n **enumeration**     | -                   | Supported               | tests/enumeration\n **identityref**     | -                   | Supported               | tests/identityref\n **int{8,16,32,64}** | -                   | [numpy int](http://docs.scipy.org/doc/numpy/user/basics.types.html) | tests/int\n \\-                   | range               | Supported               | tests/int\n **uint{8,16,32,64}**| -                   | [numpy uint](http://docs.scipy.org/doc/numpy/user/basics.types.html) | tests/int\n \\-                   | range               | Supported               | tests/int\n **leafref**         | -                   | Supported               | tests/xpath/...\n **string**          | -                   | *str*                   | tests/string\n \\-                   | pattern             | Using python *re.match* | tests/string\n \\-                   | length              | Supported using *len*   | tests/string\n **typedef**         | -                   | Supported               | tests/typedef\n **container**       | -                   | *class*                 | tests/*\n **list**            | -                   | YANGList                | tests/list\n **leaf-list**       | -                   | TypedList               | tests/leaf-list\n **union**           | -                   | Supported               | tests/union\n **choice**          | -                   | Supported               | tests/choice\n **rpc**             | -                   | Supported                  | tests/rpc\n **extension**             | -                   | Supported                  | *TODO*\n"
  },
  {
    "path": "pyangbind/__init__.py",
    "content": "__version__ = \"0.8.7\"\n"
  },
  {
    "path": "pyangbind/helpers/__init__.py",
    "content": ""
  },
  {
    "path": "pyangbind/helpers/identity.py",
    "content": "\"\"\"\nCopyright 2016, Google Inc.\n\nAuthor: robjs@google.com\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n\"\"\"\n\nfrom pyang import util\nfrom .misc import module_import_prefixes\n\n\nclass Identity(object):\n    def __init__(self, name):\n        self.name = name\n        self.source_module = None\n        self._imported_prefixes = []\n        self.source_namespace = None\n        self.base = None\n        self.children = []\n\n    def add_prefix(self, prefix):\n        if self.source_module not in self._imported_prefixes:\n            self._imported_prefixes.append(self.source_module)\n        if prefix not in self._imported_prefixes:\n            self._imported_prefixes.append(prefix)\n\n    def add_child(self, child):\n        if not isinstance(child, Identity):\n            raise ValueError(\"Must supply a identity as a child\")\n        self.children.append(child)\n\n    def __str__(self):\n        return \"%s:%s\" % (self.source_module, self.name)\n\n    def prefixes(self):\n        return self._imported_prefixes\n\n\nclass IdentityStore(object):\n    def __init__(self):\n        self._store = []\n\n    def find_identity_by_source_name(self, s, n):\n        for i in self._store:\n            if i.source_module == s and i.name == n:\n                return i\n\n    def add_identity(self, i):\n        if isinstance(i, Identity):\n            if not self.find_identity_by_source_name(i.source_module, i.name):\n                self._store.append(i)\n        else:\n            raise ValueError(\"Must specify an identity\")\n\n    def identities(self):\n        return [\"%s:%s\" % (i.source_module, i.name) for i in self._store]\n\n    def __iter__(self):\n        return iter(self._store)\n\n    def build_store_from_definitions(self, ctx, defnd):\n        unresolved_identities = list(defnd.keys())\n        unresolved_identity_count = {k: 0 for k in defnd}\n        error_ids = []\n\n        mod_ref_prefixes = module_import_prefixes(ctx)\n\n        while len(unresolved_identities):\n            this_id = unresolved_identities.pop(0)\n            iddef = defnd[this_id]\n\n            try:\n                mainmod = iddef.main_module()\n            except AttributeError:\n                mainmod = None\n            if mainmod is not None:\n                defmod = mainmod\n\n            defining_module = defmod.arg\n\n            # Check we don't already have this identity defined\n            if self.find_identity_by_source_name(defining_module, iddef.arg) is not None:\n                continue\n\n            namespace = defmod.search_one(\"namespace\").arg\n            prefix = defmod.search_one(\"prefix\").arg\n\n            base_ids = []\n            bases = iddef.search(\"base\")\n            for base in bases:\n                # Determine what the name of the base and the prefix for\n                # the base should be\n                if \":\" in base.arg:\n                    base_pfx, base_name = base.arg.split(\":\")\n                else:\n                    base_pfx, base_name = prefix, base.arg\n\n                parent_module = util.prefix_to_module(defmod, base_pfx, base.pos, ctx.errors)\n\n                # Find whether we have the base in the store\n                base_id = self.find_identity_by_source_name(parent_module.arg, base_name)\n\n                if base_id is None:\n                    # and if not, then push this identity back onto the stack\n                    unresolved_identities.append(this_id)\n                    unresolved_identity_count[this_id] += 1\n                    break\n                    # FIXME(Joey Wilhelm): `unresolved_idc` and `ident` are both undefined. What is the intent of this code?\n                    # if unresolved_identity_count[this_id] > 1000:\n                    #   if unresolved_idc[ident] > 1000:\n                    #     sys.stderr.write(\"could not find a match for %s base: %s\\n\" %\n                    #       (iddef.arg, base_name))\n                    #     error_ids.append(ident)\n\n                base_ids.append(base_id)\n\n            else:\n                # We found all the bases (of which there may be none).\n                # Create a new identity that reflects this one.\n                tid = Identity(iddef.arg)\n                tid.source_module = defining_module\n                tid.source_namespace = namespace\n                tid.add_prefix(prefix)\n                self.add_identity(tid)\n                for base_id in base_ids:\n                    base_id.add_child(tid)\n\n                if defining_module in mod_ref_prefixes:\n                    for i in mod_ref_prefixes[defining_module]:\n                        tid.add_prefix(i)\n\n            if error_ids:\n                raise TypeError(\"could not resolve identities %s\" % error_ids)\n\n        self._build_inheritance()\n\n    def _recurse_children(self, identity, children):\n        for child in identity.children:\n            children.append(child)\n            self._recurse_children(child, children)\n        return children\n\n    def _build_inheritance(self):\n        for i in self._store:\n            ch = list()\n            self._recurse_children(i, ch)\n            i.children = ch\n"
  },
  {
    "path": "pyangbind/helpers/misc.py",
    "content": "\"\"\"\nCopyright 2015, Rob Shakir\nModifications copyright 2016, Google Inc.\n\nAuthor: robjs@google.com\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n\"\"\"\n\nimport sys\n\n\ndef module_import_prefixes(ctx):\n    mod_ref_prefixes = {}\n    for mod in ctx.modules:\n        m = ctx.search_module(0, mod[0])\n        for importstmt in m.search(\"import\"):\n            if importstmt.arg not in mod_ref_prefixes:\n                mod_ref_prefixes[importstmt.arg] = []\n            mod_ref_prefixes[importstmt.arg].append(importstmt.search_one(\"prefix\").arg)\n    return mod_ref_prefixes\n\n\ndef find_child_definitions(obj, defn, prefix, definitions):\n    for i in obj.search(defn):\n        if i.arg in definitions:\n            sys.stderr.write(\"WARNING: duplicate definition of %s\" % i.arg)\n        else:\n            definitions[\"%s:%s\" % (prefix, i.arg)] = i\n\n    possible_parents = [\"grouping\", \"container\", \"list\", \"rpc\", \"input\", \"output\", \"notification\"]\n\n    for parent_type in possible_parents:\n        for ch in obj.search(parent_type):\n            if ch.i_children:\n                find_child_definitions(ch, defn, prefix, definitions)\n\n    return definitions\n\n\ndef find_definitions(defn, ctx, module, prefix):\n    # Find the statements within a module that map to a particular type of\n    # statement, for instance - find typedefs, or identities, and reutrn them\n    # as a dictionary to the calling function.\n    definitions = {}\n    defin = find_child_definitions(module, defn, prefix, definitions)\n    return defin\n"
  },
  {
    "path": "pyangbind/lib/__init__.py",
    "content": ""
  },
  {
    "path": "pyangbind/lib/base.py",
    "content": "\"\"\"\nCopyright 2015, Rob Shakir (rjs@jive.com, rjs@rob.sh)\n\nThis project has been supported by:\n          * Jive Communcations, Inc.\n          * BT plc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n\"\"\"\n\n\nclass PybindBase(object):\n    __slots__ = ()\n\n    def elements(self):\n        return self._pyangbind_elements\n\n    def __str__(self):\n        return str(self.elements())\n\n    def get(self, filter=False):\n        def error():\n            return NameError, \"element does not exist\"\n\n        d = {}\n        # for each YANG element within this container.\n        for element_name in self._pyangbind_elements:\n            element = getattr(self, element_name, error)\n            if hasattr(element, \"yang_name\"):\n                # retrieve the YANG name method\n                yang_name = getattr(element, \"yang_name\", error)\n                element_id = yang_name()\n            else:\n                element_id = element_name\n\n            if hasattr(element, \"get\"):\n                # this is a YANG container that has its own\n                # get method\n                d[element_id] = element.get(filter=filter)\n                if filter is True:\n                    # if the element hadn't changed but we were\n                    # filtering unchanged elements, remove it\n                    # from the dictionary\n                    if isinstance(d[element_id], dict):\n                        for entry in d[element_id].keys():\n                            changed, present = False, False\n\n                            if hasattr(d[element_id][entry], \"_changed\"):\n                                if d[element_id][entry]._changed():\n                                    changed = True\n                            else:\n                                changed = None\n\n                            if hasattr(d[element_id][entry], \"_present\"):\n                                if not d[element_id][entry]._present() is True:\n                                    present = True\n                            else:\n                                present = None\n\n                            if present is False and changed is False:\n                                del d[element_id][entry]\n\n                        if len(d[element_id]) == 0:\n                            if element._presence and element._present():\n                                pass\n                            else:\n                                del d[element_id]\n                    elif isinstance(d[element_id], list):\n                        for list_entry in d[element_id]:\n                            if hasattr(list_entry, \"_changed\"):\n                                if not list_entry._changed():\n                                    d[element_id].remove(list_entry)\n                        if len(d[element_id]) == 0:\n                            del d[element_id]\n            else:\n                # this is an attribute that does not have get()\n                # method\n                if filter is False and not element._changed() and not element._present() is True:\n                    if element._default is not False and element._default:\n                        d[element_id] = element._default\n                    else:\n                        d[element_id] = element\n                elif element._changed() or element._present() is True:\n                    d[element_id] = element\n                else:\n                    # changed = False, and filter = True\n                    pass\n        return d\n\n    def __getitem__(self, k):\n        def error():\n            raise KeyError(\"Key %s does not exist\" % k)\n\n        element = getattr(self, k, error)\n        return element()\n\n    def __iter__(self):\n        for elem in self._pyangbind_elements:\n            yield (elem, getattr(self, elem))\n"
  },
  {
    "path": "pyangbind/lib/pybindJSON.py",
    "content": "\"\"\"\nCopyright 2015, Rob Shakir (rjs@jive.com, rjs@rob.sh)\n\nThis project has been supported by:\n          * Jive Communications, Inc.\n          * BT plc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n\"\"\"\n\nfrom __future__ import unicode_literals\n\nimport copy\nimport json\nfrom collections import OrderedDict\n\nfrom pyangbind.lib.serialise import pybindIETFJSONEncoder, pybindJSONDecoder, pybindJSONEncoder, pybindJSONIOError\n\n\ndef remove_path(tree, path):\n    this_part = path.pop(0)\n    if len(path) == 0:\n        try:\n            del tree[this_part]\n            return tree\n        except KeyError:\n            # ignore missing dictionary key\n            pass\n    else:\n        try:\n            tree[this_part] = remove_path(tree[this_part], path)\n        except KeyError:\n            pass\n    return tree\n\n\ndef loads(d, parent_pymod, yang_base, path_helper=None, extmethods=None, overwrite=False):\n    # This check is not really logical - since one would expect 'd' to be\n    # a string, given that this is loads. However, a previous issue meant\n    # that this really expected a dict, so this check simply makes sure\n    # that if the user really did give us a string, we're happy with that\n    # without breaking other code.\n    if isinstance(d, (str,)):\n        d = json.loads(d, object_pairs_hook=OrderedDict)\n    return pybindJSONDecoder.load_json(\n        d, parent_pymod, yang_base, path_helper=path_helper, extmethods=extmethods, overwrite=overwrite\n    )\n\n\ndef loads_ietf(d, parent_pymod, yang_base, path_helper=None, extmethods=None, overwrite=False):\n    # Same as above, to allow for load_ietf to work the same way\n    if isinstance(d, (str,)):\n        d = json.loads(d, object_pairs_hook=OrderedDict)\n    return pybindJSONDecoder.load_ietf_json(\n        d, parent_pymod, yang_base, path_helper=path_helper, extmethods=extmethods, overwrite=overwrite\n    )\n\n\ndef load(fn, parent_pymod, yang_module, path_helper=None, extmethods=None, overwrite=False):\n    try:\n        with open(fn, \"r\") as fp:\n            f = json.load(fp, object_pairs_hook=OrderedDict)\n    except IOError as m:\n        raise pybindJSONIOError(\"could not open file to read: %s\" % m)\n    return loads(f, parent_pymod, yang_module, path_helper=path_helper, extmethods=extmethods, overwrite=overwrite)\n\n\ndef load_ietf(fn, parent_pymod, yang_module, path_helper=None, extmethods=None, overwrite=False):\n    try:\n        f = json.load(open(fn, \"r\"), object_pairs_hook=OrderedDict)\n    except IOError as m:\n        raise pybindJSONIOError(\"Could not open file to read: %s\" % m)\n    return loads_ietf(f, parent_pymod, yang_module, path_helper, extmethods=extmethods, overwrite=overwrite)\n\n\ndef dumps(obj, indent=4, filter=True, skip_subtrees=[], select=False, mode=\"default\", with_defaults=None):\n    def lookup_subdict(dictionary, key):\n        if not isinstance(key, list):\n            raise AttributeError(\"keys should be a list\")\n        unresolved_dict = {}\n        for k, v in dictionary.items():\n            if \":\" in k:\n                k = k.split(\":\")[1]\n            unresolved_dict[k] = v\n\n        if not key[0] in unresolved_dict:\n            raise KeyError(\"requested non-existent key (%s)\" % key[0])\n        if len(key) == 1:\n            return unresolved_dict[key[0]]\n        current = key.pop(0)\n        return lookup_subdict(dictionary[current], key)\n\n    if not isinstance(skip_subtrees, list):\n        raise AttributeError(\"the subtrees to be skipped should be a list\")\n    if mode == \"ietf\":\n        tree = pybindIETFJSONEncoder.generate_element(obj, flt=filter, with_defaults=with_defaults)\n    else:\n        tree = obj.get(filter=filter)\n    for p in skip_subtrees:\n        pp = p.split(\"/\")[1:]\n        # Iterate through the skip path and the object's own path to determine\n        # whether they match, then skip the relevant subtrees.\n        match = True\n        trimmed_path = copy.deepcopy(pp)\n        for i, j in zip(obj._path(), pp):\n            # paths may have attributes in them, but the skip dictionary does\n            # not, so we ensure that the object's absolute path is attribute\n            # free,\n            if \"[\" in i:\n                i = i.split(\"[\")[0]\n            if not i == j:\n                match = False\n                break\n            trimmed_path.pop(0)\n\n        if match and len(trimmed_path):\n            tree = remove_path(tree, trimmed_path)\n\n    if select:\n        key_del = []\n        for t in tree:\n            keep = True\n            for k, v in select.items():\n                v = str(v)\n                if mode == \"default\" or isinstance(tree, dict):\n                    if keep and not str(lookup_subdict(tree[t], k.split(\".\"))) == v:\n                        keep = False\n                else:\n                    # handle ietf case where we have a list and might have namespaces\n                    if keep and not str(lookup_subdict(t, k.split(\".\"))) == v:\n                        keep = False\n            if not keep:\n                key_del.append(t)\n        if mode == \"default\" or isinstance(tree, dict):\n            for k in key_del:\n                if mode == \"default\":\n                    del tree[k]\n        else:\n            for i in key_del:\n                tree.remove(i)\n\n    if mode == \"ietf\":\n        cls = pybindIETFJSONEncoder\n    else:\n        cls = pybindJSONEncoder\n\n    return json.dumps(tree, cls=cls, indent=indent)\n\n\ndef dump(obj, fn, indent=4, filter=True, skip_subtrees=[], mode=\"default\"):\n    try:\n        fh = open(fn, \"w\")\n    except IOError as m:\n        raise pybindJSONIOError(\"could not open file for writing: %s\" % m)\n    fh.write(dumps(obj, indent=indent, filter=filter, skip_subtrees=skip_subtrees, mode=mode))\n    fh.close()\n"
  },
  {
    "path": "pyangbind/lib/serialise.py",
    "content": "\"\"\"\nCopyright 2015  Rob Shakir (rjs@jive.com, rjs@rob.sh)\n\nModifications copyright 2016, Google, Inc.\n\nThis project has been supported by:\n          * Jive Communcations, Inc.\n          * BT plc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n\nserialise:\n  * module containing methods to serialise pyangbind class hierarchie\n    to various data encodings. XML and/or JSON as the primary examples.\n\"\"\"\n\nfrom __future__ import unicode_literals\n\nimport json\nfrom collections import OrderedDict\nfrom decimal import Decimal\nimport base64\n\nfrom enum import IntEnum\nfrom lxml import objectify, etree\n\nfrom pyangbind.lib.yangtypes import YANGBool, safe_name\n\nlong = int\n\n\nclass WithDefaults(IntEnum):\n    IF_SET = 0\n\n\nclass pybindJSONIOError(Exception):\n    pass\n\n\nclass pybindLoadUpdateError(Exception):\n    pass\n\n\nclass pybindJSONDecodeError(Exception):\n    pass\n\n\nclass UnmappedItem(Exception):\n    \"\"\"Used to simulate an Optional value\"\"\"\n\n    pass\n\n\nclass SerialisedTypedList(list):\n    pass\n\n\nclass YangDataSerialiser(object):\n    \"\"\"\n    This class encapsulates the logic to parse the object tree and generate appropriate\n    value data types for encoding\n    \"\"\"\n\n    def preprocess_element(self, d):\n        nd = {}\n        if isinstance(d, OrderedDict) or isinstance(d, dict):\n            index = 0\n            for k in d:\n                if isinstance(d[k], dict) or isinstance(d[k], OrderedDict):\n                    nd[k] = self.preprocess_element(d[k])\n                    if getattr(d, \"_user_ordered\", False):\n                        nd[k][\"__yang_order\"] = index\n                else:\n                    nd[k] = self.default(d[k])\n                # if we wanted to do this as per draft-ietf-netmod-yang-metadata\n                # then the encoding is like this\n                # if not \"@%s\" % k in nd:\n                #  nd[\"@%s\" % k] = {}\n                #  nd[\"@%s\" % k]['order'] = index\n                # this has the downside that iterating over the dict gives you\n                # some elements that do not really exist - there is a need to\n                # exclude all elements that begin with \"@\"\n                index += 1\n        else:\n            nd = d\n        return nd\n\n    def default(self, obj):\n        pybc = getattr(obj, \"_pybind_generated_by\", None)\n        elem_name = getattr(obj, \"_yang_name\", None)\n        orig_yangt = getattr(obj, \"_yang_type\", None)\n\n        # Expand lists\n        if isinstance(obj, list):\n            return [self.default(i) for i in obj]\n        # Expand dictionaries\n        elif isinstance(obj, dict):\n            return {k: self.default(v) for k, v in obj.items()}\n\n        if pybc is not None:\n            # Special cases where the wrapper has an underlying class\n            if pybc == \"RestrictedClassType\":\n                pybc = getattr(obj, \"_restricted_class_base\")[0]\n            elif pybc == \"TypedListType\":\n                return self.yangt_typed_list(obj)\n\n        # Map based on YANG type\n        if orig_yangt in [\"leafref\"]:\n            return self.default(obj._get()) if hasattr(obj, \"_get\") else str(obj)\n        elif orig_yangt in [\"int64\", \"uint64\"]:\n            return self.yangt_long(obj)\n        elif orig_yangt in [\"identityref\"]:\n            try:\n                return self.yangt_identityref(obj)\n            except UnmappedItem:\n                pass\n        elif orig_yangt in [\"int8\", \"int16\", \"int32\", \"uint8\", \"uint16\", \"uint32\"]:\n            return self.yangt_int(obj)\n        elif orig_yangt in [\"string\", \"enumeration\"]:\n            return str(obj)\n        elif orig_yangt in [\"binary\"]:\n            return str(base64.b64encode(obj), \"ascii\")\n        elif orig_yangt in [\"decimal64\"]:\n            return self.yangt_decimal(obj)\n        elif orig_yangt in [\"bool\"]:\n            return True if obj else False\n        elif orig_yangt in [\"empty\"]:\n            return self.yangt_empty(obj)\n        elif orig_yangt in [\"container\"]:\n            return self.preprocess_element(obj.get())\n\n        # The value class is actually a pyangbind class, so map it\n        pyc = getattr(obj, \"_pybind_base_class\", None) if pybc is None else pybc\n        if pyc is not None:\n            val = self.map_pyangbind_type(pyc, orig_yangt, obj)\n            if val is not None:\n                return val\n\n        # We are left with a native type\n        if isinstance(obj, list):\n            nlist = []\n            for elem in obj:\n                nlist.append(self.default(elem))\n            return nlist\n        elif isinstance(obj, dict):\n            ndict = {}\n            for k, v in obj.items():\n                ndict[k] = self.default(v)\n            return ndict\n        elif isinstance(obj, (str,)):\n            return str(obj)\n        elif isinstance(obj, (int,)):\n            return int(obj)\n        elif isinstance(obj, (YANGBool, bool)):\n            return bool(obj)\n        elif isinstance(obj, Decimal):\n            return self.yangt_decimal(obj)\n\n        raise AttributeError(\n            \"Unmapped type: %s, %s, %s, %s, %s, %s\" % (elem_name, orig_yangt, pybc, pyc, type(obj), obj)\n        )\n\n    def map_pyangbind_type(self, map_val, original_yang_type, obj):\n        if map_val in [\"pyangbind.lib.yangtypes.RestrictedClass\", \"RestrictedClassType\"]:\n            map_val = getattr(obj, \"_restricted_class_base\")[0]\n\n        if map_val in [\"pyangbind.lib.yangtypes.ReferencePathType\", \"ReferencePathType\"]:\n            return self.default(obj._get())\n        elif map_val in [\"pyangbind.lib.yangtypes.RestrictedPrecisionDecimal\", \"RestrictedPrecisionDecimal\"]:\n            # NOTE: this doesn't seem like it needs to be a special case?\n            return self.yangt_decimal(obj)\n        elif map_val in [\"pyangbind.lib.yangtypes.YANGBinary\", \"YANGBinary\"]:\n            return str(base64.b64encode(obj), \"ascii\")\n        elif map_val in [\"unicode\"]:\n            return str(obj)\n        elif map_val in [\"pyangbind.lib.yangtypes.YANGBool\"]:\n            if original_yang_type == \"empty\":\n                # NOTE: previously with IETF mode the code would fall-through if obj was falsey\n                return self.yangt_empty(obj)\n            else:\n                return bool(obj)\n        elif map_val in [\"pyangbind.lib.yangtypes.TypedList\"]:\n            return self.yangt_typed_list(obj)\n        elif map_val in [\"int\", \"long\"]:\n            int_size = getattr(obj, \"_restricted_int_size\", None)\n            return self.yangt_long(obj) if int_size == 64 else self.yangt_int(obj)\n        elif map_val in [\"container\"]:\n            return self.preprocess_element(obj.get())\n        elif map_val in [\"decimal.Decimal\"]:\n            return self.yangt_decimal(obj)\n        elif map_val in [\"YANGBits\"]:\n            return str(obj)\n\n    def yangt_int(self, obj):\n        # for values that are 32-bits and under..\n        return int(obj)\n\n    def yangt_long(self, obj):\n        # don't need to special-case for PY3 because we assign `long = int` in the header\n        return long(obj)\n\n    def yangt_identityref(self, obj):\n        # we don't want to do anything for non-IETF serialisation, this will fall-through\n        raise UnmappedItem\n\n    def yangt_decimal(self, obj):\n        return float(obj)\n\n    def yangt_empty(self, obj):\n        return bool(obj)\n\n    def yangt_typed_list(self, obj):\n        return [self.default(i) for i in obj]\n\n\nclass IETFYangDataSerialiser(YangDataSerialiser):\n    \"\"\"\n    IETF data serialiser overrides some of the data type formats only\n    \"\"\"\n\n    def yangt_long(self, obj):\n        return str(obj)\n\n    def yangt_identityref(self, obj):\n        try:\n            emod = obj._enumeration_dict[obj][\"@module\"]\n            if emod != obj._defining_module:\n                # if not already prefixed with the type namespace\n                if not obj.startswith(\"%s\" % emod):\n                    return \"%s:%s\" % (emod, obj)\n        except KeyError:\n            pass\n        return str(obj)\n\n    def yangt_decimal(self, obj):\n        return str(obj)\n\n    def yangt_empty(self, obj):\n        return [None] if obj else False\n\n\nclass XmlYangDataSerialiser(IETFYangDataSerialiser):\n    \"\"\"\n    XML can have an empty tag, and a TypedList must be treated specially, here we mark it with a custom type\n    \"\"\"\n\n    def yangt_typed_list(self, obj):\n        # We have already used a standard list to denote a container, so we instead we use a custom list\n        # type here in the serialised model\n        return SerialisedTypedList([self.default(i) for i in obj])\n\n    def yangt_empty(self, obj):\n        return None\n\n\nclass _pybindJSONEncoderBase(json.JSONEncoder):\n    \"\"\"\n    Pybind JSON encoder base class. Implements default `encode()` and `default()` methods\n    to be used as an encoder class with the deault *json* module.\n\n    Do not use directly, subclass and set the `serialiser_class` attribute appropriately\n    \"\"\"\n\n    serialiser_class = None\n\n    def encode(self, obj):\n        return json.JSONEncoder.encode(self, self.serialiser_class().preprocess_element(obj))\n\n    def default(self, obj):\n        return self.serialiser_class().default(obj)\n\n\nclass pybindJSONEncoder(_pybindJSONEncoderBase):\n    \"\"\"Default pybind JSON encoder\"\"\"\n\n    serialiser_class = YangDataSerialiser\n\n\nclass pybindIETFJSONEncoder(_pybindJSONEncoderBase):\n    \"\"\"IETF JSON encoder, we add a special method `generate_element()` that should be used\n    to restructure the pybind object to fit IETF requirements prior to JSON encoding.\"\"\"\n\n    serialiser_class = IETFYangDataSerialiser\n\n    @staticmethod\n    def yname_ns_func(parent_namespace, element, yname):\n        if not element._namespace == parent_namespace:\n            # if the namespace is different, then precede with the module\n            # name as per spec.\n            return \"%s:%s\" % (element._defining_module, yname)\n        else:\n            return yname\n\n    @staticmethod\n    def generate_element(obj, parent_namespace=None, flt=False, with_defaults=None):\n        \"\"\"Restructure pybind `obj` to IETF spec\"\"\"\n        ietf_tree_json_func = make_generate_ietf_tree(pybindIETFJSONEncoder.yname_ns_func)\n        return ietf_tree_json_func(obj, parent_namespace=parent_namespace, flt=flt, with_defaults=with_defaults)\n\n\nclass pybindIETFXMLEncoder(object):\n    \"\"\"\n    IETF XML encoder for pybind object tree serialisation.\n    Use the `encode()` method to return an lxml.objectify tree representation of the pybind object.\n    The `serialise()` method is a helper around that to return a pretty-printed XML string.\n    \"\"\"\n\n    class EMF(objectify.ElementMaker):\n        \"\"\"Custome ElementMaker class to ease netconf namespace handling\"\"\"\n\n        def __init__(self, namespace=None, nsmap=None):\n            assert namespace or nsmap, \"Must set either namespace or nsmap\"\n            if namespace:\n                nsmap = {None: namespace}\n            elif nsmap:\n                namespace = nsmap[None]\n            super(pybindIETFXMLEncoder.EMF, self).__init__(annotate=False, namespace=namespace, nsmap=nsmap)\n\n    @classmethod\n    def generate_xml_tree(cls, module_name, module_namespace, tree):\n        \"\"\"Map the IETF structured, and value-processed, object tree into an lxml objectify object\"\"\"\n        doc = pybindIETFXMLEncoder.EMF(namespace=module_namespace)(module_name)\n\n        def aux(parent, root):\n            for k, v in root.items():\n                k, nsmap = k\n                E = pybindIETFXMLEncoder.EMF(nsmap=dict(nsmap))\n                if isinstance(v, SerialisedTypedList):\n                    # TypedList (e.g. leaf-list or union-list), process each element as a sibling\n                    for i in v:\n                        el = E(k, str(i))\n                        parent.append(el)\n                elif isinstance(v, list):\n                    # a container maps to a list, recursively process each element as a child element\n                    for i in v:\n                        el = E(k)\n                        parent.append(el)\n                        aux(el, i)\n                elif isinstance(v, dict):\n                    el = E(k)\n                    parent.append(el)\n                    aux(el, v)\n                elif v is None:\n                    el = E(k)\n                    parent.append(el)\n                elif isinstance(v, bool):\n                    _v = str(v).lower()\n                    parent.append(E(k, _v))\n                else:\n                    parent.append(E(k, str(v)))\n\n        aux(doc, tree)\n        return doc\n\n    @staticmethod\n    def yname_ns_func(parent_namespace, element, yname):\n        # to keeps things simple, we augment every key with a complete namespace map\n        ns_map = [(None, element._namespace)]\n        if element._yang_type == \"identityref\" and element._changed():\n            # configured identityref (i.e. points to a valid identity)\n            ns_map.append(\n                (element._enumeration_dict[element][\"@module\"], element._enumeration_dict[element][\"@namespace\"])\n            )\n        return yname, tuple(ns_map)\n\n    @classmethod\n    def encode(cls, obj, filter=True):\n        \"\"\"return the lxml objectify tree for the pybind object\"\"\"\n        ietf_tree_xml_func = make_generate_ietf_tree(pybindIETFXMLEncoder.yname_ns_func)\n        tree = ietf_tree_xml_func(obj, flt=filter)\n        preprocessed = XmlYangDataSerialiser().preprocess_element(tree)\n        return cls.generate_xml_tree(obj._yang_name, obj._yang_namespace, preprocessed)\n\n    @classmethod\n    def serialise(cls, obj, filter=True, pretty_print=True):\n        \"\"\"return the complete XML document, as pretty-printed string\"\"\"\n        doc = cls.encode(obj, filter=filter)\n        return etree.tostring(doc, pretty_print=pretty_print).decode(\"utf8\")\n\n\ndef make_generate_ietf_tree(yname_ns_func):\n    \"\"\"\n    Convert a pyangbind class to a format which encodes to the IETF JSON\n    specification, rather than the default .get() format, which does not\n    match this specification.\n\n    Modes of operation controlled by with_defaults:\n\n      - None: skip data set to default values\n      - WithDefaults.IF_SET: include all explicitly set data\n\n    The implementation is based on draft-ietf-netmod-yang-json-07.\n\n    Resulting namespaced key names can be customised via *yname_func*\n    \"\"\"\n\n    def generate_ietf_tree(obj, parent_namespace=None, flt=False, with_defaults=None):\n        generated_by = getattr(obj, \"_pybind_generated_by\", None)\n        if generated_by == \"YANGListType\":\n            return [generate_ietf_tree(i, flt=flt, with_defaults=with_defaults) for i in obj.itervalues()]\n        elif generated_by is None:\n            # This is an element that is not specifically generated by\n            # pyangbind, so we simply serialise it how we would if it\n            # were a scalar.\n            return obj\n\n        d = {}\n        for element_name in obj._pyangbind_elements:\n            element = getattr(obj, element_name, None)\n            yang_name = getattr(element, \"yang_name\", None)\n            yname = yang_name() if yang_name is not None else element_name\n\n            # adjust yname, if necessary, given the current namespace context\n            yname = yname_ns_func(parent_namespace, element, yname)\n\n            generated_by = getattr(element, \"_pybind_generated_by\", None)\n            if generated_by == \"container\":\n                d[yname] = generate_ietf_tree(\n                    element, parent_namespace=element._namespace, flt=flt, with_defaults=with_defaults\n                )\n                present = getattr(element, \"_cpresent\", False)\n                if not len(d[yname]) and not present:\n                    del d[yname]\n            elif generated_by == \"YANGListType\":\n                d[yname] = [\n                    generate_ietf_tree(i, parent_namespace=element._namespace, flt=flt, with_defaults=with_defaults)\n                    for i in element.itervalues()\n                ]\n                if not len(d[yname]):\n                    del d[yname]\n            else:\n                if with_defaults is None:\n                    if flt and element._changed():\n                        d[yname] = element\n                    elif not flt:\n                        d[yname] = element\n                elif with_defaults == WithDefaults.IF_SET:\n                    if element._changed() or element._default == element:\n                        d[yname] = element\n        return d\n\n    return generate_ietf_tree\n\n\nclass pybindIETFXMLDecoder(object):\n    \"\"\"\n    IETF XML decoder for pybind object tree deserialisation.\n    Use the `decode()` method to return an pyangbind representation of the yang object.\n    \"\"\"\n\n    @classmethod\n    def decode(cls, xml, bindings, module_name):\n        # using a custom parser to strip comments (so we don't handle them later)\n        parser = objectify.makeparser(remove_comments=True, remove_blank_text=True)\n        doc = objectify.fromstring(xml, parser=parser)\n        return cls.load_xml(doc, bindings, module_name)\n\n    @staticmethod\n    def load_xml(d, parent, yang_base, obj=None, path_helper=None, extmethods=None):\n        \"\"\"low-level XML deserialisation function, based on pybindJSONDecoder.load_ietf_json()\"\"\"\n        if obj is None:\n            # we need to find the class to create, as one has not been supplied.\n            base_mod_cls = getattr(parent, safe_name(yang_base))\n            tmp = base_mod_cls(path_helper=False)\n\n            if path_helper is not None:\n                # check that this path doesn't already exist in the\n                # tree, otherwise we create a duplicate.\n                existing_objs = path_helper.get(tmp._path())\n                if len(existing_objs) == 0:\n                    obj = base_mod_cls(path_helper=path_helper, extmethods=extmethods)\n                elif len(existing_objs) == 1:\n                    obj = existing_objs[0]\n                else:\n                    raise pybindLoadUpdateError(\"update was attempted to a node that \" + \"was not unique\")\n            else:\n                # in this case, we cannot check for an existing object\n                obj = base_mod_cls(path_helper=path_helper, extmethods=extmethods)\n\n        for child in d.getchildren():\n            # separate element namespace and tag\n            qn = etree.QName(child)\n            namespace, ykey = qn.namespace, qn.localname\n\n            # need to look up the key in the object to find out what type it should be,\n            # because we can't tell from the XML structure\n            attr_get = getattr(obj, \"_get_%s\" % safe_name(ykey), None)\n            if attr_get is None:\n                raise AttributeError(\"Invalid attribute specified (%s)\" % ykey)\n            chobj = attr_get()\n\n            if chobj._yang_type == \"container\":\n                if hasattr(chobj, \"_presence\"):\n                    if chobj._presence:\n                        chobj._set_present()\n\n                pybindIETFXMLDecoder.load_xml(\n                    child, None, None, obj=chobj, path_helper=path_helper, extmethods=extmethods\n                )\n\n            elif chobj._yang_type == \"list\":\n                if not chobj._keyval:\n                    raise NotImplementedError(\"keyless list?\")\n\n                # we just need to find the key value to add it to the list\n                key_parts = []\n                add_kwargs = {}\n                for pkv, ykv in zip(chobj._keyval.split(\" \"), chobj._yang_keys.split(\" \")):\n                    add_kwargs[pkv] = child[ykv]\n                    key_parts.append(str(child[ykv]))\n                key_str = \" \".join(map(str, key_parts))\n                if key_str not in chobj:\n                    nobj = chobj.add(**add_kwargs)\n                else:\n                    nobj = chobj[key_str]\n\n                # now we have created the nested object element, we add other members\n                pybindIETFXMLDecoder.load_xml(\n                    child, None, None, obj=nobj, path_helper=path_helper, extmethods=extmethods\n                )\n\n            elif hasattr(chobj, \"_pybind_generated_by\") and chobj._pybind_generated_by == \"TypedListType\":\n                # NOTE: this is a little curious, because we are relying on the coercion of types\n                #   i.e. lxml will \"identify\" the type based on its own internal model of Python\n                #   types, see: https://lxml.de/2.0/objectify.html#how-data-types-are-matched\n                # There are limitations which need to be addressed, e.g. hexadecimal strings.\n                # Already, we have a stringify-fallback: if we fail on the first attempt then\n                # try again as a pure string (if its allowed).\n                try:\n                    chobj.append(child.pyval)\n                except ValueError:\n                    if str in chobj._allowed_type:\n                        chobj.append(str(child.pyval))\n                    else:\n                        raise\n\n            else:\n                if chobj._is_keyval is True:\n                    # we've already added the key\n                    continue\n\n                val = child.text\n                if chobj._yang_type == \"empty\":\n                    if child.text is None:\n                        val = True\n                    else:\n                        raise ValueError(\"Invalid value for empty in input XML - key: %s, got: %s\" % (ykey, val))\n\n                elif chobj._yang_type == \"identityref\":\n                    if \":\" in val:\n                        _, val = val.split(\":\", 1)\n\n                if val is not None:\n                    set_method = getattr(obj, \"_set_%s\" % safe_name(ykey), None)\n                    if set_method is None:\n                        raise AttributeError(\"Invalid attribute specified in XML - %s\" % (ykey))\n                    set_method(val)\n\n        return obj\n\n\nclass pybindJSONDecoder(object):\n    @staticmethod\n    def load_json(\n        d, parent, yang_base, obj=None, path_helper=None, extmethods=None, overwrite=False, skip_unknown=False\n    ):\n        if obj is None:\n            # we need to find the class to create, as one has not been supplied.\n            base_mod_cls = getattr(parent, safe_name(yang_base))\n            tmp = base_mod_cls(path_helper=False)\n\n            if path_helper is not None:\n                # check that this path doesn't already exist in the\n                # tree, otherwise we create a duplicate.\n                existing_objs = path_helper.get(tmp._path())\n                if len(existing_objs) == 0:\n                    obj = base_mod_cls(path_helper=path_helper, extmethods=extmethods)\n                elif len(existing_objs) == 1:\n                    obj = existing_objs[0]\n                else:\n                    raise pybindLoadUpdateError(\"update was attempted to a node that \" + \"was not unique\")\n            else:\n                # in this case, we cannot check for an existing object\n                obj = base_mod_cls(path_helper=path_helper, extmethods=extmethods)\n\n        # Handle the case where we are supplied with a scalar value rather than\n        # a list\n        if not isinstance(d, dict) or isinstance(d, list):\n            set_method = getattr(obj._parent, \"_set_%s\" % safe_name(obj._yang_name))\n            set_method(d)\n            return obj\n\n        for key in d:\n            child = getattr(obj, \"_get_%s\" % safe_name(key), None)\n            if child is None and skip_unknown is False:\n                raise AttributeError(\"JSON object contained a key that\" + \"did not exist (%s)\" % (key))\n            elif child is None and skip_unknown:\n                # skip unknown elements if we are asked to by the user`\n                continue\n            chobj = child()\n\n            if hasattr(chobj, \"_presence\"):\n                if chobj._presence:\n                    chobj._set_present()\n\n            set_via_stdmethod = True\n            pybind_attr = getattr(chobj, \"_pybind_generated_by\", None)\n            if pybind_attr in [\"container\"]:\n                if overwrite:\n                    for elem in chobj._pyangbind_elements:\n                        unsetchildelem = getattr(chobj, \"_unset_%s\" % elem)\n                        unsetchildelem()\n                pybindJSONDecoder.load_json(\n                    d[key], chobj, yang_base, obj=chobj, path_helper=path_helper, skip_unknown=skip_unknown\n                )\n                set_via_stdmethod = False\n            elif pybind_attr in [\"YANGListType\", \"list\"]:\n                # we need to add each key to the list and then skip a level in the\n                # JSON hierarchy\n                list_obj = getattr(obj, safe_name(key), None)\n                if list_obj is None and skip_unknown is False:\n                    raise pybindJSONDecodeError(\"Could not load list object \" + \"with name %s\" % key)\n                if list_obj is None and skip_unknown is not False:\n                    continue\n\n                ordered_list = getattr(list_obj, \"_ordered\", None)\n                if ordered_list:\n                    # Put keys in order:\n                    okeys = []\n                    kdict = {}\n                    for k, v in d[key].items():\n                        if \"__yang_order\" not in v:\n                            # Element is not specified in terms of order, so\n                            # push to a list that keeps this order\n                            okeys.append(k)\n                        else:\n                            kdict[v[\"__yang_order\"]] = k\n                            # Throw this metadata away\n                            v.pop(\"__yang_order\", None)\n                    okeys.reverse()\n                    key_order = [kdict[k] for k in sorted(kdict)]\n                    for add_element in okeys:\n                        key_order.append(add_element)\n                else:\n                    key_order = d[key].keys()\n\n                for child_key in key_order:\n                    if child_key not in chobj:\n                        chobj.add(child_key)\n                    parent = chobj[child_key]\n                    pybindJSONDecoder.load_json(\n                        d[key][child_key],\n                        parent,\n                        yang_base,\n                        obj=parent,\n                        path_helper=path_helper,\n                        skip_unknown=skip_unknown,\n                    )\n                    set_via_stdmethod = False\n                if overwrite:\n                    for child_key in chobj:\n                        if child_key not in d[key]:\n                            chobj.delete(child_key)\n            elif pybind_attr in [\"TypedListType\"]:\n                if not overwrite:\n                    list_obj = getattr(obj, \"_get_%s\" % safe_name(key))()\n                    for item in d[key]:\n                        if item not in list_obj:\n                            list_obj.append(item)\n                    list_copy = []\n                    for elem in list_obj:\n                        list_copy.append(elem)\n                    for e in list_copy:\n                        if e not in d[key]:\n                            list_obj.remove(e)\n                    set_via_stdmethod = False\n                else:\n                    # use the set method\n                    pass\n            elif pybind_attr in [\n                \"RestrictedClassType\",\n                \"ReferencePathType\",\n                \"RestrictedPrecisionDecimal\",\n                \"YANGBits\",\n            ]:\n                # normal but valid types - which use the std set method\n                pass\n            elif pybind_attr is None:\n                # not a pybind attribute at all - keep using the std set method\n                pass\n            else:\n                raise pybindLoadUpdateError(\"unknown pybind type when loading JSON: %s\" % pybind_attr)\n\n            if set_via_stdmethod:\n                # simply get the set method and then set the value of the leaf\n                set_method = getattr(obj, \"_set_%s\" % safe_name(key))\n                set_method(d[key], load=True)\n        return obj\n\n    @staticmethod\n    def check_metadata_add(key, data, obj):\n        keys = [str(k) for k in data]\n        if (\"@\" + key) in keys:\n            for k, v in data[\"@\" + key].items():\n                obj._add_metadata(k, v)\n\n    @staticmethod\n    def load_ietf_json(\n        d, parent, yang_base, obj=None, path_helper=None, extmethods=None, overwrite=False, skip_unknown=False\n    ):\n        if obj is None:\n            # we need to find the class to create, as one has not been supplied.\n            base_mod_cls = getattr(parent, safe_name(yang_base))\n            tmp = base_mod_cls(path_helper=False)\n\n            if path_helper is not None:\n                # check that this path doesn't already exist in the\n                # tree, otherwise we create a duplicate.\n                existing_objs = path_helper.get(tmp._path())\n                if len(existing_objs) == 0:\n                    obj = base_mod_cls(path_helper=path_helper, extmethods=extmethods)\n                elif len(existing_objs) == 1:\n                    obj = existing_objs[0]\n                else:\n                    raise pybindLoadUpdateError(\"update was attempted to a node that \" + \"was not unique\")\n            else:\n                # in this case, we cannot check for an existing object\n                obj = base_mod_cls(path_helper=path_helper, extmethods=extmethods)\n\n        # Handle the case where we are supplied with a scalar value rather than\n        # a list\n        if not isinstance(d, dict) or isinstance(d, list):\n            set_method = getattr(obj._parent, \"_set_%s\" % safe_name(obj._yang_name))\n            set_method(d)\n            return obj\n\n        for key in d:\n            # Fix any namespace that was supplied in the JSON\n            if \":\" in key:\n                ykey = key.split(\":\")[-1]\n            else:\n                ykey = key\n\n            if key == \"@\":\n                # Handle whole container metadata object\n                for k, v in d[key].items():\n                    obj._add_metadata(k, v)\n                continue\n            elif \"@\" in key:\n                # Don't handle metadata elements, each element\n                # will look up its own metadata\n                continue\n\n            std_method_set = False\n            # Handle the case that this is a JSON object\n            if isinstance(d[key], dict):\n                # Iterate through attributes and set to that value\n                attr_get = getattr(obj, \"_get_%s\" % safe_name(ykey), None)\n                if attr_get is None and skip_unknown is False:\n                    raise AttributeError(\"Invalid attribute specified (%s)\" % ykey)\n                elif attr_get is None and skip_unknown is not False:\n                    # Skip unknown JSON keys\n                    continue\n\n                chobj = attr_get()\n                if hasattr(chobj, \"_presence\"):\n                    if chobj._presence:\n                        chobj._set_present()\n\n                pybindJSONDecoder.check_metadata_add(key, d, chobj)\n                pybindJSONDecoder.load_ietf_json(\n                    d[key],\n                    None,\n                    None,\n                    obj=chobj,\n                    path_helper=path_helper,\n                    extmethods=extmethods,\n                    overwrite=overwrite,\n                    skip_unknown=skip_unknown,\n                )\n            elif isinstance(d[key], list):\n                for elem in d[key]:\n                    # if this is a list, then this is a YANG list\n                    this_attr = getattr(obj, \"_get_%s\" % safe_name(ykey), None)\n                    if this_attr is None:\n                        raise AttributeError(\"List specified that did not exist\")\n                    this_attr = this_attr()\n                    if hasattr(this_attr, \"_keyval\"):\n                        if overwrite:\n                            existing_keys = this_attr.keys()\n                            for i in existing_keys:\n                                this_attr.delete(i)\n                        #  this handles YANGLists\n                        if this_attr._keyval is False:\n                            # Keyless list, generate a key\n                            k = this_attr.add()\n                            nobj = this_attr[k]\n                        elif \" \" in this_attr._keyval:\n                            keystr = \"\"\n                            kwargs = {}\n                            for pkv, ykv in zip(this_attr._keyval.split(\" \"), this_attr._yang_keys.split(\" \")):\n                                kwargs[pkv] = elem[ykv]\n                                keystr += \"%s \" % elem[ykv]\n                            keystr = keystr.rstrip(\" \")\n                            if keystr not in this_attr:\n                                nobj = this_attr.add(**kwargs)\n                            else:\n                                nobj = this_attr[keystr]\n                        else:\n                            k = elem[this_attr._yang_keys]\n                            if k not in this_attr:\n                                nobj = this_attr.add(k)\n                            else:\n                                nobj = this_attr[k]\n                        pybindJSONDecoder.load_ietf_json(\n                            elem,\n                            None,\n                            None,\n                            obj=nobj,\n                            path_helper=path_helper,\n                            extmethods=extmethods,\n                            overwrite=overwrite,\n                            skip_unknown=skip_unknown,\n                        )\n                        pybindJSONDecoder.check_metadata_add(key, d, nobj)\n                    else:\n                        # this is a leaf-list\n                        std_method_set = True\n            else:\n                std_method_set = True\n\n            if std_method_set:\n                get_method = getattr(obj, \"_get_%s\" % safe_name(ykey), None)\n                if get_method is None and skip_unknown is False:\n                    raise AttributeError(\"JSON object contained a key that \" + \"did not exist (%s)\" % (ykey))\n                elif get_method is None and skip_unknown is not False:\n                    continue\n                chk = get_method()\n                if chk._is_keyval is True:\n                    pass\n                else:\n                    val = d[key]\n                    if chk._yang_type == \"empty\":\n                        # A 'none' value in the JSON means that an empty value is set,\n                        # since this is serialised as [null] in the input JSON.\n                        if val == [None]:\n                            val = True\n                        else:\n                            raise ValueError(\"Invalid value for empty in input JSON \" \"key: %s, got: %s\" % (ykey, val))\n\n                    if chk._yang_type == \"identityref\":\n                        # identityref values in IETF JSON may contain their module name, as a prefix,\n                        # but we don't build identities with these as valid values. If this is the\n                        # case then re-write the value to just be the name of the identity that we\n                        # should know about.\n                        if \":\" in val:\n                            _, val = val.split(\":\", 1)\n\n                    if val is not None:\n                        set_method = getattr(obj, \"_set_%s\" % safe_name(ykey), None)\n                        if set_method is None:\n                            raise AttributeError(\"Invalid attribute specified in JSON - %s\" % (ykey))\n                        set_method(val)\n                pybindJSONDecoder.check_metadata_add(key, d, get_method())\n        return obj\n"
  },
  {
    "path": "pyangbind/lib/xpathhelper.py",
    "content": "\"\"\"\nCopyright 2015, Rob Shakir (rjs@jive.com, rjs@rob.sh)\n\nModifications copyright 2016, Google Inc.\n\nThis project has been supported by:\n          * Jive Communications, Inc.\n          * BT plc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n\nxpathhelper:\n  This module maintains an XML ElementTree for the registered Python\n  classes, so that XPATH can be used to lookup particular items.\n\"\"\"\n\nfrom __future__ import unicode_literals\n\nimport uuid\nfrom collections import OrderedDict\n\nimport regex\nfrom lxml import etree\n\nfrom .base import PybindBase\nfrom .yangtypes import safe_name\n\n\nclass YANGPathHelperException(Exception):\n    pass\n\n\nclass XPathError(Exception):\n    pass\n\n\nclass PybindImplementationError(Exception):\n    pass\n\n\nclass PybindXpathHelper(object):\n    def register(self, path, object_ptr, caller=False):\n        \"\"\"\n        A PybindXpathHelper class should supply a register() method that\n        takes two mandatory arguments, and one optional.\n\n        * path - the path to which the object should be registered. This is\n          supplied as a list of the names of the elements of the path. For\n          example, /device/interfaces/interface[name='eth0'] is supplied as\n          a [\"device\", \"interfaces\", \"interface[@name='eth0']\"].\n\n        * object_ptr - a reference to the object that is to be stored at this\n          location in the tree.\n\n        * caller=False - this supplies the path of the object that is currently\n          trying to perform a register. In general, it will not be used, but it\n          is supplied to facilitate relative path lookups.\n        \"\"\"\n        raise PybindImplementationError(\"The path helper class specified does \" + \"not implement register()\")\n\n    def unregister(self, path, caller=False):\n        \"\"\"\n        A PybindXpathHelper class should supply an unregister() method that\n        takes one mandatory argument, and one optional.\n\n        * path - the path of the object to be unregistered. Supplied as a list()\n          object of the elements of the path.\n\n        * caller=False - the absolute path of the object calling the unregister()\n          method.\n        \"\"\"\n        raise PybindImplementationError(\"The path helper class specified does \" + \"not implement unregister()\")\n\n    def get(self, path, caller=False):\n        \"\"\"\n        A PybindXpathHelper class should supply a get() method that takes one\n        mandatory argument and one optional.\n\n        * path - the path to the object to be retrieved. This may be specified as\n          a list of parts, or an XPATH expression.\n\n        * caller=False - the absolute path of the object calling the get method.\n        \"\"\"\n        raise PybindImplementationError(\"The path helper class specified does \" + \"not implement get()\")\n\n\n# A class which acts as \"/\" within the hierarchy - it acts as per any other\n# PyangBind element for the purposes of get() calls - allowing \"/\" to be\n# serialised to\nclass FakeRoot(PybindBase):\n    _pybind_generated_by = \"FakeRoot\"\n\n    def __init__(self):\n        self._pyangbind_elements = OrderedDict()\n\n\nclass YANGPathHelper(PybindXpathHelper):\n    _attr_re = regex.compile(r\"^(?P<tagname>[^\\[]+)(?P<args>(\\[[^\\]]+\\])+)$\")\n    _arg_re = regex.compile(\n        r\"^((and|or) )?[@]?(?P<cmd>[a-zA-Z0-9\\-\\_:]+)([ ]+)?\"\n        + r\"=([ ]+)?['\\\"]?(?P<arg>[^ ^'^\\\"]+)(['\\\"])?([ ]+)?\"\n        + \"(?P<remainder>.*)\"\n    )\n    _relative_path_re = regex.compile(r\"^(\\.|\\.\\.)\")\n\n    def __init__(self):\n        # Initialise an empty library and a new FakeRoot class to act as the\n        # data tree's root.\n        self._root = etree.Element(\"root\")\n        self._library = {}\n        self._library[\"root\"] = FakeRoot()\n        self._root.set(\"obj_ptr\", \"root\")\n\n    def _path_parts(self, path):\n        c = 0\n        parts = []\n        buf = \"\"\n        in_qstr, in_attr = False, False\n        while c < len(path):\n            if path[c] == \"/\" and not in_qstr and not in_attr:\n                parts.append(buf)\n                buf = \"\"\n            elif path[c] == '\"' and in_qstr:\n                in_qstr = False\n                buf += path[c]\n            elif path[c] == '\"':\n                in_qstr = True\n                buf += path[c]\n            elif path[c] == \"[\":\n                in_attr = True\n                buf += path[c]\n            elif path[c] == \"]\":\n                in_attr = False\n                buf += path[c]\n            else:\n                buf += path[c]\n            c += 1\n        parts.append(buf)\n        return parts\n\n    def _encode_path(self, path, mode=\"search\", find_parent=False, normalise_namespace=True, caller=False):\n        if mode not in [\"search\", \"set\"]:\n            raise XPathError(\"Path can only be encoded based on searching or \" + \"setting attributes\")\n\n        parts = path\n        if len(parts) == 0:\n            return \"/\"\n        elif find_parent and len(parts) == 1:\n            return []\n\n        lastelem = len(parts) - 1 if find_parent else len(parts)\n        startelem = 1 if parts[0] == \"\" else 0\n        if self._relative_path_re.match(parts[0]):\n            epath = \"\"\n        else:\n            epath = \"/\"\n        for i in range(startelem, lastelem):\n            tagname, attributes = self._tagname_attributes(parts[i], normalise_namespace=normalise_namespace)\n            if \":\" in tagname and normalise_namespace:\n                tagname = tagname.split(\":\")[1]\n\n            if attributes is not None:\n                epath += tagname + \"[\"\n                for k, v in attributes.items():\n                    # handling for rfc6020 current() specification\n                    if \"current()\" in v:\n                        remaining_path = regex.sub(\"current\\(\\)(?P<remaining>.*)\", r\"\\g<remaining>\", v).split(\"/\")\n                        # since the calling leaf may not exist, we need to do a\n                        # lookup on a path that will do, which is the parent\n                        if remaining_path[1] == \"..\":\n                            lookup = caller[:-1] + remaining_path[2:]\n                        else:\n                            lookup = caller + remaining_path[1:]\n                        resolved_current_attr = self.get(lookup)\n                        if not len(resolved_current_attr) == 1:\n                            raise XPathError(\n                                \"XPATH specified a current() expression that \" + \"returned a non-unique list\"\n                            )\n                        v = resolved_current_attr[0]\n                    epath += \"@%s='%s' \" % (k, v)\n                    if mode == \"search\":\n                        epath += \"and \"\n                if mode == \"search\":\n                    epath = epath.rstrip(\"and \")\n                epath = epath.rstrip(\" \") + \"]\"\n                epath += \"/\"\n            else:\n                epath += tagname + \"/\"\n        epath = epath.rstrip(\"/\")\n        return epath\n\n    def _tagname_attributes(self, tag, normalise_namespace=True):\n        tagname, attributes = tag, None\n        if self._attr_re.match(tag):\n            tagname, args = self._attr_re.sub(r\"\\g<tagname>||\\g<args>\", tag).split(\"||\")\n            arg_parts = [i.strip(\"[\") for i in args.split(\"]\")]\n\n            attributes = {}\n            for arg in arg_parts:\n                tmp_arg = arg\n                while len(tmp_arg):\n                    if self._arg_re.match(tmp_arg):\n                        c, a, r = self._arg_re.sub(r\"\\g<cmd>||\\g<arg>||\\g<remainder>\", tmp_arg).split(\"||\")\n                        if \":\" in c and normalise_namespace:\n                            c = c.split(\":\")[1]\n                        attributes[c] = a\n                        tmp_arg = r\n                    else:\n                        raise XPathError(\n                            \"invalid attribute string specified\"\n                            + \"for %s\" % tagname\n                            + \"(err part: %s (%s))\" % (arg, tmp_arg)\n                        )\n        return (tagname, attributes)\n\n    def register(self, object_path, object_ptr, caller=False):\n        if isinstance(object_path, str):\n            raise XPathError(\"not meant to receive strings as input to register()\")\n\n        if regex.match(r\"^\\.\\.\", object_path[0]):\n            raise XPathError(\"unhandled relative path in register()\")\n\n        # This is a hack to register anything that is a top-level object,\n        # it allows modules to register themselves against the FakeRoot\n        # class which acts as per other PyangBind objects.\n        if len(object_path) == 1:\n            setattr(self._library[\"root\"], object_path[0], object_ptr)\n            setattr(self._library[\"root\"], \"_get_%s\" % safe_name(object_path[0]), lambda: object_ptr)\n            self._library[\"root\"]._pyangbind_elements[object_path[0]] = None\n\n        # check whether we're updating\n        this_obj_existing = self._get_etree(object_path)\n        if len(this_obj_existing) > 1:\n            raise XPathError(\"duplicate objects in tree - %s\" % object_path)\n        if this_obj_existing is not None and not this_obj_existing == []:\n            this_obj_existing = this_obj_existing[0]\n            if self._library[this_obj_existing.get(\"obj_ptr\")] == object_ptr:\n                return True\n            else:\n                del self._library[this_obj_existing.get(\"obj_ptr\")]\n                new_uuid = str(uuid.uuid1())\n                self._library[new_uuid] = object_ptr\n                this_obj_existing.set(\"obj_ptr\", new_uuid)\n                return True\n\n        this_obj_id = str(uuid.uuid1())\n        self._library[this_obj_id] = object_ptr\n        parent = object_path[:-1]\n        tagname, attributes = self._tagname_attributes(object_path[-1])\n\n        if parent == []:\n            parent_o = self._root\n        else:\n            parent_o = self._get_etree(parent)\n            if len(parent_o) > 1:\n                raise XPathError(\n                    \"multiple elements returned for parent %s, must be \"\n                    + \"exact path for registration\" % \"/\"\n                    + \"/\".join(parent)\n                )\n            if parent_o == []:\n                raise XPathError(\"parent node did not exist for %s @ %s\" % (tagname, \"/\" + \"/\".join(parent)))\n            parent_o = parent_o[0]\n\n        added_item = etree.SubElement(parent_o, tagname, obj_ptr=this_obj_id)\n        if attributes is not None:\n            for k, v in attributes.items():\n                added_item.set(k, v)\n\n    def unregister(self, object_path, caller=False):\n        if isinstance(object_path, str):\n            raise XPathError(\"should not receive paths as a str in unregister()\")\n        if regex.match(r\"^(\\.|\\.\\.|\\/)\", object_path[0]):\n            raise XPathError(\"unhandled relative path in unregister()\")\n\n        existing_objs = self._get_etree(object_path)\n        if len(existing_objs) == 0:\n            raise XPathError(\"object did not exist to unregister - %s\" % object_path)\n\n        for obj in existing_objs:\n            ref = obj.get(\"obj_ptr\")\n            del self._library[ref]\n            obj.getparent().remove(obj)\n\n    def _get_etree(self, object_path, caller=False):\n        fx_q = self._encode_path(object_path, caller=caller)\n        if self._relative_path_re.match(fx_q) and caller:\n            fx_q = \".\" + self._encode_path(caller)\n            fx_q += \"/\" + self._encode_path(object_path, caller=caller)\n        else:\n            if not fx_q == \"/\":\n                fx_q = \".\" + fx_q\n\n        retr_obj = self._root.xpath(fx_q)\n        return retr_obj\n\n    def get(self, object_path, caller=False):\n        if isinstance(object_path, (str,)):\n            object_path = self._path_parts(object_path)\n\n        return [self._library[i.get(\"obj_ptr\")] for i in self._get_etree(object_path, caller=caller)]\n\n    def get_unique(self, object_path, caller=False, exception_to_raise=YANGPathHelperException):\n        obj = self.get(object_path, caller=caller)\n        if len(obj) == 0:\n            raise exception_to_raise(\"Supplied path for %s found no results!\" % object_path)\n        if len(obj) != 1:\n            raise exception_to_raise(\"Supplied path for %s was not unique!\" % object_path)\n        return obj[0]\n\n    def get_list(self, object_path, caller=False, exception_to_raise=YANGPathHelperException):\n        if isinstance(object_path, (str,)):\n            object_path = self._path_parts(object_path)\n\n        parent_obj = self.get_unique(object_path[:-1], caller=caller, exception_to_raise=exception_to_raise)\n\n        list_get_attr = getattr(parent_obj, \"_get_%s\" % safe_name(object_path[-1]), None)\n        if list_get_attr is None:\n            raise exception_to_raise(\n                \"Element %s does not have an attribute named %s\" % (\"/\".join(object_path[:-1]), object_path[-1])\n            )\n\n        return list_get_attr()\n\n    def tostring(self, pretty_print=False):\n        return etree.tostring(self._root, pretty_print=pretty_print)\n"
  },
  {
    "path": "pyangbind/lib/yangtypes.py",
    "content": "\"\"\"\nCopyright 2015, Rob Shakir (rjs@jive.com, rjs@rob.sh)\n\nModifications copyright 2016, Google Inc.\n\nThis project has been supported by:\n          * Jive Communications, Inc.\n          * BT plc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n\"\"\"\n\nfrom __future__ import unicode_literals\n\nimport base64\nimport collections\nfrom collections import abc\nimport copy\nimport uuid\nfrom decimal import Decimal\n\nimport regex\n\n# Words that could turn up in YANG definition files that are actually\n# reserved names in Python, such as being builtin types. This list is\n# not complete, but will probably continue to grow.\nreserved_name = [\n    \"list\",\n    \"str\",\n    \"int\",\n    \"global\",\n    \"decimal\",\n    \"float\",\n    \"as\",\n    \"if\",\n    \"else\",\n    \"elif\",\n    \"map\",\n    \"set\",\n    \"class\",\n    \"from\",\n    \"import\",\n    \"pass\",\n    \"return\",\n    \"is\",\n    \"exec\",\n    \"pop\",\n    \"insert\",\n    \"remove\",\n    \"add\",\n    \"delete\",\n    \"local\",\n    \"get\",\n    \"default\",\n    \"yang_name\",\n    \"def\",\n    \"print\",\n    \"del\",\n    \"break\",\n    \"continue\",\n    \"raise\",\n    \"in\",\n    \"assert\",\n    \"while\",\n    \"for\",\n    \"try\",\n    \"finally\",\n    \"with\",\n    \"except\",\n    \"lambda\",\n    \"or\",\n    \"and\",\n    \"not\",\n    \"yield\",\n    \"property\",\n    \"min\",\n    \"max\",\n    \"async\",\n]\n\n\ndef is_yang_list(arg):\n    if isinstance(arg, list):\n        return True\n    elif hasattr(arg, \"_pybind_generated_by\"):\n        pygen = getattr(arg, \"_pybind_generated_by\")\n        if pygen in [\"TypedListType\", \"YANGListType\"]:\n            return True\n    return False\n\n\ndef is_yang_leaflist(arg):\n    pygen = getattr(arg, \"_pybind_generated_by\", None)\n    if pygen is None:\n        return False\n    elif pygen == \"TypedListType\":\n        return True\n    return False\n\n\ndef remove_path_attributes(p):\n    new_path = []\n    for i in p:\n        if \"[\" in i:\n            new_path.append(i.split(\"[\")[0])\n        else:\n            new_path.append(i)\n    return new_path\n\n\ndef safe_name(arg):\n    \"\"\"\n    Make a leaf or container name safe for use in Python.\n    \"\"\"\n    arg = arg.replace(\"-\", \"_\")\n    arg = arg.replace(\".\", \"_\")\n    if arg in reserved_name:\n        arg += \"_\"\n    # store the unsafe->original version mapping\n    # so that we can retrieve it when get() is called.\n    return arg\n\n\ndef RestrictedPrecisionDecimalType(*args, **kwargs):\n    \"\"\"\n    Function to return a new type that is based on decimal.Decimal with\n    an arbitrary restricted precision.\n    \"\"\"\n    precision = kwargs.pop(\"precision\", False)\n\n    class RestrictedPrecisionDecimal(Decimal):\n        \"\"\"\n        Class extending decimal.Decimal to restrict the precision that is\n        stored, supporting the fraction-digits argument of the YANG decimal64\n        type.\n        \"\"\"\n\n        _precision = 10.0 ** (-1.0 * int(precision))\n        _pybind_generated_by = \"RestrictedPrecisionDecimal\"\n\n        def __new__(self, *args, **kwargs):\n            \"\"\"\n            Overloads the decimal __new__ function in order to round the input\n            value to the new value.\n            \"\"\"\n            if self._precision is not None:\n                if len(args):\n                    value = Decimal(args[0]).quantize(Decimal(str(self._precision)))\n                else:\n                    value = Decimal(0)\n            elif len(args):\n                value = Decimal(args[0])\n            else:\n                value = Decimal(0)\n            obj = Decimal.__new__(self, value, **kwargs)\n            return obj\n\n    return type(RestrictedPrecisionDecimal(*args, **kwargs))\n\n\ndef RestrictedClassType(*args, **kwargs):\n    \"\"\"\n    Function to return a new type that restricts an arbitrary base_type with\n    a specified restriction. The restriction_type specified determines the\n    type of restriction placed on the class, and the restriction_arg gives\n    any input data that this function needs.\n    \"\"\"\n    base_type = kwargs.pop(\"base_type\", str)\n    restriction_type = kwargs.pop(\"restriction_type\", None)\n    restriction_arg = kwargs.pop(\"restriction_arg\", None)\n    restriction_dict = kwargs.pop(\"restriction_dict\", None)\n    int_size = kwargs.pop(\"int_size\", None)\n\n    # this gives deserialisers some hints as to how to encode/decode this value\n    # it must be a list since a restricted class can encapsulate a restricted\n    # class\n    current_restricted_class_type = regex.sub(\"<(type|class) '(?P<class>.*)'>\", r\"\\g<class>\", str(base_type))\n    if hasattr(base_type, \"_restricted_class_base\"):\n        restricted_class_hint = getattr(base_type, \"_restricted_class_base\")\n        restricted_class_hint.append(current_restricted_class_type)\n    else:\n        restricted_class_hint = [current_restricted_class_type]\n\n    class RestrictedClass(base_type):\n        \"\"\"\n        A class that restricts the base_type class with a new function that the\n        input value is validated against before being applied. The function is\n        a static method which is assigned to _restricted_test.\n        \"\"\"\n\n        _pybind_generated_by = \"RestrictedClassType\"\n\n        _restricted_class_base = restricted_class_hint\n        _restricted_int_size = int_size\n\n        def __init__(self, *args, **kwargs):\n            \"\"\"\n            Overloads the base_class __init__ method to check the input argument\n            against the validation function - returns on instance of the base_type\n            class, which can be manipulated as per a usual Python object.\n            \"\"\"\n            try:\n                self.__check(args[0])\n            except IndexError:\n                pass\n            try:\n                super(RestrictedClass, self).__init__(*args, **kwargs)\n            except TypeError:\n                super(RestrictedClass, self).__init__()\n\n        def __new__(self, *args, **kwargs):\n            self._restriction_dict = restriction_dict\n            self._restriction_tests = []\n\n            \"\"\"\n        Create a new class instance, and dynamically define the\n        _restriction_test method so that it can be called by other functions.\n      \"\"\"\n\n            range_regex = regex.compile(r\"(?P<low>\\-?[0-9\\.]+|min)([ ]+)?\\.\\.([ ]+)?\" + r\"(?P<high>(\\-?[0-9\\.]+|max))\")\n            range_single_value_regex = regex.compile(r\"(?P<value>\\-?[0-9\\.]+)\")\n\n            def convert_regexp(pattern):\n                # Some patterns include a $ character in them in some IANA modules, this\n                # is not escaped. Do some logic to escape them, whilst leaving one at the\n                # end of the string if it's there.\n                trimmed = False\n                if pattern[-1] == \"$\":\n                    tmp_pattern = pattern[:-1]\n                    trimmed = True\n                else:\n                    tmp_pattern = pattern\n                tmp_pattern = tmp_pattern.replace(\"$\", r\"\\$\")\n                pattern = tmp_pattern\n                if trimmed:\n                    pattern += \"$\"\n\n                if not pattern[0] == \"^\":\n                    pattern = \"^%s\" % pattern\n                if not pattern[len(pattern) - 1] == \"$\":\n                    pattern = \"%s$\" % pattern\n\n                return pattern\n\n            def build_length_range_tuples(range, length=False, multiplier=1):\n                if range_regex.match(range_spec):\n                    low, high = range_regex.sub(r\"\\g<low>,\\g<high>\", range_spec).split(\",\")\n                    if not length:\n                        high = base_type(high) if not high == \"max\" else None\n                        low = base_type(low) if not low == \"min\" else None\n                    else:\n                        high = int(high) * multiplier if not high == \"max\" else None\n                        low = int(low) * multiplier if not low == \"min\" else None\n                    return (low, high)\n                elif range_single_value_regex.match(range_spec):\n                    eqval = range_single_value_regex.sub(r\"\\g<value>\", range_spec)\n                    if not length:\n                        eqval = base_type(eqval) if eqval not in [\"max\", \"min\"] else None\n                    else:\n                        eqval = int(eqval) * multiplier\n                    return (eqval,)\n                else:\n                    raise ValueError(\"Invalid range or length argument specified\")\n\n            def in_range_check(low_high_tuples, length=False):\n                def range_check(value):\n                    if length:\n                        value = len(value)\n                    range_results = []\n                    for check_tuple in low_high_tuples:\n                        chk = True\n                        if len(check_tuple) == 2:\n                            if check_tuple[0] is not None and value < check_tuple[0]:\n                                chk = False\n                            if check_tuple[1] is not None and value > check_tuple[1]:\n                                chk = False\n                        elif len(check_tuple) == 1:\n                            if value != float(check_tuple[0]):\n                                chk = False\n                        else:\n                            raise AttributeError(\"Invalid check tuple length specified\")\n                        range_results.append(chk)\n                    return True in range_results\n\n                return range_check\n\n            def match_pattern_check(regexp):\n                def mp_check(value):\n                    if regex.match(convert_regexp(regexp), str(value)):\n                        return True\n                    return False\n\n                return mp_check\n\n            def in_dictionary_check(dictionary):\n                return lambda i: str(i) in dictionary\n\n            val = False\n            try:\n                val = args[0]\n            except IndexError:\n                pass\n            if self._restriction_dict is None:\n                if restriction_type is not None and restriction_arg is not None:\n                    self._restriction_dict = {restriction_type: restriction_arg}\n                else:\n                    raise ValueError(\"must specify either a restriction dictionary or\" + \" a type and argument\")\n\n            for rtype, rarg in self._restriction_dict.items():\n                if rtype == \"pattern\":\n                    self._restriction_tests.append(match_pattern_check(rarg))\n                elif rtype == \"range\":\n                    ranges = []\n                    for range_spec in rarg:\n                        ranges.append(build_length_range_tuples(range_spec))\n                    self._restriction_tests.append(in_range_check(ranges))\n                    if val:\n                        try:\n                            val = base_type(val)\n                        except Exception:\n                            raise TypeError(\"must specify a numeric type for a range \" + \"argument\")\n                elif rtype == \"length\":\n                    multiplier = 1\n                    lengths = []\n                    for range_spec in rarg:\n                        lengths.append(build_length_range_tuples(range_spec, length=True, multiplier=multiplier))\n                    self._restriction_tests.append(in_range_check(lengths, length=True))\n                elif rtype == \"dict_key\":\n                    new_rarg = copy.deepcopy(rarg)\n                    for k in rarg:\n                        if k.startswith(\"@\"):\n                            new_rarg.pop(k, None)\n                    # populate enum values\n                    used_values = []\n                    for k in new_rarg:\n                        if \"value\" in new_rarg[k]:\n                            used_values.append(int(new_rarg[k][\"value\"]))\n                    c = 0\n                    for k in new_rarg:\n                        while c in used_values:\n                            c += 1\n                        if \"value\" not in new_rarg[k]:\n                            new_rarg[k][\"value\"] = c\n                        c += 1\n                    self._restriction_tests.append(in_dictionary_check(new_rarg))\n                    self._enumeration_dict = new_rarg\n                else:\n                    raise TypeError(\"unsupported restriction type\")\n\n            if val is not False:\n                for test in self._restriction_tests:\n                    passed = False\n                    if test(val) is not False:\n                        passed = True\n                        break\n                if not passed:\n                    raise ValueError(\"%s does not match a restricted type\" % val)\n\n            try:\n                obj = base_type.__new__(self, *args, **kwargs)\n            except TypeError:\n                obj = base_type.__new__(self)\n            return obj\n\n        def __check(self, v):\n            \"\"\"\n            Run the _restriction_test static method against the argument v,\n            returning an error if the value does not validate.\n            \"\"\"\n            v = base_type(v)\n            for chkfn in self._restriction_tests:\n                if not chkfn(v):\n                    raise ValueError(\"did not match restricted type\")\n            return True\n\n        def getValue(self, *args, **kwargs):\n            \"\"\"\n            For types where there is a dict_key restriction (such as YANG\n            enumeration), return the value of the dictionary key.\n            \"\"\"\n            if \"dict_key\" in self._restriction_dict:\n                value = kwargs.pop(\"mapped\", False)\n                if value:\n                    return self._enumeration_dict[self.__str__()][\"value\"]\n            return self\n\n    return type(RestrictedClass(*args, **kwargs))\n\n\ndef TypedListType(*args, **kwargs):\n    \"\"\"\n    Return a type that consists of a list object where only\n    certain types (specified by allowed_type kwarg to the function)\n    can be added to the list.\n    \"\"\"\n    allowed_type = kwargs.pop(\"allowed_type\", str)\n    if not isinstance(allowed_type, list):\n        allowed_type = [allowed_type]\n\n    class TypedList(abc.MutableSequence):\n        _pybind_generated_by = \"TypedListType\"\n        _list = list()\n\n        def __init__(self, *args, **kwargs):\n            self._unique = kwargs.pop(\"unique\", False)\n            self._allowed_type = allowed_type\n            self._list = list()\n            if len(args):\n                if isinstance(args[0], list):\n                    tmp = []\n                    for i in args[0]:\n                        if not self._unique or i not in tmp:\n                            tmp.append(self.check(i))\n                    self._list.extend(tmp)\n                else:\n                    tmp = self.check(args[0])\n                    self._list.append(tmp)\n\n        def check(self, v):\n            # Short circuit uniqueness check\n            if self._unique and v in self._list:\n                raise ValueError(\"Values in this list must be unique.\")\n\n            passed = False\n            count = 0\n            for i in self._allowed_type:\n                if isinstance(v, i):\n                    tmp = v\n                    passed = True\n                    break\n                try:\n                    if hasattr(i, \"_pybind_generated_by\"):\n                        attr = getattr(i, \"_pybind_generated_by\")\n                        if attr == \"RestrictedClassType\":\n                            tmp = i(v)\n                            passed = True\n                            break\n                        elif attr == \"ReferencePathType\":\n                            tmp = i(v)\n                            passed = True\n                            break\n                        elif attr == \"RestrictedPrecisionDecimal\":\n                            tmp = i(v)\n                            passed = True\n                            break\n                    elif i == str and isinstance(v, (str,)):\n                        tmp = str(v)\n                        passed = True\n                        break\n                    elif i not in (str,):\n                        # for anything other than string we try\n                        # and cast. Using things for string or\n                        # unicode gives us strange results because we get\n                        # class name represetnations\n                        tmp = i(v)\n                        passed = True\n                        break\n                except Exception:\n                    # we catch all exceptions because we duck-type as\n                    # much as possible and some types - e.g., decimal do\n                    # not use builtins.\n                    pass\n                count += 1\n            if not passed:\n                raise ValueError(\"Cannot add %s to TypedList (accepts only %s)\" % (v, self._allowed_type))\n            else:\n                return tmp\n\n        def __len__(self):\n            return len(self._list)\n\n        def __getitem__(self, i):\n            return self._list[i]\n\n        def __delitem__(self, i):\n            del self._list[i]\n\n        def __setitem__(self, i, v):\n            self.insert(i, v)\n\n        def insert(self, i, v):\n            val = self.check(v)\n            self._list.insert(i, val)\n\n        def append(self, v):\n            if not self._unique or v not in self._list:\n                val = self.check(v)\n                self._list.append(val)\n\n        def __str__(self):\n            return str(self._list)\n\n        def __iter__(self):\n            return iter(self._list)\n\n        def __eq__(self, other):\n            if self._list == other:\n                return True\n            return False\n\n        def get(self, filter=False):\n            return self._list\n\n    return type(TypedList(*args, **kwargs))\n\n\ndef YANGListType(*args, **kwargs):\n    \"\"\"\n    Return a type representing a YANG list, with a contained class.\n    An ordered dict is used to store the list, and the\n    returned object behaves akin to a dictionary.\n\n    Additional checks are performed to ensure that the keys of the\n    list are valid before adding the value.\n\n    .add(key) - initialises a new member of the list\n    .delete(key) - removes it.\n\n    Where a list exists that does not have a key - which can be the\n    case for 'config false' lists - a uuid is generated and used\n    as the key for the list.\n    \"\"\"\n    try:\n        keyname = args[0]\n        listclass = args[1]\n    except Exception:\n        raise TypeError(\"A YANGList must be specified with a key value and a \" + \"contained class\")\n    is_container = kwargs.pop(\"is_container\", False)\n    parent = kwargs.pop(\"parent\", False)\n    yang_name = kwargs.pop(\"yang_name\", False)\n    yang_keys = kwargs.pop(\"yang_keys\", False)\n    user_ordered = kwargs.pop(\"user_ordered\", False)\n    path_helper = kwargs.pop(\"path_helper\", None)\n    extensions = kwargs.pop(\"extensions\", None)\n\n    class YANGList(object):\n        __slots__ = (\"_members\", \"_keyval\", \"_contained_class\", \"_path_helper\", \"_yang_keys\", \"_ordered\")\n        _pybind_generated_by = \"YANGListType\"\n\n        def __init__(self, *args, **kwargs):\n            self._ordered = True if user_ordered else False\n            self._members = collections.OrderedDict()\n\n            self._members._user_ordered = True if user_ordered else False\n\n            self._keyval = keyname\n            if not type(listclass) == type(int):\n                raise ValueError(\"contained class of a YANGList must be a class\")\n            self._contained_class = listclass\n            self._path_helper = path_helper\n            self._yang_keys = yang_keys\n\n        def __str__(self):\n            return str(self._members)\n\n        def __repr__(self):\n            return repr(self._members)\n\n        def __check__(self, v):\n            if self._contained_class is None:\n                return False\n            try:\n                tmp = YANGDynClass(\n                    base=self._contained_class,\n                    parent=parent,\n                    yang_name=yang_name,\n                    is_container=is_container,\n                    path_helper=False,\n                )\n                valid = False\n                if not tmp.__slots__ == v.__slots__:\n                    valid = True\n                elif self._contained_class.__slots__ == v.__slots__:\n                    valid = True\n                if valid is False:\n                    return valid\n            except Exception:\n                return False\n            return True\n\n        def iteritems(self):\n            return self._members.items()\n\n        def itervalues(self):\n            return self._members.values()\n\n        def _key_to_native_key_type(self, k):\n            if self._keyval is False:\n                raise AttributeError(\"List does not have a key\")\n            elif \" \" in self._keyval:\n                raise AttributeError(\"Multiple key, string type should be used\")\n            else:\n                member = self._members[k]\n                getfn = getattr(member, \"_get_%s\" % self._keyval)\n                return getfn()\n\n        def __iter__(self):\n            return iter(self._members)\n\n        def __contains__(self, k):\n            if k in self._members:\n                return True\n            return False\n\n        def __getitem__(self, k):\n            return self._members[k]\n\n        def __setitem__(self, k, v):\n            self.__set(_k=k, _v=v)\n\n        def __set(self, *args, **kwargs):\n            k = kwargs.pop(\"_k\", None)\n            v = kwargs.pop(\"_v\", None)\n            named_set = kwargs.pop(\"_named_set\", False)\n\n            if k is None and self._keyval and not named_set:\n                k = args[0]\n            elif k is None:\n                # this is a list that does not have a key specified, and hence\n                # we generate a uuid that is used as the key, the method then\n                # returns the uuid for the upstream process to use\n                k = str(uuid.uuid1())\n\n            update = False\n            if v is not None:\n                if not self.__check__(v):\n                    raise ValueError(\"value must be set to an instance of %s\" % (self._contained_class))\n                else:\n                    update = True\n\n            if k in self._members:\n                update = True\n\n            if self._keyval:\n                try:\n                    tmp = YANGDynClass(\n                        base=self._contained_class,\n                        parent=parent,\n                        yang_name=yang_name,\n                        is_container=\"container\",\n                        path_helper=False,\n                    )\n                    keydict = None\n\n                    if \" \" in self._keyval and not named_set:\n                        keys = self._keyval.split(\" \")\n                        keyparts = k.split(\" \")\n                        keydict = {}\n                        for kp, kv in zip(keys, keyparts):\n                            keydict[kp] = kv\n\n                        if not len(keyparts) == len(keys):\n                            raise KeyError(\n                                \"YANGList key must contain all key elements (%s)\" % (self._keyval.split(\" \"))\n                            )\n                    elif named_set:\n                        k = kwargs.pop(\"_python_key\", None)\n                        keydict = copy.copy(kwargs)\n                    else:\n                        if k == \"\":\n                            raise KeyError(\"Cannot set a null key for a list entry!\")\n                        keydict = {self._keyval: k}\n                        kv_obj = getattr(tmp, self._keyval)\n                        path_keystring = \"[%s='%s']\" % (kv_obj.yang_name(), k)\n\n                    if keydict is not None:\n                        keys = self._keyval.split(\" \")\n                        path_keystring = \"[\"\n                        for kv in keys:\n                            kv_obj = getattr(tmp, kv)\n                            path_keystring += \"%s='%s' \" % (kv_obj.yang_name(), keydict[kv])\n                        path_keystring = path_keystring[:-1]\n                        path_keystring += \"]\"\n\n                    if not update:\n                        tmp = YANGDynClass(\n                            base=self._contained_class,\n                            parent=parent,\n                            yang_name=yang_name,\n                            is_container=\"container\",\n                            path_helper=path_helper,\n                            register_path=(self._parent._path() + [self._yang_name + path_keystring]),\n                            extmethods=self._parent._extmethods,\n                            extensions=extensions,\n                        )\n                    else:\n                        # hand the value to the init, rather than simply creating an empty\n                        # object.\n                        tmp = YANGDynClass(\n                            v,\n                            base=self._contained_class,\n                            parent=parent,\n                            yang_name=yang_name,\n                            is_container=\"container\",\n                            path_helper=path_helper,\n                            register_path=(self._parent._path() + [self._yang_name + path_keystring]),\n                            extmethods=self._parent._extmethods,\n                            load=True,\n                            extensions=extensions,\n                        )\n\n                    if keydict is not None:\n                        for kn in keydict:\n                            key = getattr(tmp, \"_set_%s\" % safe_name(kn))\n                            key(keydict[kn], load=True)\n\n                    if hasattr(k, \"_referenced_object\") and k._referenced_object is not None:\n                        k = k._referenced_object\n\n                    self._members[k] = tmp\n\n                except ValueError as m:\n                    raise KeyError(\"key value %s must be valid, %s\" % (self._keyval, m))\n            else:\n                self._members[k] = YANGDynClass(\n                    base=self._contained_class,\n                    parent=parent,\n                    yang_name=yang_name,\n                    is_container=is_container,\n                    path_helper=path_helper,\n                    extmethods=self._parent._extmethods,\n                    extensions=extensions,\n                )\n                return k\n\n        def __delitem__(self, k):\n            del self._members[k]\n\n        def __len__(self):\n            return len(self._members)\n\n        def keys(self):\n            return self._members.keys()\n\n        def items(self):\n            return self._members.items()\n\n        def values(self):\n            return self._members.values()\n\n        def _generate_key(self, *args, **kwargs):\n            keyargs = None\n            if len(args):\n                k = args[0]\n            elif len(kwargs):\n                keyargs = {}\n                k = \"\"\n                for kn in self._keyval.split(\" \"):\n                    try:\n                        keyargs[kn] = kwargs[kn]\n                    except KeyError as m:\n                        raise AttributeError(\n                            \"Keyword list add function must have all \" + \"keys specified - cannot find %s\" % m\n                        )\n                    k += \"%s \" % kwargs[kn]\n                k = k[:-1]\n            else:\n                k = None\n            return (k, keyargs)\n\n        def _extract_key(self, obj):\n            kp = self._keyval.split(\" \")\n            if len(kp) > 1:\n                ks = \"\"\n                for k in kp:\n                    kv = getattr(obj, \"_get_%s\" % safe_name(k), None)\n                    if kv is None:\n                        raise KeyError(\"Invalid key attribute specified for object\")\n                    ks += \"%s \" % str(kv())\n                return ks.rstrip(\" \")\n            else:\n                kv = getattr(obj, \"_get_%s\" % safe_name(self._keyval), None)\n                if kv is None:\n                    raise KeyError(\"Invalid key attribute specified for object: %s\" % self._keyval)\n                return kv()\n\n        def append(self, obj):\n            self.__set(_k=self._extract_key(obj), _v=obj)\n\n        def _new_item(self):\n            return self._contained_class()\n\n        def add(self, *args, **kwargs):\n            if len(args) and len(kwargs):\n                raise AttributeError(\"Cannot add an entry to a list based on both \" + \" keywords and string args\")\n\n            value = kwargs.pop(\"_v\", None)\n\n            k, keyargs = self._generate_key(*args, **kwargs)\n\n            if k in self._members:\n                raise KeyError(\"%s is already defined as a list entry\" % k)\n            if self._keyval and keyargs is None:\n                if k is None:\n                    raise KeyError(\"a list with a key value must have a key specified\")\n                self.__set(_k=k)\n                return self._members[k]\n            elif self._keyval and keyargs is not None:\n                keyargs[\"_python_key\"] = k\n                keyargs[\"_named_set\"] = True\n                if value is not None:\n                    keyargs[\"_v\"] = value\n                self.__set(**keyargs)\n                return self._members[k]\n            else:\n                k = self.__set()\n                return k\n\n        def delete(self, *args, **kwargs):\n            k, _ = self._generate_key(*args, **kwargs)\n\n            if self._path_helper:\n                current_item = self._members[k]\n                if \" \" in self._keyval:\n                    keyparts = self._keyval.split(\" \")\n                    keyargs = k.split(\" \")\n                    key_string = \"[\"\n                    for key, val in zip(keyparts, keyargs):\n                        kv_o = getattr(current_item, key)\n                        key_string += \"%s=%s \" % (kv_o.yang_name(), val)\n                    key_string = key_string.rstrip(\" \")\n                    key_string += \"]\"\n                else:\n                    kv_o = getattr(current_item, self._keyval)\n                    key_string = \"[@%s=%s]\" % (kv_o.yang_name(), k)\n\n                obj_path = self._parent._path() + [self._yang_name + key_string]\n\n            try:\n                del self._members[k]\n                if self._path_helper:\n                    self._path_helper.unregister(obj_path)\n            except KeyError as m:\n                raise KeyError(\"key %s was not in list (%s)\" % (k, m))\n\n        def _item(self, *args, **kwargs):\n            keystr = \"\"\n            if \" \" in self._keyval:\n                keyparts = self._keyval.split(\" \")\n            else:\n                keyparts = [self._keyval]\n            for kn in keyparts:\n                try:\n                    keystr += \"%s \" % kwargs[kn]\n                except KeyError:\n                    raise KeyError(\"Must specify all keys to retrieve a list entry\")\n            keystr = keystr[:-1]\n\n            return self._members[keystr]\n\n        def get(self, filter=False):\n            d = collections.OrderedDict()\n            d._user_ordered = self._members._user_ordered\n            for i in self._members:\n                if hasattr(self._members[i], \"get\"):\n                    d[i] = self._members[i].get(filter=filter)\n                else:\n                    d[i] = self._members[i]\n            return d\n\n    return type(YANGList(*args, **kwargs))\n\n\nclass YANGBool(int):\n    \"\"\"\n    A custom boolean class for using in YANG. Since bool has specific\n    logic in python, it is not possible to extend the existing bool\n    objects.\n\n    This bool also accepts input matching strings to handle the\n    forms that might be used in YANG modules.\n    \"\"\"\n\n    def __new__(self, *args, **kwargs):\n        false_args = [\"false\", \"False\", False, 0, \"0\"]\n        true_args = [\"true\", \"True\", True, 1, \"1\"]\n        if len(args):\n            if not args[0] in false_args + true_args:\n                raise ValueError(\"%s is an invalid value for a YANGBool\" % args[0])\n            value = 0 if args[0] in false_args else 1\n        else:\n            value = 0\n        return int.__new__(self, bool(value))\n\n    def __repr__(self):\n        return str([False, True][self])\n\n    def __str__(self):\n        return str(self.__repr__())\n\n\ndef YANGDynClass(*args, **kwargs):\n    \"\"\"\n    Wrap an type - specified in the base_type arugment - with\n    a set of custom attributes that YANG specifies (or are required\n    for serialisation of that object). Particularly:\n\n      - base_type:  the original type - for example, string, int.\n      - default:    the YANG specified default value of the type.\n      - yang_name:  the YANG name of the type (as opposed to a 'safe'\n                    Python version).\n      - parent:     the class which this type is a member of in the\n                    YANG-specified tree.\n      - choice:     The choice branch that this type is a member of.\n      - is_{container,leaf}: whether this element is a container or\n                             a leaf.\n      - path_helper: pyangbind helper class to allow XPATH lookups.\n      - supplied_register_path: an override for the path that this\n                                object should register to. This is\n                                used when an element is a member of\n                                a list to add the key attributes to\n                                the path.\n      - extensions:  The list of extensions that should be stored\n                     with the type.\n      - is_config:   Whether this is a configuration (editable)\n                     node.\n      - presence:    Whether the YANG container that is being\n                     represented has the presence keyword\n    \"\"\"\n    base_type = kwargs.pop(\"base\", False)\n    default = kwargs.pop(\"default\", False)\n    yang_name = kwargs.pop(\"yang_name\", False)\n    parent_instance = kwargs.pop(\"parent\", False)\n    choice_member = kwargs.pop(\"choice\", False)\n    is_container = kwargs.pop(\"is_container\", False)\n    is_leaf = kwargs.pop(\"is_leaf\", False)\n    path_helper = kwargs.pop(\"path_helper\", None)\n    supplied_register_path = kwargs.pop(\"register_path\", None)\n    extensions = kwargs.pop(\"extensions\", None)\n    extmethods = kwargs.pop(\"extmethods\", None)\n    is_keyval = kwargs.pop(\"is_keyval\", False)\n    register_paths = kwargs.pop(\"register_paths\", True)\n    yang_type = kwargs.pop(\"yang_type\", None)\n    namespace = kwargs.pop(\"namespace\", None)\n    defining_module = kwargs.pop(\"defining_module\", None)\n    load = kwargs.pop(\"load\", None)\n    is_config = kwargs.pop(\"is_config\", True)\n    has_presence = kwargs.pop(\"presence\", None)\n\n    if not base_type:\n        raise TypeError(\"must have a base type\")\n\n    if isinstance(base_type, list):\n        # this is a union, we must infer type\n        if not len(args):\n            # there is no argument to infer the type from\n            # so use the first type (default)\n            base_type = base_type[0]\n        else:\n            type_test = False\n            for candidate_type in base_type:\n                try:\n                    type_test = candidate_type(args[0])  # does the slipper fit?\n                    break\n                except Exception:\n                    pass  # don't worry, move on, plenty more fish (types) in the sea...\n            if type_test is False:\n                # we're left alone at midnight -- no types fit the arguments\n                raise TypeError(\"did not find a valid type using the argument as a\" + \" hint\")\n            # otherwise, hop, skip and jump with the last candidate\n            base_type = candidate_type\n\n    clsslots = [\n        \"_default\",\n        \"_mchanged\",\n        \"_yang_name\",\n        \"_choice\",\n        \"_parent\",\n        \"_supplied_register_path\",\n        \"_path_helper\",\n        \"_base_type\",\n        \"_is_leaf\",\n        \"_is_container\",\n        \"_extensionsd\",\n        \"_extmethods\",\n        \"_is_keyval\",\n        \"_register_paths\",\n        \"_namespace\",\n        \"_yang_type\",\n        \"_defining_module\",\n        \"_metadata\",\n        \"_is_config\",\n        \"_cpresent\",\n        \"_presence\",\n    ]\n\n    if extmethods:\n        rpath = None\n        if supplied_register_path is not None:\n            rpath = supplied_register_path\n        if parent_instance is not None:\n            rpath = parent_instance._path() + [yang_name]\n        else:\n            rpath = []\n        chk_path = \"/\" + \"/\".join(remove_path_attributes(rpath))\n        if chk_path in extmethods:\n            for method in [i for i in dir(extmethods[chk_path]) if not i.startswith(\"_\")]:\n                clsslots.append(\"_\" + method)\n\n    class YANGBaseClass(base_type):\n        # we only create slots for things that are restricted\n        # in adding attributes to them - this means containing\n        # data nodes. This means that we can allow\n        # leaf._someattr to be used by consuming code - it\n        # also fixes an issue whereby we could set __slots__\n        # and try and inherit a variable-length inbuilt such\n        # as long, which is not allowed.\n        if yang_type in [\"container\", \"list\"] or is_container == \"container\":\n            __slots__ = tuple(clsslots)\n\n        _pybind_base_class = regex.sub(\"<(type|class) '(?P<class>.*)'>\", r\"\\g<class>\", str(base_type))\n\n        def __new__(self, *args, **kwargs):\n            try:\n                obj = base_type.__new__(self, *args, **kwargs)\n            except TypeError:\n                obj = base_type.__new__(self)\n            return obj\n\n        def __init__(self, *args, **kwargs):\n            self._default = False\n            self._mchanged = False\n            self._yang_name = yang_name\n            self._parent = parent_instance\n            self._choice = choice_member\n            self._path_helper = path_helper\n            self._supplied_register_path = supplied_register_path\n            self._base_type = base_type\n            self._is_leaf = is_leaf\n            self._is_container = is_container\n            self._is_config = is_config\n            self._extensionsd = extensions\n            self._extmethods = extmethods\n            self._is_keyval = is_keyval\n            self._register_paths = register_paths\n            self._namespace = namespace\n            self._yang_type = yang_type\n            self._defining_module = defining_module\n            self._metadata = {}\n            self._presence = has_presence\n            self._cpresent = False\n\n            if self._extmethods:\n                chk_path = \"/\" + \"/\".join(remove_path_attributes(self._register_path()))\n                if chk_path in self._extmethods:\n                    for method in [i for i in dir(self._extmethods[chk_path]) if not i.startswith(\"_\")]:\n                        # Don't allow methods to be overwritten\n                        if hasattr(self, \"_\" + method):\n                            continue\n                        member = getattr(self._extmethods[chk_path], method)\n                        if hasattr(member, \"__call__\"):\n                            if not hasattr(self, \"_method\"):\n                                setattr(self, \"_\" + method, self.__generate_extmethod(member))\n\n            if default:\n                self._default = default\n            if len(args):\n                self._set()\n\n            # lists themselves do not register, only elements within them\n            # are actually created in the tree.\n            if not self._is_container == \"list\":\n                if self._path_helper:\n                    if self._supplied_register_path is None:\n                        if self._register_paths:\n                            self._path_helper.register(self._register_path(), self)\n                    else:\n                        if self._register_paths:\n                            self._path_helper.register(self._supplied_register_path, self)\n\n            if self._is_container == \"list\" or self._is_container == \"container\":\n                kwargs[\"path_helper\"] = self._path_helper\n                if load is not None:\n                    kwargs[\"load\"] = load\n\n            try:\n                super(YANGBaseClass, self).__init__(*args, **kwargs)\n            except TypeError:\n                super(YANGBaseClass, self).__init__()\n            except Exception:\n                raise\n\n        def _changed(self):\n            return self._mchanged\n\n        def _extensions(self):\n            return self._extensionsd\n\n        def _path(self):\n            return self._register_path()\n\n        def _yang_path(self):\n            return \"/\" + \"/\".join(self._register_path())\n\n        def __str__(self):\n            return super(YANGBaseClass, self).__str__()\n\n        def __repr__(self):\n            return super(YANGBaseClass, self).__repr__()\n\n        def _set(self, choice=False):\n            if hasattr(self, \"__choices__\") and choice:\n                for ch in self.__choices__:\n                    if ch == choice[0]:\n                        for case in self.__choices__[ch]:\n                            if not case == choice[1]:\n                                for elem in self.__choices__[ch][case]:\n                                    method = \"_unset_%s\" % elem\n                                    if not hasattr(self, method):\n                                        raise AttributeError(\"unmapped choice!\")\n                                    x = getattr(self, method)\n                                    x()\n\n            if self._choice and not choice:\n                choice = self._choice\n\n            self._mchanged = True\n\n            if self._presence:\n                self._cpresent = True\n\n            if self._parent and hasattr(self._parent, \"_set\"):\n                self._parent._set(choice=choice)\n\n        def _add_metadata(self, k, v):\n            self._metadata[k] = v\n\n        def yang_name(self):\n            return self._yang_name\n\n        def default(self):\n            return self._default\n\n        # we need to overload the set methods\n        def __setitem__(self, *args, **kwargs):\n            self._set()\n            super(YANGBaseClass, self).__setitem__(*args, **kwargs)\n\n        def append(self, *args, **kwargs):\n            if not hasattr(super(YANGBaseClass, self), \"append\"):\n                raise AttributeError(\"%s object has no attribute append\" % base_type)\n            self._set()\n            super(YANGBaseClass, self).append(*args, **kwargs)\n\n        def pop(self, *args, **kwargs):\n            if not hasattr(super(YANGBaseClass, self), \"pop\"):\n                raise AttributeError(\"%s object has no attribute pop\" % base_type)\n            self._set()\n            item = super(YANGBaseClass, self).pop(*args, **kwargs)\n            return item\n\n        def remove(self, *args, **kwargs):\n            if not hasattr(super(YANGBaseClass, self), \"remove\"):\n                raise AttributeError(\"%s object has no attribute remove\" % base_type)\n            self._set()\n            if self._path_helper:\n                elem_index = super(YANGBaseClass, self).index(*args, **kwargs)\n                super(YANGBaseClass, self).__getitem__(elem_index)\n            super(YANGBaseClass, self).remove(*args, **kwargs)\n\n        def extend(self, *args, **kwargs):\n            if not hasattr(super(YANGBaseClass, self), \"extend\"):\n                raise AttributeError(\"%s object has no attribute extend\" % base_type)\n            self._set()\n            super(YANGBaseClass, self).extend(*args, **kwargs)\n\n        def insert(self, *args, **kwargs):\n            if not hasattr(super(YANGBaseClass, self), \"insert\"):\n                raise AttributeError(\"%s object has no attribute insert\" % base_type)\n            self._set()\n            super(YANGBaseClass, self).insert(*args, **kwargs)\n\n        def _register_path(self):\n            if self._supplied_register_path is not None:\n                return self._supplied_register_path\n            if self._parent is not None:\n                return self._parent._path() + [self._yang_name]\n            else:\n                return []\n\n        def __generate_extmethod(self, methodfn):\n            def extmethodfn(*args, **kwargs):\n                kwargs[\"caller\"] = self._register_path()\n                kwargs[\"path_helper\"] = self._path_helper\n                return methodfn(*args, **kwargs)\n\n            return extmethodfn\n\n        def _set_present(self, present=True):\n            if not self._is_container == \"container\":\n                raise AttributeError(\"Cannot set presence on a non-container\")\n            self._cpresent = present\n            if present is True:\n                self._set()\n            if present is False:\n                self._mchanged = False\n\n        def _present(self):\n            if not self._is_container == \"container\":\n                return None\n\n            if self._presence is False:\n                return None\n\n            return self._cpresent\n\n    return YANGBaseClass(*args, **kwargs)\n\n\ndef ReferenceType(*args, **kwargs):\n    \"\"\"\n    A type which based on a path provided acts as a leafref.\n    The caller argument is used to allow the path that is provided\n    to be a relative (rather than absolute) path. The require_instance\n    argument specifies whether errors should be thrown in the case\n    that the referenced instance does not exist.\n    \"\"\"\n    ref_path = kwargs.pop(\"referenced_path\", False)\n    path_helper = kwargs.pop(\"path_helper\", None)\n    caller = kwargs.pop(\"caller\", False)\n    require_instance = kwargs.pop(\"require_instance\", False)\n\n    class ReferencePathType(object):\n        __slots__ = (\n            \"_referenced_path\",\n            \"_path_helper\",\n            \"_caller\",\n            \"_referenced_object\",\n            \"_ptr\",\n            \"_require_instance\",\n            \"_type\",\n            \"_utype\",\n        )\n\n        _pybind_generated_by = \"ReferencePathType\"\n\n        def __init__(self, *args, **kwargs):\n            self._referenced_path = ref_path\n            self._path_helper = path_helper\n            self._referenced_object = False\n            self._caller = caller\n            self._ptr = False\n            self._require_instance = require_instance\n            self._type = \"unicode\"\n            self._utype = str\n\n            if len(args):\n                value = args[0]\n                if hasattr(self, \"_set\"):\n                    self._set()\n            else:\n                value = None\n\n            if self._path_helper and value is not None:\n                path_chk = self._path_helper.get(self._referenced_path, caller=self._caller)\n\n                # if the lookup returns only one leaf, then this means that we have\n                # something that could potentially be a pointer. However, this is not\n                # sufficient to tell whether it is (it could be a single list entry)\n                # - thus perform two additional checks. 1) check whether this is the\n                # key value of a list (if it is then this is something that can be\n                # externally referenced) and 2) check that this is not\n                # a list itself (including a leaf-list)\n                if len(path_chk) == 1 and not path_chk[0]._is_keyval and not is_yang_list(path_chk[0]):\n                    # we are not checking whether this leaf exists, but rather\n                    # this is a pointer to some other value.\n                    path_parts = self._referenced_path.split(\"/\")\n                    leaf_name = path_parts[-1]\n                    if \":\" in leaf_name:\n                        # normalise the namespace\n                        leaf_name = leaf_name.split(\":\")[1]\n                    set_method = getattr(path_chk[0]._parent, \"_set_%s\" % safe_name(leaf_name))\n                    get_method = getattr(path_chk[0]._parent, \"_get_%s\" % safe_name(leaf_name))\n\n                    if value is not None:\n                        set_method(value)\n                    self._type = regex.sub(\n                        r\"<(type|class) '(?P<class>.*)'>\", r\"\\g<class>\", str(get_method()._base_type)\n                    )\n\n                    self._utype = get_method()._base_type\n                    self._ptr = True\n                elif self._require_instance:\n                    if value is None:\n                        self._referenced_object = None\n                    else:\n                        found = False\n                        path_chk = self._path_helper.get(self._referenced_path, caller=self._caller)\n\n                        if len(path_chk) == 1 and is_yang_leaflist(path_chk[0]):\n                            index = 0\n                            for i in path_chk[0]:\n                                if str(i) == str(value):\n                                    found = True\n                                    self._referenced_object = path_chk[0][index]\n                                    break\n                                index += 1\n                        else:\n                            found = False\n                            for i in path_chk:\n                                if str(i) == str(value):\n                                    found = True\n                                    self._referenced_object = i\n\n                        if not found:\n                            raise ValueError(\n                                \"no such key (%s) existed in path (%s -> %s)\"\n                                % (value, self._referenced_path, path_chk)\n                            )\n                else:\n                    # require instance is not set, so act like the referenced type\n                    self._referenced_object = value\n            elif value is not None:\n                # No path helper and a value is set, just act like the referenced type\n                self._referenced_object = value\n\n        def _get_ptr(self):\n            if self._ptr:\n                ptr = self._path_helper.get(self._referenced_path, caller=self._caller)\n                if len(ptr) == 1:\n                    return ptr[0]\n            raise ValueError(\"Invalid pointer specified\")\n\n        def __repr__(self):\n            if not self._ptr:\n                return repr(self._referenced_object)\n            return repr(self._get_ptr())\n\n        def _get(self):\n            if not self._ptr:\n                return self._referenced_object\n            return self._get_ptr()\n\n        def __str__(self):\n            if not self._ptr:\n                return str(self._referenced_object)\n            return str(self._get_ptr())\n\n    return type(ReferencePathType(*args, **kwargs))\n\n\nclass YANGBinary(bytes):\n    \"\"\"\n    A custom binary class for using in YANG.\n    \"\"\"\n\n    def __new__(self, *args, **kwargs):\n        value = b\"\"\n        if args:\n            value = args[0]\n            if isinstance(value, str):\n                value = base64.b64decode(value)\n            elif isinstance(value, bytes):\n                value = value\n            else:\n                raise ValueError(f\"invalid type for {value}: {type(value)}\")\n\n        return bytes.__new__(self, value)\n\n    def __repr__(self):\n        return str(self)\n\n    def __str__(self, encoding=\"ascii\", errors=\"replace\"):\n        return str(self, encoding=encoding, errors=errors)\n\n\ndef YANGBitsType(allowed_bits):\n    class YANGBits(set):\n        \"\"\"Map the ``bits`` built-in type of YANG\n\n        From RFC 6020, 9.7:\n\n        > The bits built-in type represents a bit set. That is, a bits value\n        > is a set of flags identified by small integer position numbers\n        > starting at 0. Each bit number has an assigned name.\n        >\n        > ... In the canonical form, the bit values are separated by a single\n        > space character and they appear ordered by their position.\n\n        __init__ parses such a string, and __str__() prints the value as\n        above. In the Python model, the bits that are set are kept as a set\n        of strings.\n\n        Override the ``add()``, ``discard()`` and similar methods to set and\n        clear the bits, checking for legal values.\n\n        :param allowed_bits: dictionary of legal bit values and positions; it\n            is set when generating the YANG-Python type mapping, when\n            ``pyangbing.lib.pybing.build_elemtype`` recurses over the YANG model\n        \"\"\"\n\n        _pybind_generated_by = \"YANGBits\"\n\n        def __init__(self, *args, **kwargs):\n            super().__init__()\n            self._allowed_bits = allowed_bits\n\n            if args:\n                value = args[0]\n                for bit in value.split():\n                    self.add(bit)\n\n        def _add_bit_definition(self, bit, position):\n            self._allowed_bits[bit] = position\n\n        # overwrite set methods to 1/ check for legal values and 2/ set the\n        # changed flag\n        def add(self, bit):\n            if bit not in self._allowed_bits:\n                raise ValueError(f\"Bit value {bit} not valid, expected one of {self._allowed_bits}\")\n            super().add(bit)\n            self._mchanged = True\n\n        def clear(self):\n            super().clear()\n            self._mchanged = True\n\n        def discard(self, bit):\n            if bit not in self._allowed_bits:\n                raise ValueError(f\"Bit value {bit} not valid, expected one of {self._allowed_bits}\")\n            super().discard(bit)\n            self._mchanged = True\n\n        def pop(self):\n            super().pop()\n            self._mchanged = True\n\n        def remove(self, bit):\n            super().remove(bit)\n            self._mchanged = True\n\n        def __str__(self, encoding=\"ascii\", errors=\"replace\"):\n            \"\"\"Return bits as shown in JSON.\"\"\"\n            sort_key = self._allowed_bits.__getitem__\n            return \" \".join(sorted(self, key=sort_key))\n\n    return YANGBits\n"
  },
  {
    "path": "pyangbind/plugin/__init__.py",
    "content": ""
  },
  {
    "path": "pyangbind/plugin/pybind.py",
    "content": "\"\"\"\nCopyright 2015, Rob Shakir (rjs@jive.com, rjs@rob.sh)\n\nModifications copyright 2016, Google Inc.\n\nThis project has been supported by:\n          * Google, Inc.\n          * Jive Communications, Inc.\n          * BT plc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n\"\"\"\n\nfrom __future__ import unicode_literals\n\nimport copy\nimport decimal\nimport optparse\nimport os\nimport sys\nfrom collections import OrderedDict\n\nfrom pyang import plugin, statements, util\n\nimport pyangbind.helpers.misc as misc_help\nfrom pyangbind.helpers.identity import IdentityStore\nfrom pyangbind.lib.yangtypes import RestrictedClassType, YANGBool, safe_name, YANGBinary, YANGBitsType\n\nlong = int\n\nDEBUG = True\nif DEBUG:\n    import pprint\n\n    pp = pprint.PrettyPrinter(indent=2)\n\n# YANG is quite flexible in terms of what it allows as input to a boolean\n# value, this map is used to provide a mapping of these values to the python\n# True and False boolean instances.\nclass_bool_map = {\"false\": False, \"False\": False, \"true\": True, \"True\": True}\n\nclass_map = {\n    # this map is dynamically built upon but defines how we take\n    # a YANG type  and translate it into a native Python class\n    # along with other attributes that are required for this mapping.\n    #\n    # key:                the name of the YANG type\n    # native_type:        the Python class that is used to support this\n    #                     YANG type natively.\n    # map (optional):     a map to take input values and translate them\n    #                     into valid values of the type.\n    # base_type:          whether the class can be used as\n    #                     class(*args, **kwargs) in Python, or whether it is a\n    #                     derived class (such as is created based on a typedef,\n    #                     or for types that cannot be supported natively, such\n    #                     as enumeration, or a string\n    #                     with a restriction placed on it)\n    # quote_arg (opt):    whether the argument to this class' __init__ needs to\n    #                     be quoted (e.g., str(\"hello\")) in the code that is\n    #                     output.\n    # pytype (opt):       A reference to the actual type that is used, this is\n    #                     used where we infer types, such as for an input\n    #                     value to a union since we need to actually compare\n    #                     the value against the __init__ method and see whether\n    #                     it works.\n    # parent_type (opt):  for \"derived\" types, then we store what the enclosed\n    #                     type is such that we can create instances where\n    #                     required e.g., a restricted string will have a\n    #                     parent_type of a string. this can be a list if the\n    #                     type is a union.\n    # restriction ...:    where the type is a restricted type, then the\n    # (optional)          class_map dict entry can store more information about\n    #                     the type of restriction. this is generally used when\n    #                     we need to re-initialise an instance of the class,\n    #                     such as in the setter methods of containers.\n    # Other types may add their own types to this dictionary that have\n    # meaning only for themselves. For example, a ReferenceType can add the\n    # path it references, and whether the require-instance keyword was set\n    # or not.\n    #\n    \"boolean\": {\n        \"native_type\": \"YANGBool\",\n        \"map\": class_bool_map,\n        \"base_type\": True,\n        \"quote_arg\": True,\n        \"pytype\": YANGBool,\n    },\n    \"binary\": {\"native_type\": \"YANGBinary\", \"base_type\": True, \"quote_arg\": True, \"pytype\": YANGBinary},\n    \"uint8\": {\n        \"native_type\": (\"RestrictedClassType(base_type=int,\" + \" restriction_dict={'range': ['0..255']}, int_size=8)\"),\n        \"base_type\": True,\n        \"pytype\": RestrictedClassType(base_type=int, restriction_dict={\"range\": [\"0..255\"]}, int_size=8),\n    },\n    \"bits\": {\n        \"native_type\": \"YANGBitsType(allowed_bits={})\",\n        \"base_type\": True,\n        \"quote_arg\": True,\n        \"pytype\": YANGBitsType(allowed_bits={}),\n    },\n    \"uint16\": {\n        \"native_type\": (\n            \"RestrictedClassType(base_type=int,\" + \" restriction_dict={'range': ['0..65535']},\" + \"int_size=16)\"\n        ),\n        \"base_type\": True,\n        \"pytype\": RestrictedClassType(base_type=int, restriction_dict={\"range\": [\"0..65535\"]}, int_size=16),\n    },\n    \"uint32\": {\n        \"native_type\": (\n            \"RestrictedClassType(base_type=long,\" + \" restriction_dict={'range': ['0..4294967295']},\" + \" int_size=32)\"\n        ),\n        \"base_type\": True,\n        \"pytype\": RestrictedClassType(base_type=long, restriction_dict={\"range\": [\"0..4294967295\"]}, int_size=32),\n    },\n    \"uint64\": {\n        \"native_type\": (\n            \"RestrictedClassType(base_type=long, \"\n            + \"restriction_dict={'range': \"\n            + \" ['0..18446744073709551615']}, int_size=64)\"\n        ),\n        \"base_type\": True,\n        \"pytype\": RestrictedClassType(\n            base_type=long, restriction_dict={\"range\": [\"0..18446744073709551615\"]}, int_size=64\n        ),\n    },\n    \"string\": {\"native_type\": \"str\", \"base_type\": True, \"quote_arg\": True, \"pytype\": str},\n    \"decimal64\": {\"native_type\": \"Decimal\", \"base_type\": True, \"pytype\": decimal.Decimal},\n    \"empty\": {\n        \"native_type\": \"YANGBool\",\n        \"map\": class_bool_map,\n        \"base_type\": True,\n        \"quote_arg\": True,\n        \"pytype\": YANGBool,\n    },\n    \"int8\": {\n        \"native_type\": \"RestrictedClassType(base_type=int, \"\n        + \"restriction_dict={'range': ['-128..127']}, int_size=8)\",\n        \"base_type\": True,\n        \"pytype\": RestrictedClassType(base_type=int, restriction_dict={\"range\": [\"-128..127\"]}, int_size=8),\n    },\n    \"int16\": {\n        \"native_type\": (\n            \"RestrictedClassType(base_type=int,\" \"restriction_dict={'range': ['-32768..32767']}, \" + \"int_size=16)\"\n        ),\n        \"base_type\": True,\n        \"pytype\": RestrictedClassType(base_type=int, restriction_dict={\"range\": [\"-32768..32767\"]}, int_size=16),\n    },\n    \"int32\": {\n        \"native_type\": (\n            \"RestrictedClassType(base_type=long,\"\n            + \" restriction_dict={'range': \"\n            + \"['-2147483648..2147483647']}, int_size=32)\"\n        ),\n        \"base_type\": True,\n        \"pytype\": RestrictedClassType(\n            base_type=long, restriction_dict={\"range\": [\"-2147483648..2147483647\"]}, int_size=32\n        ),\n    },\n    \"int64\": {\n        \"native_type\": (\n            \"RestrictedClassType(base_type=long, \"\n            + \"restriction_dict={'range': \"\n            + \"['-9223372036854775808..9223372036854775807']}, \"\n            + \"int_size=64)\"\n        ),\n        \"base_type\": True,\n        \"pytype\": RestrictedClassType(\n            base_type=long, restriction_dict={\"range\": [\"-9223372036854775808..9223372036854775807\"]}, int_size=64\n        ),\n    },\n}\n\n# We have a set of types which support \"range\" statements in RFC6020. This\n# list determins types that should be allowed to have a \"range\" argument.\nINT_RANGE_TYPES = [\"uint8\", \"uint16\", \"uint32\", \"uint64\", \"int8\", \"int16\", \"int32\", \"int64\"]\n\n# The types that are built-in to YANG\nYANG_BUILTIN_TYPES = list(class_map.keys()) + [\"container\", \"list\", \"rpc\", \"notification\", \"leafref\"]\n\n\n# Base machinery to support operation as a plugin to pyang.\ndef pyang_plugin_init():\n    plugin.register_plugin(PyangBindClass())\n\n\nclass PyangBindClass(plugin.PyangPlugin):\n    def add_output_format(self, fmts):\n        # Add the 'pybind' output format to pyang.\n        self.multiple_modules = True\n        fmts[\"pybind\"] = self\n\n    def emit(self, ctx, modules, fd):\n        # When called, call the build_pyangbind function.\n        build_pybind(ctx, modules, fd)\n\n    def add_opts(self, optparser):\n        # Add pyangbind specific operations to pyang. These are documented in the\n        # options, but are essentially divided into three sets.\n        #   * xpathhelper - How pyangbind should deal with xpath expressions.\n        #     This module is documented in lib/xpathhelper and describes how\n        #     to support registration, updates, and retrieval of xpaths.\n        #   * class output - whether a single file should be created, or whether\n        #     a hierarchy of python modules should be created. The latter is\n        #     preferable when one has large trees being compiled.\n        #   * extensions - support for YANG extensions that pyangbind should look\n        #     for, and add as a dictionary with each element.\n        option_group = optparse.OptionGroup(optparser, \"pyangbind output specific options\")\n        option_group.add_option(\n            \"--use-xpathhelper\",\n            dest=\"use_xpathhelper\",\n            action=\"store_true\",\n            help=\"\"\"Use the xpathhelper module to\n                                   resolve leafrefs\"\"\",\n        ),\n        option_group.add_option(\n            \"--split-class-dir\",\n            metavar=\"DIR\",\n            dest=\"split_class_dir\",\n            help=\"\"\"Split the code output into\n                                   multiple directories\"\"\",\n        ),\n        option_group.add_option(\n            \"--interesting-extension\",\n            metavar=\"EXTENSION-MODULE\",\n            default=[],\n            action=\"append\",\n            type=str,\n            dest=\"pybind_interested_exts\",\n            help=\"\"\"A set of extensions that\n                                  are interesting and should be\n                                  stored with the class. They\n                                  can be accessed through the\n                                  \"extension_dict()\" argument.\n                                  Multiple arguments can be\n                                  specified.\"\"\",\n        ),\n        option_group.add_option(\n            \"--use-extmethods\",\n            dest=\"use_extmethods\",\n            action=\"store_true\",\n            help=\"\"\"Allow a path-keyed dictionary\n                                  to be used to specify methods\n                                  related to a particular class\"\"\",\n        ),\n        option_group.add_option(\n            \"--build-rpcs\",\n            dest=\"build_rpcs\",\n            action=\"store_true\",\n            help=\"\"\"Generate class bindings for\n                                  the input and output of RPCs\n                                  defined in each module. These\n                                  are placed at the root of\n                                  each module\"\"\",\n        ),\n        option_group.add_option(\n            \"--presence\",\n            dest=\"generate_presence\",\n            action=\"store_true\",\n            help=\"\"\"Capture whether the presence\n                                    keyword is used in the generated\n                                    code.\"\"\",\n        ),\n        option_group.add_option(\n            \"--build-notifications\",\n            dest=\"build_notifications\",\n            action=\"store_true\",\n            help=\"\"\"Generate class bindings for\n                                  notifications defined in each\n                                  module. These are placed at\n                                  the root of each module\"\"\",\n        ),\n        optparser.add_option_group(option_group)\n\n\n# Core function to build the pyangbind output - starting with building the\n# dependencies - and then working through the instantiated tree that pyang has\n# already parsed.\ndef build_pybind(ctx, modules, fd):\n    # Restrict the output of the plugin to only the modules that are supplied\n    # to pyang. More modules are parsed by pyangbind to resolve typedefs and\n    # identities.\n    module_d = {}\n    for mod in modules:\n        module_d[mod.arg] = mod\n    pyang_called_modules = module_d.keys()\n\n    # Bail if there are pyang errors, since this certainly means that the\n    # pyangbind output will fail - unless these are solely due to imports that\n    # we provided but then unused.\n    if len(ctx.errors):\n        for e in ctx.errors:\n            print(\"INFO: encountered %s\" % str(e))\n            if not e[1] in [\"UNUSED_IMPORT\", \"PATTERN_ERROR\"]:\n                sys.stderr.write(\"FATAL: pyangbind cannot build module that pyang\" + \" has found errors with.\\n\")\n                sys.exit(127)\n\n    # Build the common set of imports that all pyangbind files needs\n    ctx.pybind_common_hdr = \"# -*- coding: utf-8 -*-\"\n    ctx.pybind_common_hdr += \"\\n\"\n    ctx.pybind_common_hdr += \"from operator import attrgetter\\n\"\n    if ctx.opts.use_xpathhelper:\n        ctx.pybind_common_hdr += \"import pyangbind.lib.xpathhelper as xpathhelper\\n\"\n\n    yangtypes_imports = [\n        \"RestrictedPrecisionDecimalType\",\n        \"RestrictedClassType\",\n        \"TypedListType\",\n        \"YANGBool\",\n        \"YANGListType\",\n        \"YANGDynClass\",\n        \"ReferenceType\",\n        \"YANGBinary\",\n        \"YANGBitsType\",\n    ]\n    for library in yangtypes_imports:\n        ctx.pybind_common_hdr += \"from pyangbind.lib.yangtypes import {}\\n\".format(library)\n    ctx.pybind_common_hdr += \"\"\"from pyangbind.lib.base import PybindBase\nfrom collections import OrderedDict\nfrom decimal import Decimal\n\nimport builtins as __builtin__\n\nlong = int\n\n\"\"\"\n    if not ctx.opts.split_class_dir:\n        fd.write(ctx.pybind_common_hdr)\n    else:\n        ctx.pybind_split_basepath = os.path.abspath(ctx.opts.split_class_dir)\n        if not os.path.exists(ctx.pybind_split_basepath):\n            os.makedirs(ctx.pybind_split_basepath)\n\n    # Determine all modules, and submodules that are needed, along with the\n    # prefix that is used for it. We need to ensure that we understand all of the\n    # prefixes that might be used to reference an identity or a typedef.\n    all_mods = []\n    for module in modules:\n        local_module_prefix = module.search_one(\"prefix\")\n        if local_module_prefix is None:\n            local_module_prefix = module.search_one(\"belongs-to\").search_one(\"prefix\")\n            if local_module_prefix is None:\n                raise AttributeError(\"A module (%s) must have a prefix or parent \" + \"module\")\n            local_module_prefix = local_module_prefix.arg\n        else:\n            local_module_prefix = local_module_prefix.arg\n        mods = [(local_module_prefix, module)]\n\n        imported_modules = module.search(\"import\")\n\n        # 'include' statements specify the submodules of the existing module -\n        # that also need to be parsed.\n        for i in module.search(\"include\"):\n            subm = ctx.get_module(i.arg)\n            if subm is not None:\n                mods.append((local_module_prefix, subm))\n                # Handle the case that imports are within a submodule\n                if subm.search(\"import\") is not None:\n                    imported_modules.extend(subm.search(\"import\"))\n\n        # 'import' statements specify the other modules that this module will\n        # reference.\n        for j in imported_modules:\n            mod = ctx.get_module(j.arg)\n            if mod is not None:\n                imported_module_prefix = j.search_one(\"prefix\").arg\n                mods.append((imported_module_prefix, mod))\n                modules.append(mod)\n        all_mods.extend(mods)\n\n    # remove duplicates from the list (same module and prefix)\n    new_all_mods = []\n    for mod in all_mods:\n        if mod not in new_all_mods:\n            new_all_mods.append(mod)\n    all_mods = new_all_mods\n\n    # Build a list of the 'typedef' and 'identity' statements that are included\n    # in the modules supplied.\n    defn = OrderedDict()\n    for defnt in [\"typedef\", \"identity\"]:\n        defn[defnt] = OrderedDict()\n        for m in all_mods:\n            t = misc_help.find_definitions(defnt, ctx, m[1], m[0])\n            for k in t:\n                if k not in defn[defnt]:\n                    defn[defnt][k] = t[k]\n\n    # Build the identities and typedefs (these are added to the class_map which\n    # is globally referenced).\n    build_identities(ctx, defn[\"identity\"])\n    build_typedefs(ctx, defn[\"typedef\"])\n\n    # Iterate through the tree which pyang has built, solely for the modules\n    # that pyang was asked to build\n    for modname in pyang_called_modules:\n        module = module_d[modname]\n        mods = [module]\n        for i in module.search(\"include\"):\n            subm = ctx.get_module(i.arg)\n            if subm is not None:\n                mods.append(subm)\n        for m in mods:\n            children = [ch for ch in module.i_children if ch.keyword in statements.data_definition_keywords]\n            get_children(ctx, fd, children, m, m)\n\n            if ctx.opts.build_rpcs:\n                rpcs = [ch for ch in module.i_children if ch.keyword == \"rpc\"]\n                # Build RPCs specifically under the module name, since this\n                # can be used as a proxy for the namespace.\n                if len(rpcs):\n                    get_children(\n                        ctx, fd, rpcs, module, module, register_paths=False, path=\"/%s_rpc\" % (safe_name(module.arg))\n                    )\n\n            if ctx.opts.build_notifications:\n                notifications = [ch for ch in module.i_children if ch.keyword == \"notification\"]\n                # Build notifications specifically under the module name,\n                # since this can be used as a proxy for the namespace.\n                if len(notifications):\n                    get_children(\n                        ctx,\n                        fd,\n                        notifications,\n                        module,\n                        module,\n                        register_paths=False,\n                        path=\"/%s_notification\" % (safe_name(module.arg)),\n                    )\n\n\ndef build_identities(ctx, defnd):\n    # Build a storage object that has all the definitions that we\n    # require within it.\n    idstore = IdentityStore()\n    idstore.build_store_from_definitions(ctx, defnd)\n\n    identity_dict = {}\n    for identity in idstore:\n        for prefix in identity.prefixes():\n            ident = \"%s:%s\" % (prefix, identity.name)\n            identity_dict[ident] = {}\n            identity_dict[\"%s\" % identity.name] = {}\n            for ch in identity.children:\n                d = {\"@module\": ch.source_module, \"@namespace\": ch.source_namespace}\n                for cpfx in ch.prefixes() + [None]:\n                    if cpfx is not None:\n                        spfx = \"%s:\" % cpfx\n                    else:\n                        spfx = \"\"\n                    identity_dict[ident][ch.name] = d\n                    identity_dict[identity.name][ch.name] = d\n                    identity_dict[ident][\"%s%s\" % (spfx, ch.name)] = d\n                    identity_dict[identity.name][\"%s%s\" % (spfx, ch.name)] = d\n\n        if identity.name not in identity_dict:\n            identity_dict[identity.name] = {}\n\n    # Add entries to the class_map such that this identity can be referenced by\n    # elements that use this identity ref.\n    for i in identity_dict:\n        id_type = {\n            \"native_type\": \"\"\"RestrictedClassType(base_type=str, \"\"\"\n            + \"\"\"restriction_type=\"dict_key\", \"\"\"\n            + \"\"\"restriction_arg=%s,)\"\"\" % identity_dict[i],\n            \"restriction_argument\": identity_dict[i],\n            \"restriction_type\": \"dict_key\",\n            \"parent_type\": \"string\",\n            \"base_type\": False,\n        }\n        class_map[i] = id_type\n\n\ndef build_typedefs(ctx, defnd):\n    # Build the type definitions that are specified within a model. Since\n    # typedefs are essentially derived from existing types, order of processing\n    # is important - we need to go through and build the types in order where\n    # they have a known 'type'.\n    unresolved_tc = {}\n    for i in defnd:\n        unresolved_tc[i] = 0\n    unresolved_t = list(defnd.keys())\n    error_ids = []\n    known_types = list(class_map.keys())\n    known_types.append(\"enumeration\")\n    known_types.append(\"leafref\")\n    known_types.append(\"bits\")\n    base_types = copy.deepcopy(known_types)\n    process_typedefs_ordered = []\n\n    while len(unresolved_t):\n        t = unresolved_t.pop(0)\n        base_t = defnd[t].search_one(\"type\")\n        if base_t.arg == \"union\":\n            subtypes = []\n            for i in base_t.search(\"type\"):\n                if i.arg == \"identityref\":\n                    subtypes.append(i.search_one(\"base\"))\n                else:\n                    subtypes.append(i)\n        elif base_t.arg == \"identityref\":\n            subtypes = [base_t.search_one(\"base\")]\n        else:\n            subtypes = [base_t]\n\n        any_unknown = False\n        for i in subtypes:\n            # Resolve this typedef to the module that it\n            # was defined by\n\n            if \":\" in i.arg:\n                defining_module = util.prefix_to_module(\n                    defnd[t].i_module, i.arg.split(\":\")[0], defnd[t].pos, ctx.errors\n                )\n            else:\n                defining_module = defnd[t].i_module\n\n            belongs_to = defining_module.search_one(\"belongs-to\")\n            if belongs_to is not None:\n                for mod in ctx.modules:\n                    if mod[0] == belongs_to.arg:\n                        defining_module = ctx.modules[mod]\n\n            real_pfx = defining_module.search_one(\"prefix\").arg\n\n            if \":\" in i.arg:\n                tn = \"%s:%s\" % (real_pfx, i.arg.split(\":\")[1])\n            elif i.arg not in base_types:\n                # If this was not a base type (defined in YANG) then resolve it\n                # to the module it belongs to.\n                tn = \"%s:%s\" % (real_pfx, i.arg)\n            else:\n                tn = i.arg\n\n            if tn not in known_types:\n                any_unknown = True\n\n        if not any_unknown:\n            process_typedefs_ordered.append((t, defnd[t]))\n            known_types.append(t)\n        else:\n            unresolved_tc[t] += 1\n            if unresolved_tc[t] > 1000:\n                # Take a similar approach to the resolution of identities. If we have a\n                # typedef that has a type in it that is not found after many iterations\n                # then we should bail.\n                error_ids.append(t)\n                sys.stderr.write(\"could not find a match for %s type -> %s\\n\" % (t, [i.arg for i in subtypes]))\n            else:\n                unresolved_t.append(t)\n\n    if error_ids:\n        raise TypeError(\"could not resolve typedefs %s\" % error_ids)\n\n    # Process the types that we built above.\n    for i_tuple in process_typedefs_ordered:\n        item = i_tuple[1]\n        type_name = i_tuple[0]\n        # Copy the class_map entry - this is done so that we do not alter the\n        # existing instance in memory as we add to it.\n        cls, elemtype = copy.deepcopy(build_elemtype(ctx, item.search_one(\"type\")))\n        known_types = list(class_map.keys())\n        # Enumeration is a native type, but is not natively supported\n        # in the class_map, and hence we append it here.\n        known_types.append(\"enumeration\")\n        known_types.append(\"leafref\")\n        known_types.append(\"bits\")\n\n        # Don't allow duplicate definitions of types\n        if type_name in known_types:\n            raise TypeError(\"Duplicate definition of %s\" % type_name)\n        default_stmt = item.search_one(\"default\")\n\n        # 'elemtype' is a list when the type includes a union, so we need to go\n        # through and build a type definition that supports multiple types.\n        if not isinstance(elemtype, list):\n            # Map the original type to the new type, parsing the additional arguments\n            # that may be specified, for example, a new default, a pattern that must\n            # be matched, or a length (stored in the restriction_argument, and\n            # restriction_type class_map variables).\n            class_map[type_name] = {\"base_type\": False}\n            class_map[type_name][\"native_type\"] = elemtype[\"native_type\"]\n            if \"parent_type\" in elemtype:\n                class_map[type_name][\"parent_type\"] = elemtype[\"parent_type\"]\n            else:\n                yang_type = item.search_one(\"type\").arg\n                if yang_type not in known_types:\n                    raise TypeError(\"typedef specified a native type that was not \" + \"supported\")\n                class_map[type_name][\"parent_type\"] = yang_type\n            if default_stmt is not None:\n                class_map[type_name][\"default\"] = default_stmt.arg\n            if \"referenced_path\" in elemtype:\n                class_map[type_name][\"referenced_path\"] = elemtype[\"referenced_path\"]\n                class_map[type_name][\"class_override\"] = \"leafref\"\n            if \"require_instance\" in elemtype:\n                class_map[type_name][\"require_instance\"] = elemtype[\"require_instance\"]\n            if \"restriction_type\" in elemtype:\n                class_map[type_name][\"restriction_type\"] = elemtype[\"restriction_type\"]\n                class_map[type_name][\"restriction_argument\"] = elemtype[\"restriction_argument\"]\n            if \"quote_arg\" in elemtype:\n                class_map[type_name][\"quote_arg\"] = elemtype[\"quote_arg\"]\n        else:\n            # Handle a typedef that is a union - extended the class_map arguments\n            # to be a list that is parsed by the relevant dynamic type generation\n            # function.\n            native_type = []\n            parent_type = []\n            default = False if default_stmt is None else default_stmt.arg\n            for i in elemtype:\n                if isinstance(i[1][\"native_type\"], list):\n                    native_type.extend(i[1][\"native_type\"])\n                else:\n                    native_type.append(i[1][\"native_type\"])\n\n                if i[1][\"yang_type\"] in known_types:\n                    parent_type.append(i[1][\"yang_type\"])\n                elif i[1][\"yang_type\"] == \"identityref\":\n                    parent_type.append(i[1][\"parent_type\"])\n                else:\n                    msg = \"typedef in a union specified a native type that was not\"\n                    msg += \" supported (%s in %s)\" % (i[1][\"yang_type\"], item.arg)\n                    raise TypeError(msg)\n\n                if \"default\" in i[1] and not default:\n                    # When multiple 'default' values are specified within a union that\n                    # is within a typedef, then pyangbind will choose the first one.\n                    q = True if \"quote_arg\" in i[1] else False\n                    default = (i[1][\"default\"], q)\n            class_map[type_name] = {\"native_type\": native_type, \"base_type\": False, \"parent_type\": parent_type}\n            if default:\n                if not isinstance(default, tuple):\n                    q = True if \"quote_arg\" in i[1] else False\n                    default = (default, q)\n                class_map[type_name][\"default\"] = default[0]\n                class_map[type_name][\"quote_default\"] = default[1]\n\n        class_map[type_name.split(\":\")[1]] = class_map[type_name]\n\n\ndef get_children_elements(\n    ctx,\n    fd,\n    i_children,\n    module,\n    parent,\n    elements,\n    imports,\n    path=str(),\n    parent_cfg=True,\n    register_paths=True,\n    choice=False,\n):\n    # Iterative function that is called to get the elements of\n    # a list of i_children.\n    # It treats 'choice' children, which have elements in its cases.\n    # It extends the list of elements and imports as childs are inspected.\n\n    for ch in i_children:\n        if ch.keyword == \"choice\":\n            # These are cases\n            for sub_choice_ch in ch.i_children:\n                get_children_elements(\n                    ctx,\n                    fd,\n                    sub_choice_ch.i_children,\n                    module,\n                    parent,\n                    elements,\n                    imports,\n                    path=path,\n                    parent_cfg=parent_cfg,\n                    choice=(ch.arg, sub_choice_ch.arg),\n                    register_paths=register_paths,\n                )\n        else:\n            elements += get_element(\n                ctx,\n                fd,\n                ch,\n                module,\n                parent,\n                path + \"/\" + ch.arg,\n                parent_cfg=parent_cfg,\n                choice=choice,\n                register_paths=register_paths,\n            )\n            if ctx.opts.split_class_dir:\n                if (hasattr(ch, \"i_children\") and len(ch.i_children)) or (\n                    ctx.opts.generate_presence and ch.search_one(\"presence\")\n                ):\n                    imports.append(ch.arg)\n\n\ndef get_children(ctx, fd, i_children, module, parent, path=str(), parent_cfg=True, choice=False, register_paths=True):\n    # Iterative function that is called for all elements that have childen\n    # data nodes in the tree. This function resolves those nodes into the\n    # relevant leaf, or container/list configuration and outputs the python\n    # code that corresponds to it to the relevant file. parent_cfg is used to\n    # ensure that where a parent container was set to config false, this is\n    # inherited by all elements below it; and choice is used to store whether\n    # these leaves are within a choice or not.\n    elements = []\n    choices = False\n\n    # When pyangbind was asked to split classes, then we need to create the\n    # relevant directories for the modules to be created into. In this case\n    # even though fd might be a valid file handle, we ignore it.\n    if ctx.opts.split_class_dir:\n        if path == \"\":\n            fpath = ctx.pybind_split_basepath + \"/__init__.py\"\n        else:\n            pparts = path.split(\"/\")\n            npath = \"/\"\n\n            # Check that we don't have the problem of containers that are nested\n            # with the same name\n            for i in range(1, len(pparts)):\n                if i > 0 and pparts[i] == pparts[i - 1]:\n                    pname = safe_name(pparts[i]) + \"_\"\n                elif i == 1 and pparts[i] == module.arg:\n                    pname = safe_name(pparts[i]) + \"_\"\n                else:\n                    pname = safe_name(pparts[i])\n                npath += pname + \"/\"\n\n            bpath = ctx.pybind_split_basepath + npath\n            if not os.path.exists(bpath):\n                os.makedirs(bpath)\n            fpath = bpath + \"/__init__.py\"\n        if not os.path.exists(fpath):\n            try:\n                nfd = open(fpath, \"w\", encoding=\"utf-8\")\n            except IOError as m:\n                raise IOError(\"could not open pyangbind output file (%s)\" % m)\n            nfd.write(ctx.pybind_common_hdr)\n        else:\n            try:\n                nfd = open(fpath, \"a\", encoding=\"utf-8\")\n            except IOError as w:\n                raise IOError(\"could not open pyangbind output file (%s)\" % w)\n    else:\n        # If we weren't asked to split the files, then just use the file handle\n        # provided.\n        nfd = fd\n\n    if parent_cfg:\n        # The first time we find a container that has config false set on it\n        # then we need to hand this down the tree - we don't need to look if\n        # parent_cfg has already been set to False as we need to inherit.\n        parent_config = parent.search_one(\"config\")\n        if parent_config is not None:\n            parent_config = parent_config.arg\n            if parent_config.upper() == \"FALSE\":\n                # this container is config false\n                parent_cfg = False\n\n    # When we are asked to split the classes into modules, then we need to find\n    # all elements that have their own class within this container, and make sure\n    # that they are imported. Additionally, we need to find the elements that are\n    # within a case, and ensure that these are built with the corresponding\n    # choice specified.\n\n    import_req = []\n    get_children_elements(\n        ctx,\n        fd,\n        i_children,\n        module,\n        parent,\n        elements,\n        import_req,\n        path=path,\n        parent_cfg=parent_cfg,\n        choice=choice,\n        register_paths=register_paths,\n    )\n\n    # Write out the import statements if needed.\n    if ctx.opts.split_class_dir:\n        if len(import_req):\n            for im in import_req:\n                if im == parent.arg:\n                    im += \"_\"\n                # Relative import in PY2/PY3 compatible style\n                nfd.write(\"from . import {}\\n\".format(safe_name(im)))\n\n    # 'container', 'module', 'list' and 'submodule' all have their own classes\n    # generated.\n    if parent.keyword in [\"container\", \"module\", \"list\", \"submodule\", \"input\", \"output\", \"rpc\", \"notification\"]:\n        if ctx.opts.split_class_dir:\n            nfd.write(\"class %s(PybindBase):\\n\" % safe_name(parent.arg))\n        else:\n            if not path == \"\":\n                nfd.write(\n                    \"class yc_%s_%s_%s(PybindBase):\\n\"\n                    % (safe_name(parent.arg), safe_name(module.arg), safe_name(path.replace(\"/\", \"_\")))\n                )\n            else:\n                nfd.write(\"class %s(PybindBase):\\n\" % safe_name(parent.arg))\n\n        # If the container is actually a list, then determine what the key value\n        # is and store this such that we can give a hint.\n        keyval = False\n        if parent.keyword == \"list\":\n            keyval = parent.search_one(\"key\").arg if parent.search_one(\"key\") is not None else False\n            if keyval and \" \" in keyval:\n                keyval = keyval.split(\" \")\n            else:\n                keyval = [keyval]\n\n        # Auto-generate a docstring based on the description that is provided in\n        # the YANG module. This aims to provide readability to someone perusing the\n        # code that is generated.\n        parent_descr = parent.search_one(\"description\")\n        if parent_descr is not None:\n            parent_descr = \"\\n\\n  YANG Description: %s\" % parent_descr.arg\n        else:\n            parent_descr = \"\"\n\n        # Add more helper text.\n        nfd.write('''  \"\"\"\n  This class was auto-generated by the PythonClass plugin for PYANG\n  from YANG module %s - based on the path %s. Each member element of\n  the container is represented as a class variable - with a specific\n  YANG type.%s\n  \"\"\"\\n''' % (module.arg, (path if not path == \"\" else \"/%s\" % parent.arg), parent_descr))\n    else:\n        raise TypeError(\"unhandled keyword with children %s at %s\" % (parent.keyword, parent.pos))\n\n    elements_str = \"\"\n    if len(elements) == 0:\n        nfd.write(\"  _pyangbind_elements = {}\")\n    else:\n        # We want to prevent a user from creating new attributes on a class that\n        # are not allowed within the data model - this uses the __slots__ magic\n        # variable of the class to restrict anyone from adding to these classes.\n        # Doing so gives an AttributeError when a user tries to specify something\n        # that was not in the model.\n        elements_str = \"_pyangbind_elements = OrderedDict([\"\n        slots_str = \"  __slots__ = ('_path_helper',\"\n        slots_str += \" '_extmethods', \"\n        for i in elements:\n            slots_str += \"'__%s',\" % i[\"name\"]\n            elements_str += \"('%s', %s), \" % (i[\"name\"], i[\"name\"])\n        slots_str += \")\\n\"\n        elements_str += \"])\\n\"\n        nfd.write(slots_str + \"\\n\")\n        # Store the real name of the element - since we often get values that are\n        # not allowed in python as identifiers, but we need the real-name when\n        # creating instance documents (e.g., peer-group is not valid due to '-').\n        nfd.write(\"  _yang_name = '%s'\\n\" % (parent.arg))\n\n        # Store the namespace URI for easier access during XML serialisation\n        if module.keyword == \"submodule\":\n            mod_location = next(v for k, v in ctx.modules.items() if k[0] == module.i_modulename)\n        else:\n            mod_location = module\n        nfd.write(\"  _yang_namespace = '%s'\\n\" % (mod_location.search(\"namespace\")[0].arg))\n\n        choices = {}\n        choice_attrs = []\n        classes = {}\n        for i in elements:\n            # Loop through the elements and build a string that corresponds to the\n            # class that is going to be created. In all cases (thus far) this uses\n            # the YANGDynClass helper function to generate a dynamic type. This\n            # can extend the base type that is provided, and does this to give us\n            # some attributes that base classes such as int(), or str() don't have -\n            # but YANG needs (such as a default value, the original YANG name, any\n            # extension that were provided with the leaf, etc.).\n            class_str = {}\n            if \"default\" in i and not i[\"default\"] is None:\n                default_arg = '\"%s\"' % (i[\"default\"]) if i[\"quote_arg\"] else \"%s\" % i[\"default\"]\n\n            if i[\"class\"] == \"leaf-list\":\n                # Map a leaf-list to the type specified in the class map. This is a\n                # TypedList (see lib.yangtypes) with a particular set of types allowed.\n                class_str[\"name\"] = \"__%s\" % (i[\"name\"])\n                class_str[\"type\"] = \"YANGDynClass\"\n                class_str[\"arg\"] = \"unique=True, base=\"\n                if isinstance(i[\"type\"][\"native_type\"][1], list):\n                    allowed_type = \"[\"\n                    for subtype in i[\"type\"][\"native_type\"][1]:\n                        allowed_type += \"%s,\" % subtype\n                    allowed_type += \"]\"\n                else:\n                    allowed_type = \"%s\" % (i[\"type\"][\"native_type\"][1])\n                class_str[\"arg\"] += \"%s(allowed_type=%s)\" % (i[\"type\"][\"native_type\"][0], allowed_type)\n                if \"default\" in i and not i[\"default\"] is None:\n                    class_str[\"arg\"] += \", default=%s(%s)\" % (i[\"defaulttype\"], default_arg)\n            elif i[\"class\"] == \"list\":\n                # Map a list to YANGList class - this is dynamically derived by the\n                # YANGListType function to have the relevant characteristics, such as\n                # whether it is ordered by the user.\n                class_str[\"name\"] = \"__%s\" % (i[\"name\"])\n                class_str[\"type\"] = \"YANGDynClass\"\n                class_str[\"arg\"] = \"base=YANGListType(\"\n                class_str[\"arg\"] += \"%s,%s\" % ('\"%s\"' % i[\"key\"] if i[\"key\"] else False, i[\"type\"])\n                class_str[\"arg\"] += ', yang_name=\"%s\", parent=self' % (i[\"yang_name\"])\n                class_str[\"arg\"] += \", is_container='list', user_ordered=%s\" % i[\"user_ordered\"]\n                class_str[\"arg\"] += \", path_helper=self._path_helper\"\n                class_str[\"arg\"] += \", yang_keys='%s'\" % i[\"yang_keys\"]\n                class_str[\"arg\"] += \", extensions=%s\" % i[\"extensions\"]\n                if i[\"choice\"]:\n                    class_str[\"arg\"] += \", choice=%s\" % repr(choice)\n                class_str[\"arg\"] += \")\"\n            elif i[\"class\"] == \"union\" or i[\"class\"] == \"leaf-union\":\n                # A special mapped type where there is a union that just includes\n                # leaves this is mapped to a particular Union type, and valid types\n                # within it. The dynamically generated class will determine whether\n                # the input can be mapped to the types included in the union.\n                class_str[\"name\"] = \"__%s\" % (i[\"name\"])\n                class_str[\"type\"] = \"YANGDynClass\"\n                class_str[\"arg\"] = \"base=[\"\n                for u in i[\"type\"][1]:\n                    if isinstance(u[1][\"native_type\"], list):\n                        for su_native_type in u[1][\"native_type\"]:\n                            class_str[\"arg\"] += \"%s,\" % su_native_type\n                    else:\n                        class_str[\"arg\"] += \"%s,\" % u[1][\"native_type\"]\n                class_str[\"arg\"] += \"]\"\n                if \"default\" in i and not i[\"default\"] is None:\n                    class_str[\"arg\"] += \", default=%s(%s)\" % (i[\"defaulttype\"], default_arg)\n            elif i[\"class\"] == \"leafref\":\n                # A leafref, pyangbind uses the special ReferenceType which performs a\n                # lookup against the path_helper class provided.\n                class_str[\"name\"] = \"__%s\" % (i[\"name\"])\n                class_str[\"type\"] = \"YANGDynClass\"\n                class_str[\"arg\"] = \"base=%s\" % i[\"type\"]\n                class_str[\"arg\"] += \"(referenced_path='%s'\" % i[\"referenced_path\"]\n                class_str[\"arg\"] += \", caller=self._path() + ['%s'], \" % (i[\"yang_name\"])\n                class_str[\"arg\"] += \"path_helper=self._path_helper, \"\n                class_str[\"arg\"] += \"require_instance=%s)\" % (i[\"require_instance\"])\n            elif i[\"class\"] == \"leafref-list\":\n                # Deal with the special case of a list of leafrefs, since the\n                # ReferenceType has different arguments that need to be provided to the\n                # class to properly initialise.\n                class_str[\"name\"] = \"__%s\" % (i[\"name\"])\n                class_str[\"type\"] = \"YANGDynClass\"\n                class_str[\"arg\"] = \"base=%s\" % i[\"type\"][\"native_type\"][0]\n                class_str[\"arg\"] += \"(allowed_type=%s(referenced_path='%s',\" % (\n                    i[\"type\"][\"native_type\"][1][\"native_type\"],\n                    i[\"type\"][\"native_type\"][1][\"referenced_path\"],\n                )\n                class_str[\"arg\"] += \"caller=self._path() + ['%s'], \" % i[\"yang_name\"]\n                class_str[\"arg\"] += \"path_helper=self._path_helper, \"\n                class_str[\"arg\"] += \"require_instance=%s))\" % (i[\"type\"][\"native_type\"][1][\"require_instance\"])\n            else:\n                # Generically handle all other classes with the 'standard' mappings.\n                class_str[\"name\"] = \"__%s\" % (i[\"name\"])\n                class_str[\"type\"] = \"YANGDynClass\"\n                if isinstance(i[\"type\"], list):\n                    class_str[\"arg\"] = \"base=[\"\n                    for u in i[\"type\"]:\n                        class_str[\"arg\"] += \"%s,\" % u\n                    class_str[\"arg\"] += \"]\"\n                else:\n                    class_str[\"arg\"] = \"base=%s\" % i[\"type\"]\n                if \"default\" in i and not i[\"default\"] is None:\n                    class_str[\"arg\"] += \", default=%s(%s)\" % (i[\"defaulttype\"], default_arg)\n            if i[\"class\"] == \"container\":\n                class_str[\"arg\"] += \", is_container='container'\"\n                if ctx.opts.generate_presence:\n                    class_str[\"arg\"] += \", presence=%s\" % i[\"presence\"]\n            elif i[\"class\"] == \"list\":\n                class_str[\"arg\"] += \", is_container='list'\"\n            elif i[\"class\"] == \"leaf-list\":\n                class_str[\"arg\"] += \", is_leaf=False\"\n            else:\n                class_str[\"arg\"] += \", is_leaf=True\"\n            if class_str[\"arg\"]:\n                class_str[\"arg\"] += ', yang_name=\"%s\"' % i[\"yang_name\"]\n                class_str[\"arg\"] += \", parent=self\"\n                if i[\"choice\"]:\n                    class_str[\"arg\"] += \", choice=%s\" % repr(i[\"choice\"])\n                    choice_attrs.append(i[\"name\"])\n                    if not i[\"choice\"][0] in choices:\n                        choices[i[\"choice\"][0]] = {}\n                    if not i[\"choice\"][1] in choices[i[\"choice\"][0]]:\n                        choices[i[\"choice\"][0]][i[\"choice\"][1]] = []\n                    choices[i[\"choice\"][0]][i[\"choice\"][1]].append(i[\"name\"])\n                class_str[\"arg\"] += \", path_helper=self._path_helper\"\n                class_str[\"arg\"] += \", extmethods=self._extmethods\"\n                class_str[\"arg\"] += \", register_paths=%s\" % i[\"register_paths\"]\n                if \"extensions\" in i:\n                    class_str[\"arg\"] += \", extensions=%s\" % i[\"extensions\"]\n                if keyval and i[\"yang_name\"] in keyval:\n                    class_str[\"arg\"] += \", is_keyval=True\"\n                class_str[\"arg\"] += \", namespace='%s'\" % i[\"namespace\"]\n                class_str[\"arg\"] += \", defining_module='%s'\" % i[\"defining_module\"]\n                class_str[\"arg\"] += \", yang_type='%s'\" % i[\"origtype\"]\n                class_str[\"arg\"] += \", is_config=%s\" % (i[\"config\"] and parent_cfg)\n                classes[i[\"name\"]] = class_str\n\n        # TODO: get and set methods currently have errors that are reported that\n        # are a bit ugly. The intention here is to act like an immutable type -\n        # such that new class instances are created each time that the value is\n        # set.\n\n        # Generic class __init__, set up the path_helper if asked to.\n        nfd.write(\"\"\"\n  _pybind_generated_by = 'container'\n\n  def __init__(self, *args, **kwargs):\\n\"\"\")\n        if ctx.opts.use_xpathhelper:\n            nfd.write(\"\"\"\n    helper = kwargs.pop(\"path_helper\", None)\n    if helper is False:\n      self._path_helper = False\n    elif helper is not None and isinstance(helper, xpathhelper.YANGPathHelper):\n      self._path_helper = helper\n    elif hasattr(self, \"_parent\"):\n      helper = getattr(self._parent, \"_path_helper\", False)\n      self._path_helper = helper\n    else:\n      self._path_helper = False\\n\"\"\")\n        else:\n            nfd.write(\"\"\"\n    self._path_helper = False\\n\"\"\")\n\n        if ctx.opts.use_extmethods:\n            nfd.write(\"\"\"\n    extmethods = kwargs.pop(\"extmethods\", None)\n    if extmethods is False:\n      self._extmethods = False\n    elif extmethods is not None and isinstance(extmethods, dict):\n      self._extmethods = extmethods\n    elif hasattr(self, \"_parent\"):\n      extmethods = getattr(self._parent, \"_extmethods\", None)\n      self._extmethods = extmethods\n    else:\n      self._extmethods = False\\n\"\"\")\n        else:\n            nfd.write(\"\"\"\n    self._extmethods = False\\n\"\"\")\n\n        # Write out the classes that are stored locally as self.__foo where\n        # foo is the safe YANG name.\n        for c in classes:\n            nfd.write(\"    self.%s = %s(%s)\\n\" % (classes[c][\"name\"], classes[c][\"type\"], classes[c][\"arg\"]))\n        # Don't accept arguments to a container/list/submodule class\n        nfd.write(\"\"\"\n    load = kwargs.pop(\"load\", None)\n    if args:\n      if len(args) > 1:\n        raise TypeError(\"cannot create a YANG container with >1 argument\")\n      all_attr = True\n      for e in self._pyangbind_elements:\n        if not hasattr(args[0], e):\n          all_attr = False\n          break\n      if not all_attr:\n        raise ValueError(\"Supplied object did not have the correct attributes\")\n      for e in self._pyangbind_elements:\n        nobj = getattr(args[0], e)\n        if nobj._changed() is False:\n          continue\n        setmethod = getattr(self, \"_set_%s\" % e)\n        if load is None:\n          setmethod(getattr(args[0], e))\n        else:\n          setmethod(getattr(args[0], e), load=load)\\n\"\"\")\n\n        # A generic method to provide a path() method on each container, that gives\n        # a path in the form of a list that describes the nodes in the hierarchy.\n        nfd.write(\"\"\"\n  def _path(self):\n    if hasattr(self, \"_parent\"):\n      return self._parent._path()+[self._yang_name]\n    else:\n      return %s\\n\"\"\" % path.split(\"/\")[1:])\n\n        # For each element, write out a getter and setter method - with the doc\n        # string of the element within the model.\n        for i in elements:\n            c_str = classes[i[\"name\"]]\n            description_str = \"\"\n            if i[\"description\"]:\n                description_str = \"\\n\\n    YANG Description: %s\" % i[\"description\"]\n            nfd.write('''\n  def _get_%s(self):\n    \"\"\"\n    Getter method for %s, mapped from YANG variable %s (%s)%s\n    \"\"\"\n    return self.__%s\n      ''' % (i[\"name\"], i[\"name\"], i[\"path\"], i[\"origtype\"], description_str, i[\"name\"]))\n\n            nfd.write('''\n  def _set_%s(self, v, load=False):\n    \"\"\"\n    Setter method for %s, mapped from YANG variable %s (%s)\n    If this variable is read-only (config: false) in the\n    source YANG file, then _set_%s is considered as a private\n    method. Backends looking to populate this variable should\n    do so via calling thisObj._set_%s() directly.%s\n    \"\"\"''' % (i[\"name\"], i[\"name\"], i[\"path\"], i[\"origtype\"], i[\"name\"], i[\"name\"], description_str))\n            if keyval and i[\"yang_name\"] in keyval:\n                nfd.write(\"\"\"\n    parent = getattr(self, \"_parent\", None)\n    if parent is not None and load is False:\n      raise AttributeError(\"Cannot set keys directly when\" +\n                             \" within an instantiated list\")\\n\"\"\")\n            nfd.write(\"\"\"\n    if hasattr(v, \"_utype\"):\n      v = v._utype(v)\"\"\")\n            nfd.write(\"\"\"\n    try:\n      t = %s(v,%s)\"\"\" % (c_str[\"type\"], c_str[\"arg\"]))\n            nfd.write(\"\"\"\n    except (TypeError, ValueError):\\n\"\"\")\n            nfd.write(\n                \"\"\"      raise ValueError({\n          'error-string': \\\"\\\"\\\"%s must be of a type compatible with %s\\\"\\\"\\\",\n          'defined-type': \"%s\",\n          'generated-type': \\\"\\\"\\\"%s(%s)\\\"\\\"\\\",\n        })\\n\\n\"\"\"\n                % (\n                    i[\"name\"],\n                    i[\"origtype\"],\n                    (\n                        \"%s:%s\" % (i[\"defining_module\"], i[\"origtype\"])\n                        if \":\" not in i[\"origtype\"] and not i[\"origtype\"] in YANG_BUILTIN_TYPES\n                        else i[\"origtype\"]\n                    ),\n                    c_str[\"type\"],\n                    c_str[\"arg\"],\n                )\n            )\n            nfd.write(\"    self.__%s = t\\n\" % (i[\"name\"]))\n            nfd.write(\"    if hasattr(self, '_set'):\\n\")\n            nfd.write(\"      self._set()\\n\")\n\n            # When we want to return a value to its default, the unset method can\n            # be used. Generally, this is done in a choice where one branch needs to\n            # be set to the default, but may be used wherever re-initialiation of\n            # the object is required.\n            nfd.write(\"\"\"\n  def _unset_%s(self):\n    self.__%s = %s(%s)\\n\\n\"\"\" % (i[\"name\"], i[\"name\"], c_str[\"type\"], c_str[\"arg\"]))\n\n        # When an element is read-only, write out the _set and _get methods, but\n        # we don't actually make the property object accessible. This ensures that\n        # where backends are populating the model, then they can do so via the\n        # _set_X method - but a 'normal' user can't just do container.X = 10.\n        for i in elements:\n            rw = True\n            if not i[\"config\"]:\n                rw = False\n            elif not parent_cfg:\n                rw = False\n\n            if not rw:\n                nfd.write(\"\"\"  %s = __builtin__.property(_get_%s)\\n\"\"\" % (i[\"name\"], i[\"name\"]))\n            else:\n                nfd.write(\"\"\"  %s = __builtin__.property(_get_%s, _set_%s)\\n\"\"\" % (i[\"name\"], i[\"name\"], i[\"name\"]))\n    nfd.write(\"\\n\")\n\n    # Store a list of the choices that are included within this module such that\n    # we can enforce each branch.\n    if choices:\n        nfd.write(\"  __choices__ = %s\" % repr(choices))\n    nfd.write(\"\"\"\\n  %s\\n\"\"\" % elements_str)\n    nfd.write(\"\\n\")\n\n    try:\n        nfd.flush()\n        os.fsync(nfd.fileno())\n    except OSError:\n        pass\n\n    if ctx.opts.split_class_dir:\n        nfd.close()\n\n    return None\n\n\ndef build_elemtype(ctx, et, prefix=False):\n    # Build a dictionary which defines the type for the element. This is used\n    # both in the case that a typedef needs to be built, as well as on per-list\n    # basis.\n    cls = None\n    pattern_stmt = et.search_one(\"pattern\") if not et.search_one(\"pattern\") is None else False\n    range_stmt = et.search_one(\"range\") if not et.search_one(\"range\") is None else False\n    length_stmt = et.search_one(\"length\") if not et.search_one(\"length\") is None else False\n\n    # Determine whether there are any restrictions that are placed on this leaf,\n    # and build a dictionary of the different restrictions to be placed on the\n    # type.\n    restrictions = {}\n    if pattern_stmt:\n        restrictions[\"pattern\"] = pattern_stmt.arg\n\n    if length_stmt:\n        if \"|\" in length_stmt.arg:\n            restrictions[\"length\"] = [i.replace(\" \", \"\") for i in length_stmt.arg.split(\"|\")]\n        else:\n            restrictions[\"length\"] = [length_stmt.arg]\n\n    if range_stmt:\n        # Complex ranges are separated by pipes\n        if \"|\" in range_stmt.arg:\n            restrictions[\"range\"] = [i.replace(\" \", \"\") for i in range_stmt.arg.split(\"|\")]\n        else:\n            restrictions[\"range\"] = [range_stmt.arg]\n\n    # Build RestrictedClassTypes based on the compiled dictionary and the\n    # underlying base type.\n    if len(restrictions):\n        if \"length\" in restrictions or \"pattern\" in restrictions:\n            cls = \"restricted-%s\" % (et.arg)\n            elemtype = {\n                \"native_type\": \"\"\"RestrictedClassType(base_type=%s, restriction_dict=%s)\"\"\"\n                % (class_map[et.arg][\"native_type\"], repr(restrictions)),\n                \"restriction_dict\": restrictions,\n                \"parent_type\": et.arg,\n                \"base_type\": False,\n            }\n        elif \"range\" in restrictions:\n            cls = \"restricted-%s\" % et.arg\n            elemtype = {\n                \"native_type\": \"\"\"RestrictedClassType(base_type=%s, restriction_dict=%s)\"\"\"\n                % (class_map[et.arg][\"native_type\"], repr(restrictions)),\n                \"restriction_dict\": restrictions,\n                \"parent_type\": et.arg,\n                \"base_type\": False,\n            }\n\n    # Handle all other types of leaves that are not restricted classes.\n    if cls is None:\n        cls = \"leaf\"\n        # Enumerations are built as RestrictedClasses where the value that is\n        # provided to the class is check against the keys of a dictionary.\n        if et.arg == \"enumeration\":\n            enumeration_dict = {}\n            for enum in et.search(\"enum\"):\n                enumeration_dict[str(enum.arg)] = {}\n                val = enum.search_one(\"value\")\n                if val is not None:\n                    enumeration_dict[str(enum.arg)][\"value\"] = int(val.arg)\n            elemtype = {\n                \"native_type\": \"\"\"RestrictedClassType(base_type=str, \\\n                                    restriction_type=\"dict_key\", \\\n                                    restriction_arg=%s,)\"\"\"\n                % (enumeration_dict),\n                \"restriction_argument\": enumeration_dict,\n                \"restriction_type\": \"dict_key\",\n                \"parent_type\": \"string\",\n                \"base_type\": False,\n            }\n        # Map decimal64 to a RestrictedPrecisionDecimalType - this is there to\n        # ensure that the fraction-digits argument can be implemented. Note that\n        # fraction-digits is a mandatory argument.\n        elif et.arg == \"decimal64\":\n            fd_stmt = et.search_one(\"fraction-digits\")\n            if fd_stmt is not None:\n                cls = \"restricted-decimal64\"\n                elemtype = {\n                    \"native_type\": \"\"\"RestrictedPrecisionDecimalType(precision=%s)\"\"\" % fd_stmt.arg,\n                    \"base_type\": False,\n                    \"parent_type\": \"decimal64\",\n                }\n            else:\n                elemtype = class_map[et.arg]\n        # Handle unions - build a list of the supported types that are under the\n        # union.\n        elif et.arg == \"union\":\n            elemtype = []\n            for uniontype in et.search(\"type\"):\n                elemtype_s = copy.deepcopy(build_elemtype(ctx, uniontype))\n                elemtype_s[1][\"yang_type\"] = uniontype.arg\n                elemtype.append(elemtype_s)\n            cls = \"union\"\n        # Map leafrefs to a ReferenceType, handling the referenced path, and\n        # whether require-instance is set. When xpathhelper is not specified, then\n        # no such mapping is done - at this point, we solely map to a string.\n        elif et.arg == \"leafref\":\n            path_stmt = et.search_one(\"path\")\n            if path_stmt is None:\n                raise ValueError(\"leafref specified with no path statement\")\n            require_instance = (\n                class_bool_map[et.search_one(\"require-instance\").arg]\n                if et.search_one(\"require-instance\") is not None\n                else True\n            )\n            if ctx.opts.use_xpathhelper:\n                elemtype = {\n                    \"native_type\": \"ReferenceType\",\n                    \"referenced_path\": path_stmt.arg,\n                    \"parent_type\": \"string\",\n                    \"base_type\": False,\n                    \"require_instance\": require_instance,\n                }\n                cls = \"leafref\"\n            else:\n                elemtype = {\"native_type\": \"str\", \"parent_type\": \"string\", \"base_type\": False}\n        # Handle identityrefs, but check whether there is a valid base where this\n        # has been specified.\n        elif et.arg == \"identityref\":\n            base_stmt = et.search_one(\"base\")\n            if base_stmt is None:\n                raise ValueError(\"identityref specified with no base statement\")\n            try:\n                elemtype = class_map[base_stmt.arg]\n            except KeyError:\n                sys.stderr.write(\"FATAL: identityref with an unknown base\\n\")\n                if DEBUG:\n                    pp.pprint(class_map.keys())\n                    pp.pprint(et.arg)\n                    pp.pprint(base_stmt.arg)\n                sys.exit(127)\n        elif et.arg == \"bits\":\n            allowed_bits = {}\n            for bit in et.search(\"bit\"):\n                position = bit.search_one(\"position\")\n                if position is not None:\n                    pos = position.arg\n                else:\n                    pos = 1 + max(allowed_bits.values(), default=-1)\n                    if pos < 0 or 4294967295 < pos:\n                        raise ValueError(\"position out of bounds\")\n                allowed_bits[bit.arg] = pos\n            cls = \"restricted-bits\"\n            elemtype = {\n                \"native_type\": f\"YANGBitsType(allowed_bits={allowed_bits})\",\n                \"base_type\": True,\n                \"parent_type\": \"bits\",\n                \"quote_arg\": True,\n            }\n        else:\n            # For all other cases, then we should be able to look up directly in the\n            # class_map for the defined type, since these are not 'derived' types\n            # at this point. In the case that we are referencing a type that is a\n            # typedef, then this has been added to the class_map.\n            try:\n                elemtype = class_map[et.arg]\n            except KeyError:\n                passed = False\n                if prefix:\n                    try:\n                        tmp_name = \"%s:%s\" % (prefix, et.arg)\n                        elemtype = class_map[tmp_name]\n                        passed = True\n                    except Exception:\n                        pass\n                if passed is False:\n                    sys.stderr.write(\"FATAL: unmapped type (%s)\\n\" % (et.arg))\n                    if DEBUG:\n                        pp.pprint(class_map.keys())\n                        pp.pprint(et.arg)\n                        pp.pprint(prefix)\n                    sys.exit(127)\n        if isinstance(elemtype, list):\n            cls = \"leaf-union\"\n        elif \"class_override\" in elemtype:\n            # this is used to propagate the fact that in some cases the\n            # native type needs to be dynamically built (e.g., leafref)\n            cls = elemtype[\"class_override\"]\n\n    return (cls, elemtype)\n\n\ndef find_absolute_default_type(default_type, default_value, elemname):\n    if not isinstance(default_type, list):\n        return default_type\n\n    for i in default_type:\n        if not i[1][\"base_type\"]:\n            test_type = class_map[i[1][\"parent_type\"]]\n        else:\n            test_type = i[1]\n        try:\n            test_type[\"pytype\"](default_value)\n            default_type = test_type\n            break\n        except (ValueError, TypeError):\n            pass\n    return find_absolute_default_type(default_type, default_value, elemname)\n\n\ndef get_element(ctx, fd, element, module, parent, path, parent_cfg=True, choice=False, register_paths=True):\n    # Handle mapping of an invidual element within the model. This function\n    # produces a dictionary that can then be mapped into the relevant code that\n    # dynamically generates a class.\n\n    # Find element's namespace and defining module\n    # If the element has the \"main_module\" attribute then it is part of a\n    # submodule and hence we should check the namespace and defining module\n    # of this, rather than the submodule\n    if hasattr(element, \"main_module\"):\n        element_module = element.main_module()\n    elif hasattr(element, \"i_orig_module\"):\n        element_module = element.i_orig_module\n    else:\n        element_module = None\n\n    namespace = (\n        element_module.search_one(\"namespace\").arg if element_module.search_one(\"namespace\") is not None else None\n    )\n    defining_module = element_module.arg\n\n    this_object = []\n    has_children = False\n    create_list = False\n\n    elemdescr = element.search_one(\"description\")\n    if elemdescr is None:\n        elemdescr = False\n    else:\n        elemdescr = elemdescr.arg\n\n        # In cases where there there are a set of interesting extensions specified\n        # then build a dictionary of these extension values to provide with the\n        # specific leaf for this element.\n    if element.substmts is not None and ctx.opts.pybind_interested_exts is not None:\n        extensions = {}\n        for ext in element.substmts:\n            if ext.keyword[0] in ctx.opts.pybind_interested_exts:\n                if not ext.keyword[0] in extensions:\n                    extensions[ext.keyword[0]] = {}\n                extensions[ext.keyword[0]][ext.keyword[1]] = ext.arg\n\n    # If the element has an i_children attribute then this is a container, list\n    # leaf-list or choice. Alternatively, it can be the 'input' or 'output'\n    # substmts of an RPC or a notification\n    if hasattr(element, \"i_children\"):\n        if element.keyword in [\"container\", \"list\", \"input\", \"output\", \"notification\"]:\n            has_children = True\n        elif element.keyword in [\"leaf-list\"]:\n            create_list = True\n\n        # Fixup the path when within a choice, because this iteration belives that\n        # we are under a new container, but this does not exist in the path.\n        if element.keyword in [\"choice\"]:\n            path_parts = path.split(\"/\")\n            npath = \"\"\n            for i in range(0, len(path_parts) - 1):\n                npath += \"%s/\" % path_parts[i]\n            npath.rstrip(\"/\")\n        else:\n            npath = path\n\n        # Create an element for a container.\n        has_presence = True if element.search_one(\"presence\") is not None else False\n        if element.i_children or (ctx.opts.generate_presence and has_presence):\n            get_children(\n                ctx,\n                fd,\n                element.i_children,\n                module,\n                element,\n                npath,\n                parent_cfg=parent_cfg,\n                choice=choice,\n                register_paths=register_paths,\n            )\n\n            elemconfigdef = element.search_one(\"config\")\n            elemconfig = class_bool_map[elemconfigdef.arg] if elemconfigdef else True\n\n            elemdict = {\n                \"name\": safe_name(element.arg),\n                \"origtype\": element.keyword,\n                \"class\": element.keyword,\n                \"path\": safe_name(npath),\n                \"config\": elemconfig,\n                \"description\": elemdescr,\n                \"yang_name\": element.arg,\n                \"choice\": choice,\n                \"register_paths\": register_paths,\n                \"namespace\": namespace,\n                \"defining_module\": defining_module,\n                \"extensions\": extensions if len(extensions) else None,\n                \"presence\": has_presence,\n            }\n\n            # Handle the different cases of class name, this depends on whether we\n            # were asked to split the bindings into a directory structure or not.\n            if ctx.opts.split_class_dir:\n                # If we were dealing with split classes, then rather than naming the\n                # class based on a unique intra-file name - and rather we must import\n                # the relative path to the module.class\n                if element.arg == parent.arg:\n                    modname = safe_name(element.arg) + \"_\"\n                else:\n                    modname = safe_name(element.arg)\n                elemdict[\"type\"] = \"%s.%s\" % (modname, safe_name(element.arg))\n\n            else:\n                # Otherwise, give a unique name for the class within the dictionary.\n                elemdict[\"type\"] = \"yc_%s_%s_%s\" % (\n                    safe_name(element.arg),\n                    safe_name(module.arg),\n                    safe_name(path.replace(\"/\", \"_\")),\n                )\n\n            # Deal with specific cases for list - such as the key and how it is\n            # ordered.\n            if element.keyword == \"list\":\n                elemdict[\"key\"] = (\n                    safe_name(element.search_one(\"key\").arg) if element.search_one(\"key\") is not None else False\n                )\n                elemdict[\"yang_keys\"] = (\n                    element.search_one(\"key\").arg if element.search_one(\"key\") is not None else False\n                )\n                user_ordered = element.search_one(\"ordered-by\")\n                elemdict[\"user_ordered\"] = (\n                    True if user_ordered is not None and user_ordered.arg.upper() == \"USER\" else False\n                )\n            this_object.append(elemdict)\n            has_children = True\n\n    # Deal with the cases that the attribute does not have children.\n    if not has_children:\n        if element.keyword in [\"leaf-list\"]:\n            create_list = True\n        cls, elemtype = copy.deepcopy(build_elemtype(ctx, element.search_one(\"type\")))\n\n        # Determine what the default for the leaf should be where there are\n        # multiple available.\n        # Algorithm:\n        #   - build a tree that is rooted on this class.\n        #   - perform a breadth-first search - the first node found\n        #   - that has the \"default\" leaf set, then we take this\n        #     as the value for the default\n\n        # then starting at the selected default node, traverse\n        # until we find a node that is declared to be a base_type\n        elemdefault = element.search_one(\"default\")\n        default_type = False\n        quote_arg = False\n        if elemdefault is not None:\n            elemdefault = elemdefault.arg\n            default_type = elemtype\n        if isinstance(elemtype, list):\n            # this is a union, we should check whether any of the types\n            # immediately has a default\n            for i in elemtype:\n                if \"default\" in i[1]:\n                    elemdefault = i[1][\"default\"]\n                    default_type = i[1]\n                    # default_type = i[1]\n                    # mapped_elemtype = i[1]\n        elif \"default\" in elemtype:\n            # if the actual type defines the default, then we need to maintain\n            # this\n            elemdefault = elemtype[\"default\"]\n            default_type = elemtype\n\n        # we need to indicate that the default type for the class_map\n        # is str\n        tmp_class_map = copy.copy(class_map)\n        tmp_class_map[\"enumeration\"] = {\"parent_type\": \"string\"}\n\n        if not default_type:\n            if isinstance(elemtype, list):\n                # this type has multiple parents\n                for i in elemtype:\n                    if \"parent_type\" in i[1]:\n                        if isinstance(i[1][\"parent_type\"], list):\n                            to_visit = [j for j in i[1][\"parent_type\"]]\n                        else:\n                            to_visit = [i[1][\"parent_type\"]]\n            elif \"parent_type\" in elemtype:\n                if isinstance(elemtype[\"parent_type\"], list):\n                    to_visit = [i for i in elemtype[\"parent_type\"]]\n                else:\n                    to_visit = [elemtype[\"parent_type\"]]\n\n                checked = list()\n                while to_visit:\n                    check = to_visit.pop(0)\n                    if check not in checked:\n                        checked.append(check)\n                        if \"parent_type\" in tmp_class_map[check]:\n                            if isinstance(tmp_class_map[check][\"parent_type\"], list):\n                                to_visit.extend(tmp_class_map[check][\"parent_type\"])\n                            else:\n                                to_visit.append(tmp_class_map[check][\"parent_type\"])\n\n                # checked now has the breadth-first search result\n                if elemdefault is None:\n                    for option in checked:\n                        if \"default\" in tmp_class_map[option]:\n                            elemdefault = tmp_class_map[option][\"default\"]\n                            default_type = tmp_class_map[option]\n                            break\n\n        if elemdefault is not None:\n            # we now need to check whether there's a need to\n            # find out what the base type is for this type\n            # we really expect a linear chain here.\n\n            # if we have a tuple as the type here, this means that\n            # the default was set at a level where there was not\n            # a single option for the type. check the default\n            # against each option, to get a to a single default_type\n            if isinstance(default_type, list):\n                default_type = find_absolute_default_type(default_type, elemdefault, element.arg)\n\n            if not default_type[\"base_type\"]:\n                if \"parent_type\" in default_type:\n                    if isinstance(default_type[\"parent_type\"], list):\n                        to_visit = [i for i in default_type[\"parent_type\"]]\n                    else:\n                        to_visit = [default_type[\"parent_type\"]]\n                checked = list()\n                while to_visit:\n                    check = to_visit.pop(0)  # remove from the top of stack - depth first\n                    if check not in checked:\n                        checked.append(check)\n                        if \"parent_type\" in tmp_class_map[check]:\n                            if isinstance(tmp_class_map[check][\"parent_type\"], list):\n                                to_visit.extend(tmp_class_map[check][\"parent_type\"])\n                            else:\n                                to_visit.append(tmp_class_map[check][\"parent_type\"])\n                default_type = tmp_class_map[checked.pop()]\n                if not default_type[\"base_type\"]:\n                    raise TypeError(\"default type was not a base type\")\n\n        # Set the default type based on what was determined above about the\n        # correct value to set.\n        if default_type:\n            quote_arg = default_type[\"quote_arg\"] if \"quote_arg\" in default_type else False\n            default_type = default_type[\"native_type\"]\n\n        elemconfigdef = element.search_one(\"config\")\n        elemconfig = class_bool_map[elemconfigdef.arg] if elemconfigdef else True\n\n        elemname = safe_name(element.arg)\n\n        # Deal with the cases that there is a requirement to create a list - these\n        # are leaf lists. There is some special handling for leaf-lists to ensure\n        # that the references are correctly created.\n        if create_list:\n            if not cls == \"leafref\":\n                cls = \"leaf-list\"\n\n                if isinstance(elemtype, list):\n                    allowed_types = []\n                    for subtype in elemtype:\n                        # nested union within a leaf-list type\n                        if isinstance(subtype, tuple):\n                            if subtype[0] == \"leaf-union\":\n                                for subelemtype in subtype[1][\"native_type\"]:\n                                    allowed_types.append(subelemtype)\n                            else:\n                                if isinstance(subtype[1][\"native_type\"], list):\n                                    allowed_types.extend(subtype[1][\"native_type\"])\n                                else:\n                                    allowed_types.append(subtype[1][\"native_type\"])\n                        else:\n                            allowed_types.append(subtype[\"native_type\"])\n                else:\n                    allowed_types = elemtype[\"native_type\"]\n            else:\n                cls = \"leafref-list\"\n                allowed_types = {\n                    \"native_type\": elemtype[\"native_type\"],\n                    \"referenced_path\": elemtype[\"referenced_path\"],\n                    \"require_instance\": elemtype[\"require_instance\"],\n                }\n            elemntype = {\"class\": cls, \"native_type\": (\"TypedListType\", allowed_types)}\n\n        else:\n            if cls == \"union\" or cls == \"leaf-union\":\n                elemtype = {\"class\": cls, \"native_type\": (\"UnionType\", elemtype)}\n            elemntype = elemtype[\"native_type\"]\n\n        # Build the dictionary for the element with the relevant meta-data\n        # specified within it.\n        elemdict = {\n            \"name\": elemname,\n            \"type\": elemntype,\n            \"origtype\": element.search_one(\"type\").arg,\n            \"path\": safe_name(path),\n            \"class\": cls,\n            \"default\": elemdefault,\n            \"config\": elemconfig,\n            \"defaulttype\": default_type,\n            \"quote_arg\": quote_arg,\n            \"description\": elemdescr,\n            \"yang_name\": element.arg,\n            \"choice\": choice,\n            \"register_paths\": register_paths,\n            \"namespace\": namespace,\n            \"defining_module\": defining_module,\n        }\n        if len(extensions):\n            elemdict[\"extensions\"] = extensions\n\n        if cls == \"leafref\":\n            elemdict[\"referenced_path\"] = elemtype[\"referenced_path\"]\n            elemdict[\"require_instance\"] = elemtype[\"require_instance\"]\n\n        this_object.append(elemdict)\n    return this_object\n"
  },
  {
    "path": "pyproject.toml",
    "content": "[build-system]\nrequires = [\n    \"setuptools\",\n    \"wheel\"\n]\nbuild-backend = \"setuptools.build_meta\""
  },
  {
    "path": "requirements.DEVELOPER.txt",
    "content": "black\nbuild\ncoverage\npytest\nrequests\nsetuptools<80.9\ntox\nwheel\n"
  },
  {
    "path": "requirements.txt",
    "content": "pyang\nlxml\nregex\nenum34\n"
  },
  {
    "path": "scripts/release.sh",
    "content": "#!/bin/bash\n\nTHISDIR=\"$(cd -P \"$(dirname \"${BASH_SOURCE[0]}\")\" && pwd)\"\nBASEDIR=$THISDIR/..\n\nvirtualenv --clear $BASEDIR/pyvirtualenv\nsource $BASEDIR/pyvirtualenv/bin/activate\n\npip install -r $BASEDIR/requirements.txt -r $BASEDIR/requirements.DEVELOPER.txt\n\npython -m tox --root $BASEDIR/\nif [ $? -ne 0 ]; then\n    echo \"FAILED: Cannot release a broken version!\"\n    exit 127\nfi\n\nrm -rf $BASEDIR/dist\npython -m build --outdir $BASEDIR/dist/ $BASEDIR/\npip install --force-reinstall $BASEDIR/dist/pyangbind-*.whl\n\nexport PYBINDPLUGIN=`/usr/bin/env python -c \\\n'import pyangbind; import os; print (\"{}/plugin\".format(os.path.dirname(pyangbind.__file__)))'`\nif ! pyang -V --plugindir $PYBINDPLUGIN -f pybind -o $BASEDIR/tests/base-binding-out.py $BASEDIR/tests/base-test.yang; then\n    echo \"FAILED: cannot bind with pyang\"\n    exit 126\nfi\n\npip install twine\npython -m twine upload  $BASEDIR/dist/pyangbind*\n\ndeactivate"
  },
  {
    "path": "setup.cfg",
    "content": "[flake8]\n# The following errors codes are ignored:\n#   * E111 - indentation is not a multiple of four\n#   * E114 - indentation is not a multiple of four (comment)\n#   * E121 - continuation line under-indented for hanging indent\n#   * E126 - continuation line over-indented for hanging indent\n#   * E127 - continuation line over-indented for visual indent\n#   * E128 - continuation line under-indented for visual indent\n#   * E131 - continuation line unaligned for hanging indent\nignore=E111,E114,E121,E126,E127,E128,E131\nexclude=.eggs,.tox,env,pyvirtualenv\nmax-line-length=119\n\n[metadata]\nname = pyangbind\nversion = attr: pyangbind.__version__\ndescription = 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.\nlong_description_content_type = text/x-rst\nlong_description = file: README.rst\nlicense = Apache License, Version 2.0\nauthor = Rob Shakir\nauthor_email = rjs@rob.sh\nurl = https://github.com/robshakir/pyangbind\nkeywords = \n   yang\n   pyang\nclassifiers =\n    Development Status :: 4 - Beta\n    Intended Audience :: Telecommunications Industry\n    Intended Audience :: Developers\n    Topic :: Software Development :: Code Generators\n    License :: OSI Approved :: Apache Software License\n    Programming Language :: Python :: 3.8\n    Programming Language :: Python :: 3.9\n    Programming Language :: Python :: 3.10\n    Programming Language :: Python :: 3.11\n    Programming Language :: Python :: 3.12\n    Programming Language :: Python :: 3.13\n    Programming Language :: Python :: Implementation :: CPython\n    Programming Language :: Python :: Implementation :: PyPy\n\n[options]\npython_requires = >=3.7\ninstall_requires = file: requirements.txt\npackages = find:\ninclude_package_data = True\nzip_safe=False\ntest_suite=\"tests.test_suite\",\n    \n[options.packages.find]\ninclude = \n    pyangbind\n    pyangbind.helpers\n    pyangbind.lib\n    pyangbind.plugin\nexclude = \n    tests"
  },
  {
    "path": "tests/__init__.py",
    "content": "import os\n\nimport unittest\n\n\ndef test_suite():\n    os.environ.setdefault(\"PYTHONDONTWRITEBYTECODE\", \"1\")\n    test_loader = unittest.TestLoader()\n    test_suite = test_loader.discover(\"tests\", pattern=\"run.py\")\n    return test_suite\n"
  },
  {
    "path": "tests/base-test.yang",
    "content": "module base-test {\n    yang-version \"1\";\n    namespace \"http://rob.sh/yang/test/base-test\";\n    prefix \"foo\";\n    organization \"BugReports Inc\";\n    contact \"A bug reporter\";\n\n    description\n        \"A test module that checks that the plugin works OK\";\n    revision 2014-01-01 {\n        description \"april-fools\";\n        reference \"fooled-you\";\n    }\n\n    container int-container {\n        description\n            \"A container\";\n\n        leaf eight {\n            type int8;\n            description\n              \"A test leaf for uint8\";\n        }\n    }\n}\n"
  },
  {
    "path": "tests/base.py",
    "content": "import distutils\nimport importlib\nimport inspect\nimport os.path\nimport requests\nimport shutil\nimport subprocess\nimport sys\nimport time\nimport types\n\nimport unittest\n\n\nclass PyangBindTestCase(unittest.TestCase):\n    yang_files = None\n    pyang_flags = None\n    split_class_dir = False\n    module_name = \"bindings\"\n    remote_yang_files = None\n    _pyang_generated_class_dir = None\n\n    @classmethod\n    def _fetch_remote_yang_files(cls):\n        # The structure of cls.remote_yang_files is expected to be as follows:\n        #   [ {'local_path': str, 'remote_prefix': str, 'files': [str, ...]}, ...]\n        for file_group in cls.remote_yang_files:\n            local_path = os.path.join(cls._test_path, file_group[\"local_path\"])\n            if not os.path.exists(local_path):\n                os.makedirs(local_path)\n            for file_path in file_group[\"files\"]:\n                file_name = file_path.split(\"/\")[-1]\n                local_file_path = os.path.join(local_path, file_name)\n                if not os.path.exists(local_file_path):\n                    remote_file_path = \"%s%s\" % (file_group[\"remote_prefix\"], file_path)\n                    downloaded = False\n                    for i in range(0, 4):\n                        response = requests.get(remote_file_path)\n                        if response.status_code != 200:\n                            time.sleep(2)\n                        else:\n                            downloaded = True\n                            with open(local_file_path, \"wb\") as fhandle:\n                                fhandle.write(response.content)\n                            break\n                    if not downloaded:\n                        raise RuntimeError(\n                            \"Could not download file: %s (response: %s)\" % (remote_file_path, response.status_code)\n                        )\n\n    @classmethod\n    def setUpClass(cls):\n        cls._test_path = os.path.dirname(inspect.getfile(cls))\n\n        if cls.remote_yang_files is not None:\n            cls._fetch_remote_yang_files()\n\n        if cls.yang_files is None:\n            raise ValueError(\"cls.yang_files must be set\")\n        pyang_path = distutils.spawn.find_executable(\"pyang\")\n        if not pyang_path:\n            raise RuntimeError(\"Could not locate `pyang` executable.\")\n        base_dir = os.path.dirname(os.path.dirname(__file__))\n        yang_files = [os.path.join(cls._test_path, filename) for filename in cls.yang_files]\n        plugin_dir = os.path.join(base_dir, \"pyangbind\", \"plugin\")\n\n        flags = cls.pyang_flags or []\n        if cls.split_class_dir is True:\n            cls._pyang_generated_class_dir = os.path.join(cls._test_path, cls.module_name)\n            flags.append(\"--split-class-dir {}\".format(cls._pyang_generated_class_dir))\n\n        pyang_cmd = \"{pyang} --plugindir {plugins} -f pybind -p {test_path} {flags} {yang_files}\".format(\n            pyang=pyang_path,\n            plugins=plugin_dir,\n            test_path=cls._test_path,\n            flags=\" \".join(flags),\n            yang_files=\" \".join(yang_files),\n        )\n        bindings_code = subprocess.check_output(\n            pyang_cmd, shell=True, stderr=subprocess.STDOUT, env={\"PYTHONPATH\": base_dir}\n        )\n        if not cls.split_class_dir:\n            module = types.ModuleType(cls.module_name)\n            exec(bindings_code, module.__dict__)\n        else:\n            sys.path.append(cls._test_path)\n            module = importlib.import_module(cls.module_name)\n            sys.path.remove(cls._test_path)\n        setattr(cls, cls.module_name, module)\n\n    @classmethod\n    def tearDownClass(cls):\n        delattr(cls, cls.module_name)\n        if cls.split_class_dir:\n            del sys.modules[cls.module_name]\n            # Remove auto-generated submodules from our system cache\n            for module in list(sys.modules.keys()):\n                if module.startswith(\"%s.\" % cls.module_name):\n                    del sys.modules[module]\n            shutil.rmtree(cls._pyang_generated_class_dir)\n            del cls._pyang_generated_class_dir\n        if cls.remote_yang_files:\n            yang_paths = set([x[\"local_path\"] for x in cls.remote_yang_files])\n            for yang_path in yang_paths:\n                shutil.rmtree(os.path.join(cls._test_path, yang_path))\n"
  },
  {
    "path": "tests/binary/__init__.py",
    "content": ""
  },
  {
    "path": "tests/binary/binary.yang",
    "content": "module binary {\n    yang-version \"1\";\n    namespace \"http://rob.sh/yang/test/binary\";\n    prefix \"foo\";\n    organization \"BugReports Inc\";\n    contact \"A bug reporter\";\n\n    description\n        \"A test module\";\n    revision 2014-01-01 {\n        description \"april-fools\";\n        reference \"fooled-you\";\n    }\n\n    container container {\n        leaf b1 {\n            type binary;\n            description\n                \"A test leaf\";\n        }\n\n        leaf b2 {\n            type binary;\n            default \"eWFuZw==\"; /* yang */\n            description\n                \"A test leaf with a default\";\n        }\n\n        leaf b3 {\n            type binary {\n                length 2;\n            }\n        }\n\n        leaf b4 {\n            type binary {\n                length 2..4;\n            }\n        }\n\n        leaf b5 {\n            type binary {\n                length \"2..4 | 6 | 10..42\";\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "tests/binary/run.py",
    "content": "#!/usr/bin/env python\n\nimport unittest\n\nfrom tests.base import PyangBindTestCase\n\n\nclass BinaryTests(PyangBindTestCase):\n    yang_files = [\"binary.yang\"]\n\n    def setUp(self):\n        self.binary_obj = self.bindings.binary()\n\n    def test_binary_leafs_exist(self):\n        for leaf in [\"b1\", \"b2\", \"b3\"]:\n            with self.subTest(leaf=leaf):\n                self.assertTrue(\n                    hasattr(self.binary_obj.container, leaf), \"Element did not exist in container (%s)\" % leaf\n                )\n\n    def test_set_binary_from_different_datatypes(self):\n        for value in [(b\"42\", True), ({\"42\": 42}, False), (-42, False), (\"Arthur Dent\", False)]:\n            with self.subTest(value=value):\n                allowed = True\n                try:\n                    self.binary_obj.container.b1 = value[0]\n                except ValueError:\n                    allowed = False\n                self.assertEqual(allowed, value[1], \"Could incorrectly set b1 to %s\" % value[0])\n\n    def test_binary_leaf_default_value(self):\n        default = b\"yang\"\n        self.assertEqual(\n            self.binary_obj.container.b2._default,\n            default,\n            \"Default for leaf b2 was not set correctly (%s != %s)\" % (self.binary_obj.container.b2._default, default),\n        )\n\n    def test_binary_leaf_is_empty_by_default(self):\n        empty = b\"\"\n        self.assertEqual(\n            self.binary_obj.container.b2,\n            empty,\n            \"Value of binary leaf was not null when checking b2 (%s != %s)\" % (self.binary_obj.container.b2, empty),\n        )\n\n    def test_binary_leaf_is_not_changed_by_default(self):\n        self.assertFalse(\n            self.binary_obj.container.b2._changed(),\n            \"Unset binary leaf specified changed when was default (%s != False)\"\n            % self.binary_obj.container.b2._changed(),\n        )\n\n    def test_set_binary_stores_value(self):\n        bits = b\"010\"\n        self.binary_obj.container.b2 = bits\n        self.assertEqual(\n            self.binary_obj.container.b2,\n            bits,\n            \"Binary leaf not successfully set (%s != %s)\" % (self.binary_obj.container.b2, bits),\n        )\n\n    def test_setting_binary_set_changed(self):\n        self.binary_obj.container.b2 = b\"010\"\n        self.assertTrue(\n            self.binary_obj.container.b2._changed(),\n            \"Binary leaf value not flagged as changed (%s != True)\" % self.binary_obj.container.b2._changed(),\n        )\n\n    def test_set_specific_length_binary_leaf(self):\n        for bits in [(b\"1\", False), (b\"12\", True), (b\"1234\", False)]:\n            with self.subTest(bits=bits):\n                allowed = True\n                try:\n                    self.binary_obj.container.b3 = bits[0]\n                except ValueError:\n                    allowed = False\n                self.assertEqual(\n                    allowed,\n                    bits[1],\n                    \"limited length binary incorrectly set to %s (%s != %s)\" % (bits[0], bits[1], allowed),\n                )\n\n    def test_set_binary_leaf_with_length_range(self):\n        for bits in [(b\"1\", False), (b\"12\", True), (b\"1234\", True), (b\"12345\", False)]:\n            with self.subTest(bits=bits):\n                allowed = True\n                try:\n                    self.binary_obj.container.b4 = bits[0]\n                except ValueError:\n                    allowed = False\n                self.assertEqual(\n                    allowed,\n                    bits[1],\n                    \"Limited length binary with range incorrectly set to %s (%s != %s)\" % (bits[0], bits[1], allowed),\n                )\n\n    def test_set_binary_leaf_with_complex_length(self):\n        for bits in [\n            (b\"1\", False),\n            (b\"12\", True),\n            (b\"123\", True),\n            (b\"12345\", False),\n            (b\"123456\", True),\n            (b\"123456789_\", True),\n            (b\"123456789_123456789_123456789_123456789_12\", True),\n            (b\"123456789_123456789_123456789_123456789_123\", False),\n        ]:\n            with self.subTest(bits=bits):\n                allowed = True\n                try:\n                    self.binary_obj.container.b5 = bits[0]\n                except ValueError:\n                    allowed = False\n                self.assertEqual(\n                    allowed,\n                    bits[1],\n                    \"Limited length binary with complex length incorrectly set to %s (%s != %s)\"\n                    % (bits[0], bits[1], allowed),\n                )\n\n\nif __name__ == \"__main__\":\n    unittest.main()\n"
  },
  {
    "path": "tests/bits/__init__.py",
    "content": ""
  },
  {
    "path": "tests/bits/bits.yang",
    "content": "module bits {\n  yang-version 1.1;\n  namespace \"http://rob.sh/yang/test/bits\";\n  prefix \"foo\";\n  organization \"BugReports Inc\";\n  contact \"A bug reporter\";\n\n  description\n      \"A test module\";\n  revision 2014-01-01 {\n      description \"april-fools\";\n      reference \"fooled-you\";\n  }\n\n  // a typedef for bits, which is then applied to a leaf\n  typedef typedefed-bits {\n    type bits {\n      bit foo {\n\tposition 0;\n      }\n      bit bar {\n\tposition 1;\n      }\n      bit baz {\n\tposition 2;\n      }\n    }\n  }\n\n  leaf bits2 {\n    type typedefed-bits;\n  }\n\n  // a leaf containing the bits type definition\n  leaf mybits {\n    type bits {\n      bit flag1 {\n\tposition 0;\n      }\n      bit flag2 {\n\tposition 1;\n      }\n      bit flag3 {\n\tposition 2;\n      }\n    }\n    default \"flag1 flag3\";\n  }\n\n  // a leaf containing the bits type definition without position statement\n  leaf bits3 {\n    type bits {\n      bit position0;\n    }\n  }\n}\n"
  },
  {
    "path": "tests/bits/run.py",
    "content": "#!/usr/bin/env python\nfrom __future__ import unicode_literals\n\nimport unittest\n\nfrom tests.base import PyangBindTestCase\n\n\nclass BitsTests(PyangBindTestCase):\n    yang_files = [\"bits.yang\"]\n\n    def setUp(self):\n        self.instance = self.bindings.bits()\n\n    def test_default_bits(self):\n        self.assertEqual(\n            self.instance.mybits._default,\n            set([\"flag1\", \"flag3\"]),\n        )\n        self.assertFalse(self.instance.mybits._changed())\n\n    def test_default_bits_with_in(self):\n        self.assertTrue(\n            \"flag1\" in self.instance.mybits._default\n            and \"flag2\" not in self.instance.mybits._default\n            and \"flag3\" in self.instance.mybits._default\n        )\n\n    def test_default_bits_is_unchanged(self):\n        self.assertFalse(self.instance.mybits._changed())\n\n    def test_set_flags(self):\n        for value, valid in [(\"flag1\", True), (\"flag2\", True), (\"unknownflag\", False)]:\n            with self.subTest(value=value, valid=valid):\n                allowed = True\n                try:\n                    self.instance.mybits.add(value)\n                except ValueError:\n                    allowed = False\n                self.assertEqual(allowed, valid)\n\n    def test_check_flags_unset(self):\n        self.instance.mybits.discard(\"flag2\")\n        self.assertTrue(\"flag2\" not in self.instance.mybits)\n        self.assertTrue(self.instance.mybits._changed())\n\n    def test_check_flags_set(self):\n        self.instance.bits2.add(\"foo\")\n        self.assertTrue(\"foo\" in self.instance.bits2)\n        self.assertEqual(set([\"foo\"]), self.instance.bits2)\n        self.assertTrue(self.instance.bits2._changed())\n\n    def test_bits_position(self):\n        self.instance.bits2.add(\"bar\")\n        self.instance.bits2.add(\"baz\")\n        self.instance.bits2.add(\"foo\")\n        self.assertEqual(str(self.instance.bits2), \"foo bar baz\")\n\n    def test_bits_no_position(self):\n        self.assertEqual(self.instance.bits3._allowed_bits[\"position0\"], 0)\n\n\nif __name__ == \"__main__\":\n    unittest.main()\n"
  },
  {
    "path": "tests/boolean-empty/__init__.py",
    "content": ""
  },
  {
    "path": "tests/boolean-empty/boolean-empty.yang",
    "content": "module boolean-empty {\n    yang-version \"1\";\n    namespace \"http://rob.sh/yang/test/boolean-empty\";\n    prefix \"foo\";\n    organization \"BugReports Inc\";\n    contact \"A bug reporter\";\n\n    description\n        \"A test module\";\n    revision 2014-01-01 {\n        description \"april-fools\";\n        reference \"fooled-you\";\n    }\n\n    container container {\n        leaf b1 {\n            type boolean;\n            description\n                \"A test leaf\";\n        }\n\n        leaf b2 {\n            type boolean;\n            default \"false\";\n            description\n                \"A test leaf with a default\";\n        }\n\n        leaf e1 {\n            type empty;\n            description\n                \"A test empty leaf\";\n        }\n    }\n}\n"
  },
  {
    "path": "tests/boolean-empty/run.py",
    "content": "#!/usr/bin/env python\n\nimport unittest\n\nfrom tests.base import PyangBindTestCase\n\n\nclass BooleanEmptyTests(PyangBindTestCase):\n    yang_files = [\"boolean-empty.yang\"]\n\n    def setUp(self):\n        self.boolean_obj = self.bindings.boolean_empty()\n\n    def test_leafs_exist(self):\n        for leaf in [\"b1\", \"b2\", \"e1\"]:\n            with self.subTest(leaf=leaf):\n                self.assertTrue(\n                    hasattr(self.boolean_obj.container, leaf), \"Element did not exist in container (%s)\" % leaf\n                )\n\n    def test_boolean_leaf_accepts_boolean_values(self):\n        for value in [0, \"0\", False, \"false\", \"False\", 1, \"1\", True, \"true\", \"True\"]:\n            with self.subTest(value=value):\n                allowed = True\n                try:\n                    self.boolean_obj.container.b1 = value\n                except ValueError:\n                    allowed = False\n                self.assertTrue(allowed, \"Value of b1 was not correctly set to %s\" % value)\n\n    def test_boolean_leaf_sets_boolean_values_correctly(self):\n        for value in [\n            (0, False),\n            (\"0\", False),\n            (False, False),\n            (\"false\", False),\n            (\"False\", False),\n            (1, True),\n            (\"1\", True),\n            (True, True),\n            (\"true\", True),\n            (\"True\", True),\n        ]:\n            with self.subTest(value=value):\n                try:\n                    self.boolean_obj.container.b1 = value[0]\n                except ValueError:\n                    pass\n                self.assertEqual(\n                    self.boolean_obj.container.b1,\n                    value[1],\n                    \"Value of b1 was not correctly set when compared (%s - set to %s)\"\n                    % (self.boolean_obj.container.b1, value),\n                )\n\n    def test_empty_leaf_accepts_boolean_values(self):\n        for value in [0, \"0\", False, \"false\", \"False\", 1, \"1\", True, \"true\", \"True\"]:\n            with self.subTest(value=value):\n                allowed = True\n                try:\n                    self.boolean_obj.container.e1 = value\n                except ValueError:\n                    allowed = False\n                self.assertTrue(allowed, \"Value of e1 was not correctly set to %s\" % value)\n\n    def test_empty_leaf_sets_boolean_values_correctly(self):\n        for value in [\n            (0, False),\n            (\"0\", False),\n            (False, False),\n            (\"false\", False),\n            (\"False\", False),\n            (1, True),\n            (\"1\", True),\n            (True, True),\n            (\"true\", True),\n            (\"True\", True),\n        ]:\n            with self.subTest(value=value):\n                try:\n                    self.boolean_obj.container.e1 = value[0]\n                except ValueError:\n                    pass\n                self.assertEqual(\n                    self.boolean_obj.container.e1,\n                    value[1],\n                    \"Value of e1 was not correctly set when compared (%s - set to %s)\"\n                    % (self.boolean_obj.container.e1, value),\n                )\n\n    def test_boolean_leaf_default_value(self):\n        self.assertFalse(\n            self.boolean_obj.container.b2._default,\n            \"Value default was not correctly set (%s)\" % self.boolean_obj.container.b2._default,\n        )\n\n    def test_boolean_leaf_is_not_changed_by_default(self):\n        self.assertFalse(\n            self.boolean_obj.container.b2._changed(),\n            \"Value was marked as changed incorrectly (%s)\" % self.boolean_obj.container.b2._changed(),\n        )\n\n    def test_boolean_leaf_sets_changed(self):\n        self.boolean_obj.container.b2 = True\n        self.assertTrue(\n            self.boolean_obj.container.b2._changed(),\n            \"Value was not flagged as changed (%s != True)\" % self.boolean_obj.container.b2._changed(),\n        )\n\n    def test_get(self):\n        self.boolean_obj.container.b1 = True\n        self.boolean_obj.container.e1 = True\n        self.assertEqual(\n            self.boolean_obj.get(),\n            {\"container\": {\"e1\": True, \"b1\": True, \"b2\": False}},\n            \"Wrong result returned from get() (%s)\" % self.boolean_obj.get(),\n        )\n\n\nif __name__ == \"__main__\":\n    unittest.main()\n"
  },
  {
    "path": "tests/choice/__init__.py",
    "content": ""
  },
  {
    "path": "tests/choice/choice.yang",
    "content": "module choice {\n    yang-version \"1\";\n    namespace \"http://rob.sh/yang/test/nested\";\n    prefix \"foo\";\n    organization \"BugReports Inc\";\n    contact \"A bug reporter\";\n\n    description\n        \"A test module\";\n    revision 2014-01-01 {\n        description \"april-fools\";\n        reference \"fooled-you\";\n    }\n\n    container container {\n        description\n            \"A container\";\n\n        choice choice-one {\n            case case-one {\n                container case-one-container {\n                    leaf case-one-leaf {\n                        type int8;\n                    }\n\n                    list user {\n                        key \"username\";\n\n                        leaf username {\n                            type string;\n                        }\n                    }\n\n                }\n            }\n\n            case case-two {\n                container case-two-container {\n                    leaf case-two-leaf {\n                        type int8;\n                    }\n\n                    list user {\n                        key \"username\";\n\n                        leaf username {\n                            type string;\n                        }\n                    }\n                }\n            }\n\n            case case-three {\n                choice choice-three- {\n                    case case-three-one {\n                        container case-three-one-container {\n                            leaf case-three-one-leaf {\n                                type int8;\n                            }\n                        }\n                    }\n                    case case-three-two {\n                        leaf case-three-two-leaf {\n                            type string;\n                        }\n                    }\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "tests/choice/run.py",
    "content": "#!/usr/bin/env python\n\nimport unittest\n\nfrom tests.base import PyangBindTestCase\n\n\nclass ChoicesTests(PyangBindTestCase):\n    yang_files = [\"choice.yang\"]\n\n    def setUp(self):\n        self.choice_obj = self.bindings.choice()\n\n    def test_class_has_container(self):\n        self.assertTrue(hasattr(self.choice_obj, \"container\"), \"Object does not have container\")\n\n    def test_class_has_choice_containers(self):\n        for container in [\"case_one_container\", \"case_two_container\", \"case_three_one_container\"]:\n            with self.subTest(container=container):\n                self.assertTrue(\n                    hasattr(self.choice_obj.container, container),\n                    \"Object does not have choice container %s\" % container,\n                )\n\n    def test_class_does_not_have_choices_as_attributes(self):\n        for choice in [\n            \"choice_one\",\n            \"choice_three\",\n            \"case_one\",\n            \"case_two\",\n            \"case_three_one\",\n            \"case_three_two\",\n        ]:\n            with self.subTest(choice=choice):\n                self.assertFalse(\n                    hasattr(self.choice_obj, choice), \"Object has an erroneous choice option, %s\" % choice\n                )\n\n    def test_case_leaf_default_values(self):\n        for leaf in [\"case_one\", \"case_two\", \"case_three_one\"]:\n            with self.subTest(leaf=leaf):\n                container = getattr(self.choice_obj.container, \"%s_container\" % leaf)\n                value = getattr(container, \"%s_leaf\" % leaf)\n                self.assertEqual(value, 0, \"Object does not have the correct value for %s_leaf, %s\" % (leaf, value))\n\n    def test_set_choice_value(self):\n        for leaf in [\"case_one\", \"case_two\", \"case_three_one\"]:\n            with self.subTest(leaf=leaf):\n                container = getattr(self.choice_obj.container, \"%s_container\" % leaf)\n                value = setattr(container, \"%s_leaf\" % leaf, 42)\n                self.assertEqual(\n                    getattr(container, \"%s_leaf\" % leaf, 0),\n                    42,\n                    \"Object did not specify a value within the choice correctly, %s\" % container,\n                )\n\n    def test_set_choice_value_doesnt_set_other_choices(self):\n        self.choice_obj.container.case_one_container.case_one_leaf = 42\n        self.assertEqual(\n            self.choice_obj.container.case_two_container.case_two_leaf,\n            0,\n            \"Object erroneously set another value within the choice, %s\"\n            % self.choice_obj.container.case_two_container.case_two_leaf,\n        )\n\n    def test_change_choice_value(self):\n        self.choice_obj.container.case_one_container.case_one_leaf = 42\n        self.choice_obj.container.case_two_container.case_two_leaf = 42\n        self.assertEqual(\n            self.choice_obj.container.case_two_container.case_two_leaf,\n            42,\n            \"Object did not allow the other choice field to be specified, %s\"\n            % self.choice_obj.container.case_two_container.case_two_leaf,\n        )\n\n    def test_change_choice_value_resets_other_side(self):\n        self.choice_obj.container.case_one_container.case_one_leaf = 42\n        self.choice_obj.container.case_two_container.case_two_leaf = 42\n        self.assertEqual(\n            self.choice_obj.container.case_one_container.case_one_leaf,\n            0,\n            \"Object did not reset the value of the case-one side of the choice, %s\"\n            % self.choice_obj.container.case_one_container.case_one_leaf,\n        )\n\n    def test_add_to_choice_list_leaf(self):\n        self.choice_obj.container.case_one_container.user.add(\"first\")\n        self.assertEqual(\n            self.choice_obj.container.case_one_container.user[\"first\"].username,\n            \"first\",\n            \"Object has the wrong username for user in case one list\",\n        )\n\n    def test_change_choice_list_to_other_side(self):\n        self.choice_obj.container.case_one_container.user.add(\"first\")\n        self.choice_obj.container.case_two_container.user.add(\"second\")\n        self.assertEqual(\n            self.choice_obj.container.case_two_container.user[\"second\"].username,\n            \"second\",\n            \"Object has the wrong username for user in case two list\",\n        )\n\n    def test_change_choice_list_resets_other_side(self):\n        self.choice_obj.container.case_one_container.user.add(\"first\")\n        self.choice_obj.container.case_two_container.user.add(\"second\")\n        self.assertEqual(\n            len(self.choice_obj.container.case_one_container.user.keys()),\n            0,\n            \"Adding to the second user list did not remove entries from the first\",\n        )\n\n    def test_set_nested_choice(self):\n        for leaf in [\n            self.choice_obj.container.case_three_one_container.case_three_one_leaf,\n            self.choice_obj.container.case_three_two_leaf,\n        ]:\n            with self.subTest(leaf=leaf):\n                leaf = 99\n                self.assertEqual(leaf, 99, \"Nested choice leaf not set\")\n\n\nif __name__ == \"__main__\":\n    unittest.main()\n"
  },
  {
    "path": "tests/config-false/__init__.py",
    "content": ""
  },
  {
    "path": "tests/config-false/config-false.yang",
    "content": "module config-false {\n    yang-version \"1\";\n    namespace \"http://rob.sh/yang/test/nested\";\n    prefix \"foo\";\n    organization \"BugReports Inc\";\n    contact \"A bug reporter\";\n\n    description\n        \"A test module\";\n    revision 2014-01-01 {\n        description \"april-fools\";\n        reference \"fooled-you\";\n    }\n\n    container container {\n        description\n            \"A container\";\n\n        container subone {\n            description\n                \"A nested container\";\n\n            leaf a-leaf {\n                type uint8;\n                description\n                    \"A test leaf\";\n            }\n\n            leaf d-leaf {\n                type uint8;\n                config false;\n                description\n                    \"A test leaf that is config false\";\n            }\n        }\n\n        container subtwo {\n            description\n                \"A nested container with config false\";\n\n            config false;\n            leaf b-leaf {\n                type uint8;\n                description\n                    \"A config false leaf\";\n            }\n\n            container subsubtwo {\n                leaf c-leaf {\n                    type uint8;\n                    description\n                        \"A config false leaf within another container\";\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "tests/config-false/run.py",
    "content": "#!/usr/bin/env python\n\nimport unittest\n\nfrom tests.base import PyangBindTestCase\n\n\nclass ConfigFalseTests(PyangBindTestCase):\n    yang_files = [\"config-false.yang\"]\n\n    def setUp(self):\n        self.test_instance = self.bindings.config_false()\n\n    def test_container_is_configurable_by_default(self):\n        self.assertTrue(self.test_instance.container._is_config)\n\n    def test_set_configurable_leaf_with_non_configurable_sibling(self):\n        allowed = True\n        try:\n            self.test_instance.container.subone.a_leaf = 1\n        except AttributeError:\n            allowed = False\n        self.assertTrue(allowed)\n\n    def test_leaf_is_configurable_by_default(self):\n        self.assertTrue(self.test_instance.container.subone.a_leaf._is_config)\n\n    def test_set_non_configurable_leaf(self):\n        allowed = True\n        try:\n            self.test_instance.container.subone.d_leaf = 1\n        except AttributeError:\n            allowed = False\n        self.assertFalse(allowed)\n\n    def test_container_reports_not_configurable_with_config_false(self):\n        self.assertFalse(self.test_instance.container.subtwo._is_config)\n\n    def test_leaf_reports_not_configurable_with_config_false(self):\n        self.assertFalse(self.test_instance.container.subone.d_leaf._is_config)\n\n    def test_set_leaf_in_non_configurable_container(self):\n        allowed = True\n        try:\n            self.test_instance.container.subtwo.b_leaf = 1\n        except AttributeError:\n            allowed = False\n        self.assertFalse(allowed)\n\n    def test_leaf_in_non_configurable_container_reports_not_configurable(self):\n        self.assertFalse(self.test_instance.container.subtwo.b_leaf._is_config)\n\n    def test_set_leaf_in_sub_container_of_non_configurable_container(self):\n        allowed = True\n        try:\n            self.test_instance.container.subtwo.subsubtwo.c_leaf = 1\n        except AttributeError:\n            allowed = False\n        self.assertFalse(allowed)\n\n    def test_container_in_non_configurable_container_reports_not_configurable(self):\n        self.assertFalse(self.test_instance.container.subtwo.subsubtwo._is_config)\n\n    def test_leaf_in_sub_container_of_non_configurable_container_reports_not_configurable(self):\n        self.assertFalse(self.test_instance.container.subtwo.subsubtwo.c_leaf._is_config)\n\n\nif __name__ == \"__main__\":\n    unittest.main()\n"
  },
  {
    "path": "tests/decimal64/__init__.py",
    "content": ""
  },
  {
    "path": "tests/decimal64/decimal.yang",
    "content": "module decimal {\n    yang-version \"1\";\n    namespace \"http://rob.sh/yang/test/decimal\";\n    prefix \"foo\";\n    organization \"BugReports Inc\";\n    contact \"A bug reporter\";\n\n    description\n        \"A test module\";\n    revision 2014-01-01 {\n        description \"april-fools\";\n        reference \"fooled-you\";\n    }\n\n    container container {\n        leaf d1 {\n            type decimal64 {\n                fraction-digits 2;\n            }\n            description\n                \"A test decimal64 leaf with restricted precision\";\n        }\n\n        leaf d2 {\n            type decimal64 {\n                fraction-digits 3;\n            }\n            default 42.00;\n            description\n                \"A test decimal64 with leaf with a default\";\n        }\n\n        leaf d3 {\n            type decimal64 {\n                fraction-digits 1;\n            }\n            default 1.0;\n            description\n                \"A test decimal64 with restriction & default\";\n        }\n\n        leaf dec64LeafWithRange {\n            type decimal64 {\n                fraction-digits 6;\n                range \"-444.44 | -333.33 .. -222.22 | 111.1 | 444.444 .. 555.555\";\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "tests/decimal64/run.py",
    "content": "#!/usr/bin/env python\n\nfrom decimal import Decimal\nimport unittest\n\nfrom tests.base import PyangBindTestCase\n\n\nclass DecimalTests(PyangBindTestCase):\n    yang_files = [\"decimal.yang\"]\n\n    def setUp(self):\n        self.decimal_obj = self.bindings.decimal_()\n\n    def test_container_has_all_leafs(self):\n        for leaf in [\"d1\", \"d2\", \"d3\"]:\n            with self.subTest(leaf=leaf):\n                self.assertTrue(hasattr(self.decimal_obj.container, leaf), \"Container missing attribute - %s\" % leaf)\n\n    def test_decimal_precision(self):\n        self.decimal_obj.container.d1 = 42.0\n        self.assertEqual(\n            str(self.decimal_obj.container.d1),\n            \"42.00\",\n            \"Precision for d1 was incorrect %s\" % self.decimal_obj.container.d1,\n        )\n\n    def test_decimal_rounding(self):\n        self.decimal_obj.container.d2 = 42.0009\n        self.assertEqual(\n            self.decimal_obj.container.d2,\n            Decimal(\"42.001\"),\n            \"Precision did not result in correct rounding (%s)\" % self.decimal_obj.container.d2,\n        )\n\n    def test_decimal_default_with_extra_precision(self):\n        self.assertEqual(\n            self.decimal_obj.container.d2._default,\n            Decimal(\"42.000\"),\n            \"Default was wrong for d2 (%s)\" % self.decimal_obj.container.d2._default,\n        )\n\n    def test_decimal_default_with_less_precision(self):\n        self.assertEqual(\n            self.decimal_obj.container.d3._default,\n            Decimal(\"1\"),\n            \"Default was set wrong for d3 (%s)\" % self.decimal_obj.container.d3._default,\n        )\n\n    def test_various_values_with_complex_range(self):\n        for value in [\n            (-452.6729, False),\n            (-444.44, True),\n            (-443.22, False),\n            (-330, True),\n            (-222.21, False),\n            (111.2, False),\n            (111.1, True),\n            (446.56, True),\n            (555.55559282, False),\n        ]:\n            with self.subTest(value=value):\n                allowed = True\n                try:\n                    self.decimal_obj.container.dec64LeafWithRange = value[0]\n                except ValueError:\n                    allowed = False\n                self.assertEqual(\n                    allowed,\n                    value[1],\n                    \"Decimal64 leaf with range was not correctly set (%f -> %s != %s)\" % (value[0], allowed, value[1]),\n                )\n\n\nif __name__ == \"__main__\":\n    unittest.main()\n"
  },
  {
    "path": "tests/enumeration/__init__.py",
    "content": ""
  },
  {
    "path": "tests/enumeration/enumeration.yang",
    "content": "module enumeration {\n    yang-version \"1\";\n    namespace \"http://rob.sh/yang/test/enumeration\";\n    prefix \"foo\";\n    organization \"BugReports Inc\";\n    contact \"A bug reporter\";\n\n    description\n        \"A test module\";\n    revision 2014-01-01 {\n        description \"april-fools\";\n        reference \"fooled-you\";\n    }\n\n    container \"container\" {\n        description\n          \"A test container\";\n\n        leaf e {\n            type enumeration {\n                enum one;\n                enum two {\n                    value 42;\n                }\n                enum three;\n            }\n        }\n\n       leaf f {\n            type enumeration {\n                enum a;\n                enum b;\n                enum c;\n            }\n            default c;\n        }\n    }\n}\n"
  },
  {
    "path": "tests/enumeration/run.py",
    "content": "#!/usr/bin/env python\n\nimport unittest\n\nfrom tests.base import PyangBindTestCase\n\n\nclass EnumerationTests(PyangBindTestCase):\n    yang_files = [\"enumeration.yang\"]\n\n    def setUp(self):\n        self.enum_obj = self.bindings.enumeration()\n\n    def test_container_has_all_leafs(self):\n        for leaf in [\"e\", \"f\"]:\n            with self.subTest(leaf=leaf):\n                self.assertTrue(\n                    hasattr(self.enum_obj.container, leaf), \"Container does not contain enumeration %s\" % leaf\n                )\n\n    def test_assign_to_enum(self):\n        self.enum_obj.container.e = \"one\"\n        self.assertEqual(\n            self.enum_obj.container.e,\n            \"one\",\n            \"Enumeration value was not correctly set (%s)\" % self.enum_obj.container.e,\n        )\n\n    def test_enum_does_not_allow_invalid_value(self):\n        allowed = True\n        try:\n            self.enum_obj.container.e = \"twentyseven\"\n        except ValueError:\n            allowed = False\n        self.assertFalse(\n            allowed, \"Erroneous value was not caught by restriction handler (%s)\" % self.enum_obj.container.e\n        )\n\n    def test_enum_default_value(self):\n        self.assertEqual(\n            self.enum_obj.container.f._default,\n            \"c\",\n            \"Erroneous default value for 'f' (%s)\" % self.enum_obj.container.f._default,\n        )\n\n    def test_static_enum_value(self):\n        self.enum_obj.container.e = \"two\"\n        self.assertEqual(\n            self.enum_obj.container.e.getValue(mapped=True),\n            42,\n            \"Erroneously statically defined value returned (%s)\" % self.enum_obj.container.e.getValue(mapped=True),\n        )\n\n\nif __name__ == \"__main__\":\n    unittest.main()\n"
  },
  {
    "path": "tests/extensions/__init__.py",
    "content": ""
  },
  {
    "path": "tests/extensions/extdef-irr.yang",
    "content": "module extdef-irr {\n    yang-version \"1\";\n    namespace \"http://rob.sh/yang/test/extdef-irr\";\n    prefix \"foo\";\n    organization \"BugReports Inc\";\n    contact \"A bug reporter\";\n\n    description\n        \"A test module\";\n    revision 2014-01-01 {\n        description \"april-fools\";\n        reference \"fooled-you\";\n    }\n\n    extension extension-four {\n        argument \"value\";\n    }\n\n}\n"
  },
  {
    "path": "tests/extensions/extdef-two.yang",
    "content": "module extdef-two {\n    yang-version \"1\";\n    namespace \"http://rob.sh/yang/test/extdef-two\";\n    prefix \"foo\";\n    organization \"BugReports Inc\";\n    contact \"A bug reporter\";\n\n    description\n        \"A test module\";\n    revision 2014-01-01 {\n        description \"april-fools\";\n        reference \"fooled-you\";\n    }\n\n    extension extension-three {\n        argument \"extname\";\n    }\n\n}\n"
  },
  {
    "path": "tests/extensions/extdef.yang",
    "content": "module extdef {\n    yang-version \"1\";\n    namespace \"http://rob.sh/yang/test/extdef\";\n    prefix \"foo\";\n    organization \"BugReports Inc\";\n    contact \"A bug reporter\";\n\n    description\n        \"A test module\";\n    revision 2014-01-01 {\n        description \"april-fools\";\n        reference \"fooled-you\";\n    }\n\n    extension extension-one {\n        argument \"extname\";\n    }\n\n    extension extension-two {\n        argument \"extval\";\n    }\n\n}\n"
  },
  {
    "path": "tests/extensions/extensions.yang",
    "content": "module extensions {\n    yang-version \"1\";\n    namespace \"http://rob.sh/yang/test/extensions\";\n    prefix \"foo\";\n\n    import extdef { prefix \"ext\"; }\n    import extdef-two { prefix \"two\"; }\n    import extdef-irr { prefix \"irr\"; }\n\n    organization \"BugReports Inc\";\n    contact \"A bug reporter\";\n\n    description\n        \"A test module\";\n    revision 2014-01-01 {\n        description \"april-fools\";\n        reference \"fooled-you\";\n    }\n\n    container test {\n        ext:extension-one \"version\";\n\n        leaf one {\n            type string;\n        }\n    }\n\n    container test-two {\n        leaf two {\n            type string;\n            ext:extension-two \"value\";\n        }\n    }\n\n    list l {\n        key \"k\";\n\n        leaf k {\n            type int8;\n            ext:extension-two \"from-leaf\";\n            ext:extension-one \"from-leaf\";\n        }\n\n        leaf q {\n            type uint64;\n            ext:extension-two \"from-q\";\n            two:extension-three \"from-q\";\n            irr:extension-four \"from-q\";\n        }\n\n        ext:extension-two \"from-list\";\n        ext:extension-two \"from-list\";\n    }\n\n}\n"
  },
  {
    "path": "tests/extensions/run.py",
    "content": "#!/usr/bin/env python\nfrom __future__ import unicode_literals\n\nimport unittest\n\nfrom tests.base import PyangBindTestCase\n\n\nclass ExtensionsTests(PyangBindTestCase):\n    yang_files = [\"extensions.yang\"]\n    pyang_flags = [\"--interesting-extension=extdef\", \"--interesting-extension=extdef-two\"]\n    maxDiff = None\n\n    def setUp(self):\n        self.ext_obj = self.bindings.extensions()\n\n    def test_extensions_get_added_to_container(self):\n        self.assertEqual(\n            self.ext_obj.test._extensions(),\n            {\"extdef\": {\"extension-one\": \"version\"}},\n            \"Did not extract extensions correctly from container object (%s)\" % self.ext_obj.test._extensions(),\n        )\n\n    def test_extensions_are_not_added_to_leaf_with_none_specified(self):\n        self.assertIsNone(\n            self.ext_obj.test.one._extensions(),\n            \"Incorrectly found extensions for a leaf with none specified (%s)\" % self.ext_obj.test.one._extensions(),\n        )\n\n    def test_extensions_are_not_added_to_container_with_none_specified(self):\n        self.assertIsNone(\n            self.ext_obj.test_two._extensions(),\n            \"Incorrectly found extensions for a container with none specified (%s)\"\n            % self.ext_obj.test_two._extensions(),\n        )\n\n    def test_extensions_get_added_to_leaf(self):\n        self.assertEqual(\n            self.ext_obj.test_two.two._extensions(),\n            {\"extdef\": {\"extension-two\": \"value\"}},\n            \"Did not extract extensions correctly for a leaf (%s)\" % self.ext_obj.test_two.two._extensions(),\n        )\n\n    def test_extensions_get_added_to_list(self):\n        self.assertEqual(\n            self.ext_obj.l._extensions(),\n            {\"extdef\": {\"extension-two\": \"from-list\"}},\n            \"Did not extract extensions correctly for a list (%s)\" % self.ext_obj.l._extensions(),\n        )\n\n    def test_extensions_get_added_to_list_member(self):\n        x = self.ext_obj.l.add(1)\n        self.assertEqual(\n            x._extensions(),\n            {\"extdef\": {\"extension-two\": \"from-list\"}},\n            \"Did not extract extensions correctly for list member (%s)\" % x._extensions(),\n        )\n\n    def test_proper_extensions_get_added_to_list_leaf(self):\n        extensions = {\n            \"k\": {\"extdef\": {\"extension-one\": \"from-leaf\", \"extension-two\": \"from-leaf\"}},\n            \"q\": {\"extdef\": {\"extension-two\": \"from-q\"}, \"extdef-two\": {\"extension-three\": \"from-q\"}},\n        }\n        x = self.ext_obj.l.add(1)\n        for leaf in extensions:\n            with self.subTest(leaf=leaf):\n                leaf_exts = getattr(x, leaf)._extensions()\n                self.assertEqual(leaf_exts, extensions[leaf])\n\n\nif __name__ == \"__main__\":\n    unittest.main()\n"
  },
  {
    "path": "tests/extmethods/__init__.py",
    "content": ""
  },
  {
    "path": "tests/extmethods/extmethods.yang",
    "content": "module extmethods {\n    yang-version \"1\";\n    namespace \"http://rob.sh/yang/test/extmethods\";\n    prefix \"foo\";\n    organization \"BugReports Inc\";\n    contact \"A bug reporter\";\n\n    description\n        \"A test module\";\n    revision 2014-01-01 {\n        description \"april-fools\";\n        reference \"fooled-you\";\n    }\n\n    container item {\n        leaf one {\n            type string;\n        }\n    }\n}\n"
  },
  {
    "path": "tests/extmethods/run.py",
    "content": "#!/usr/bin/env python\n\nimport unittest\n\nfrom tests.base import PyangBindTestCase\n\n\nclass extmethodcls(object):\n    def commit(self, *args, **kwargs):\n        return \"COMMIT_CALLED\"\n\n    def presave(self, *args, **kwargs):\n        return \"PRESAVE_CALLED\"\n\n    def postsave(self, *args, **kwargs):\n        return \"POSTSAVE_CALLED\"\n\n    def oam_check(self, *args, **kwargs):\n        return \"OAM_CHECK_CALLED\"\n\n    def echo(self, *args, **kwargs):\n        return {\"args\": args, \"kwargs\": kwargs}\n\n\nclass ExtMethodsTests(PyangBindTestCase):\n    yang_files = [\"extmethods.yang\"]\n    pyang_flags = [\"--use-extmethods\"]\n\n    def setUp(self):\n        self.instance = self.bindings.extmethods(extmethods={\"/item/one\": extmethodcls()})\n\n    def test_extmethods_get_created_on_leafs(self):\n        for method_name, valid in [\n            (\"commit\", True),\n            (\"presave\", True),\n            (\"postsave\", True),\n            (\"oam_check\", True),\n            (\"doesnotexist\", False),\n        ]:\n            with self.subTest(method_name=method_name, valid=valid):\n                method = getattr(self.instance.item.one, \"_%s\" % method_name, None)\n                self.assertEqual((method is not None), valid)\n\n    def test_extmethods_return_expected_values(self):\n        for method_name, retval in [\n            (\"commit\", \"COMMIT_CALLED\"),\n            (\"presave\", \"PRESAVE_CALLED\"),\n            (\"postsave\", \"POSTSAVE_CALLED\"),\n            (\"oam_check\", \"OAM_CHECK_CALLED\"),\n        ]:\n            with self.subTest(method_name=method_name, retval=retval):\n                method = getattr(self.instance.item.one, \"_%s\" % method_name, None)\n                self.assertEqual(method(), retval)\n\n    def test_args_and_kwargs_pass_to_extmethods_properly(self):\n        expected_return = {\"args\": (\"one\",), \"kwargs\": {\"caller\": [\"item\", \"one\"], \"two\": 2, \"path_helper\": False}}\n        self.assertEqual(self.instance.item.one._echo(\"one\", two=2), expected_return)\n\n    def test_kwargs_passed_to_extmethods_do_not_set_invalid_attributes(self):\n        self.instance.item.one._echo(\"one\", two=2)\n        with self.assertRaises(AttributeError):\n            self.instance.item.two\n\n\nif __name__ == \"__main__\":\n    unittest.main()\n"
  },
  {
    "path": "tests/identityref/__init__.py",
    "content": ""
  },
  {
    "path": "tests/identityref/identityref.yang",
    "content": "module identityref {\n    yang-version \"1.1\";\n    namespace \"http://rob.sh/yang/test/identityref\";\n    prefix \"foo\";\n\n    import remote { prefix defn; }\n\n    organization \"BugReports Inc\";\n    contact \"A bug reporter\";\n\n    description\n        \"A test module\";\n    revision 2014-01-01 {\n        description \"april-fools\";\n        reference \"fooled-you\";\n    }\n\n    identity base-identity {\n        description \"an identity base\";\n    }\n\n    identity option-one {\n        base \"base-identity\";\n        description \"option one on the base\";\n    }\n\n    identity option-two {\n        base \"base-identity\";\n        description \"option two on the base\";\n    }\n\n    identity grandfather;\n\n    identity father {\n        base grandfather;\n    }\n\n    identity son {\n        base father;\n        base mother;\n    }\n\n    identity greatgrandmother;\n\n    identity grandmother {\n        base greatgrandmother;\n    }\n\n    identity mother {\n        base grandmother;\n    }\n\n    identity aunt {\n        base grandmother;\n    }\n\n    identity cousin {\n        base aunt;\n    }\n\n    identity daughter {\n        base mother;\n        base father;\n    }\n\n    identity local-base;\n\n    container test-container {\n        leaf id_base {\n            type identityref {\n                base base-identity;\n            }\n        }\n\n        leaf id_remote {\n            type identityref {\n                base defn:remote-base;\n            }\n        }\n\n        leaf grandfather {\n            type identityref {\n                base grandfather;\n            }\n        }\n\n        leaf greatgrandmother {\n            type identityref {\n                base greatgrandmother;\n            }\n        }\n\n        leaf mother {\n            type identityref {\n                base mother;\n            }\n        }\n\n        leaf grandmother {\n            type identityref {\n                base grandmother;\n            }\n        }\n\n        leaf grandparent {\n            type identityref {\n                base grandfather;\n                base grandmother;\n            }\n        }\n    }\n\n    // Example supplied by Ashish Kumar in issue #41\n    identity address-family {\n        description\n          \"Base identity from which address\n           families are derived.\";\n    }\n    identity lcaf {\n        base address-family;\n        description\n          \"address family.\";\n    }\n    identity source-dest {\n        base lcaf;\n        description\n          \"Source/Dest LCAF type.\";\n    }\n    typedef address-family-ref {\n        type identityref {\n          base address-family;\n        }\n        description\n          \"address family reference.\";\n    }\n\n    grouping address {\n        description\n          \"Generic address.\";\n        leaf address-type {\n          type address-family-ref;\n          mandatory true;\n          description\n            \"Type of the address.\";\n        }\n    }\n\n    container ak {\n        uses address;\n    }\n\n    container ietfint {\n        leaf ref {\n            type identityref {\n                base local-base;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "tests/identityref/remote-two.yang",
    "content": "module remote-two {\n    yang-version \"1\";\n    namespace \"http://rob.sh/yang/test/typedef/remote-two\";\n    prefix \"remote-two\";\n\n    import identityref { prefix \"i\"; }\n\n    organization \"BugReports Inc\";\n    contact \"A bug reporter\";\n    description\n        \"A test module\";\n    revision 2014-01-01 {\n        description \"april-fools\";\n        reference \"fooled-you\";\n    }\n\n    identity remote-id {\n      base \"i:local-base\";\n    }\n}\n"
  },
  {
    "path": "tests/identityref/remote.yang",
    "content": "module remote {\n    yang-version \"1\";\n    namespace \"http://rob.sh/yang/test/typedef/remote\";\n    prefix \"remote\";\n    organization \"BugReports Inc\";\n    contact \"A bug reporter\";\n\n    description\n        \"A test module\";\n    revision 2014-01-01 {\n        description \"april-fools\";\n        reference \"fooled-you\";\n    }\n\n    identity remote-one {\n        description \"remote definition one\";\n        base remote-base;\n    }\n\n    identity remote-two {\n        description \"remote defintion two\";\n        base remote-base;\n    }\n\n    identity remote-base {\n        description\n            \"an identity in another namespace\n             also defined out of order just to\n             check that this works\";\n    }\n}\n"
  },
  {
    "path": "tests/identityref/run.py",
    "content": "#!/usr/bin/env python\n\nimport json\nimport unittest\n\nfrom pyangbind.lib import pybindJSON\nfrom pyangbind.lib.serialise import pybindJSONDecoder\nfrom tests.base import PyangBindTestCase\n\n\nclass IdentityRefTests(PyangBindTestCase):\n    yang_files = [\"identityref.yang\", \"remote-two.yang\"]\n\n    def setUp(self):\n        self.instance = self.bindings.identityref()\n\n    def test_identityref_leafs_get_created(self):\n        for leaf in [\"id_base\", \"id_remote\"]:\n            with self.subTest(leaf=leaf):\n                self.assertTrue(hasattr(self.instance.test_container, leaf))\n\n    def test_cant_assign_invalid_string_to_identityref(self):\n        with self.assertRaises(ValueError):\n            self.instance.test_container.grandfather = \"hello\"\n\n    def test_identityref_leafs_are_blank_by_default(self):\n        for leaf in [\"id_base\", \"id_remote\"]:\n            with self.subTest(leaf=leaf):\n                self.assertEqual(getattr(self.instance.test_container, leaf), \"\")\n\n    def test_identityref_accepts_valid_identity_values(self):\n        for identity in [\"option-one\", \"option-two\"]:\n            with self.subTest(identity=identity):\n                allowed = True\n                try:\n                    self.instance.test_container.id_base = identity\n                except ValueError:\n                    allowed = False\n                self.assertTrue(allowed)\n\n    def test_remote_identityref_accepts_valid_identity_values(self):\n        for identity in [\"remote-one\", \"remote-two\"]:\n            with self.subTest(identity=identity):\n                allowed = True\n                try:\n                    self.instance.test_container.id_remote = identity\n                except ValueError:\n                    allowed = False\n                self.assertTrue(allowed)\n\n    def test_set_ancestral_identities_one(self):\n        for identity, valid in [\n            (\"father\", True),\n            (\"son\", True),\n            (\"daughter\", True),\n            (\"mother\", False),\n            (\"foo:father\", True),\n            (\"foo:son\", True),\n            (\"elephant\", False),\n            (\"hamster\", False),\n        ]:\n            with self.subTest(identity=identity, valid=valid):\n                allowed = True\n                try:\n                    self.instance.test_container.grandfather = identity\n                except ValueError:\n                    allowed = False\n                self.assertEqual(allowed, valid, identity)\n\n    def test_set_ancestral_identities_two(self):\n        for identity, valid in [\n            (\"grandmother\", True),\n            (\"mother\", True),\n            (\"niece\", False),\n            (\"aunt\", True),\n            (\"cousin\", True),\n            (\"daughter\", True),\n            (\"son\", True),\n            (\"father\", False),\n            (\"grandfather\", False),\n        ]:\n            with self.subTest(identity=identity, valid=valid):\n                allowed = True\n                try:\n                    self.instance.test_container.greatgrandmother = identity\n                except ValueError:\n                    allowed = False\n                self.assertEqual(allowed, valid, identity)\n\n    def test_set_ancestral_identities_three(self):\n        for identity, valid in [(\"daughter\", True), (\"son\", True), (\"cousin\", False), (\"aunt\", False)]:\n            with self.subTest(identity=identity, valid=valid):\n                allowed = True\n                try:\n                    self.instance.test_container.mother = identity\n                except ValueError:\n                    allowed = False\n                self.assertEqual(allowed, valid, identity)\n\n    def test_set_ancestral_identities_four(self):\n        for identity, valid in [\n            (\"daughter\", True),\n            (\"son\", True),\n            (\"cousin\", True),\n            (\"mother\", True),\n            (\"father\", False),\n            (\"aunt\", True),\n            (\"greatgrandmother\", False),\n        ]:\n            with self.subTest(identity=identity, valid=valid):\n                allowed = True\n                try:\n                    self.instance.test_container.grandmother = identity\n                except ValueError:\n                    allowed = False\n                self.assertEqual(allowed, valid, identity)\n\n    def test_set_ancestral_identities_five(self):\n        for identity, valid in [\n            (\"mother\", False),\n            (\"father\", True),\n            (\"cousin\", False),\n            (\"son\", True),\n        ]:\n            with self.subTest(identity=identity, valid=valid):\n                allowed = True\n                try:\n                    self.instance.test_container.grandparent = identity\n                except ValueError:\n                    allowed = False\n                self.assertEqual(allowed, valid, identity)\n\n    def test_grouping_identity_inheritance(self):\n        for address_type, valid in [\n            (\"source-dest\", True),\n            (\"lcaf\", True),\n            (\"unknown\", False),\n            (\"identityref:source-dest\", True),\n        ]:\n            with self.subTest(address_type=address_type, valid=valid):\n                allowed = True\n                try:\n                    self.instance.ak.address_type = address_type\n                except ValueError:\n                    allowed = False\n                self.assertEqual(allowed, valid)\n\n    def test_set_identityref_from_imported_module(self):\n        for identity, valid in [\n            (\"remote:remote-one\", True),\n            (\"fordprefect:remote-one\", False),\n            (\"remote:remote-two\", True),\n        ]:\n            with self.subTest(identity=identity, valid=valid):\n                allowed = True\n                try:\n                    self.instance.test_container.id_remote = identity\n                except ValueError:\n                    allowed = False\n                self.assertEqual(allowed, valid)\n\n    def test_set_identityref_from_imported_module_referencing_local(self):\n        for identity, valid in [(\"remote-id\", True), (\"remote-two:remote-id\", True), (\"invalid\", False)]:\n            with self.subTest(identity=identity, valid=valid):\n                allowed = True\n                try:\n                    self.instance.ietfint.ref = identity\n                except ValueError:\n                    allowed = False\n                self.assertEqual(allowed, valid)\n\n    def test_json_ietf_serialise_namespace_handling_remote(self):\n        for identity in [\"remote-id\", \"remote-two:remote-id\"]:\n            with self.subTest(identity=identity):\n                self.instance.ietfint.ref = identity\n                data = json.loads(pybindJSON.dumps(self.instance, mode=\"ietf\"))\n                # The JSON representation of the identityref must have a namespace, as\n                # the leaf `ref` and the identity `remote-id` are defined in two separate\n                # modules\n                self.assertEqual(\n                    data[\"identityref:ietfint\"][\"ref\"],\n                    \"remote-two:remote-id\",\n                )\n\n    def test_json_ietf_serialise_namespace_handling_local(self):\n        for identity in [\"lcaf\", \"identityref:lcaf\"]:\n            with self.subTest(identity=identity):\n                self.instance.ak.address_type = \"lcaf\"\n                data = json.loads(pybindJSON.dumps(self.instance, mode=\"ietf\"))\n                # The JSON representation of the identityref may have, or may omit,\n                # the namespace, as the leaf `address-type` and the identity `lcaf` are\n                # defined in the same module, so accept either form\n                self.assertIn(\n                    data[\"identityref:ak\"][\"address-type\"],\n                    [\"lcaf\", \"identityref:lcaf\"],\n                )\n\n    def test_load_identityref_with_module_prefix(self):\n        json = {\n            \"identityref:ak\": {\n                \"address-type\": \"identityref:source-dest\",\n            }\n        }\n        obj = pybindJSONDecoder.load_ietf_json(json, self.bindings, \"identityref\")\n        self.assertIn(\n            obj.ak.address_type,\n            [\"identityref:source-dest\", \"source-dest\"],\n        )\n\n\nif __name__ == \"__main__\":\n    unittest.main()\n"
  },
  {
    "path": "tests/include-import/__init__.py",
    "content": ""
  },
  {
    "path": "tests/include-import/include-import.yang",
    "content": "module include-import {\n    yang-version \"1\";\n    namespace \"http://rob.sh/yang/test/typedef/remote\";\n    prefix \"iil\";\n    include subm;\n    import remote { prefix rem; }\n    organization \"BugReports Inc\";\n    contact \"A bug reporter\";\n\n\n    description\n        \"A test module\";\n    revision 2014-01-01 {\n        description \"april-fools\";\n        reference \"fooled-you\";\n    }\n\n    typedef ii-definition {\n        type string;\n    }\n\n    identity fish;\n    \n    identity mackerel {\n        base \"fish\";\n    }\n\n    identity bear {\n        base \"rem:mammal\";\n    }\n\n    container c {\n        leaf t1 { \n            type ii-definition;\n        }\n        \n        leaf t2 {\n            type subm-definition;\n        }\n\n        leaf t3 {\n            type rem:remote-definition;\n        }\n    }\n}\n"
  },
  {
    "path": "tests/include-import/remote.yang",
    "content": "module remote {\n    yang-version \"1\";\n    namespace \"http://rob.sh/yang/test/include-import/remote\";\n    prefix \"remote\";\n    organization \"BugReports Inc\";\n    contact \"A bug reporter\";\n\n    description\n        \"A test module\";\n    revision 2014-01-01 {\n        description \"april-fools\";\n        reference \"fooled-you\";\n    }\n\n    identity mammal; \n\n    typedef remote-definition {\n        type string;\n    }\n}\n"
  },
  {
    "path": "tests/include-import/run.py",
    "content": "#!/usr/bin/env python\n\nimport unittest\n\nfrom tests.base import PyangBindTestCase\n\n\nclass IncludeImportTests(PyangBindTestCase):\n    yang_files = [\"include-import.yang\"]\n\n    def setUp(self):\n        self.yang_obj = self.bindings.include_import()\n\n    def test_all_the_things_build(self):\n        \"\"\"This test intentionally left blank.\"\"\"\n        pass\n\n\nif __name__ == \"__main__\":\n    unittest.main()\n"
  },
  {
    "path": "tests/include-import/subm.yang",
    "content": "submodule subm {\n    belongs-to include-import { prefix ii; }\n\n    description\n        \"A test module\";\n    revision 2014-01-01 {\n        description \"april-fools\";\n        reference \"fooled-you\";\n    }\n\n    typedef subm-definition {\n        type string;\n    }\n}\n"
  },
  {
    "path": "tests/int/__init__.py",
    "content": ""
  },
  {
    "path": "tests/int/int.yang",
    "content": "module int {\n    yang-version \"1\";\n    namespace \"http://rob.sh/yang/test/uint\";\n    prefix \"foo\";\n    organization \"BugReports Inc\";\n    contact \"A bug reporter\";\n\n    description\n        \"A test module\";\n    revision 2014-01-01 {\n        description \"april-fools\";\n        reference \"fooled-you\";\n    }\n\n    container int-container {\n        description\n            \"A container\";\n\n        leaf eight {\n            type int8;\n            description\n              \"A test leaf for uint8\";\n        }\n\n        leaf eightdefault {\n            type int8;\n            default 127;\n            description\n              \"A test leaf for uint8 with a default\";\n        }\n\n        leaf eightresult {\n            type int8;\n            description\n              \"A test leaf that stores results of operations\";\n        }\n\n        leaf sixteen {\n            type int16;\n            description\n              \"A test leaf for uint16\";\n        }\n\n        leaf sixteendefault {\n            type int16;\n            default 32767;\n            description\n              \"A test leaf for uint16 with a default\";\n        }\n\n        leaf sixteenresult {\n            type int16;\n            description\n              \"A test leaf that stores results of operations\";\n        }\n\n        leaf thirtytwo {\n            type int32;\n            description\n              \"A test leaf for uint32\";\n        }\n\n        leaf thirtytwodefault {\n            type int32;\n            default 2147483647;\n            description\n              \"A test leaf for uint32 with a default\";\n        }\n\n        leaf thirtytworesult {\n            type int32;\n            description\n              \"A test leaf that stores results of operations\";\n        }\n\n        leaf sixtyfour {\n            type int64;\n            description\n              \"A test leaf for uint16\";\n        }\n\n        leaf sixtyfourdefault {\n            type int64;\n            default 9223372036854775807;\n            description\n              \"A test leaf for uint16 with a default\";\n        }\n\n        leaf sixtyfourresult {\n            type int64;\n            description\n              \"A test leaf that stores results of operations\";\n        }\n\n        leaf eightrestricted {\n            type int8 {\n                range -42..10;\n            }\n            description\n              \"A test uint8 that has restricted range\";\n        }\n\n        leaf sixteenrestricted {\n            type int16 {\n                range -42..1000;\n            }\n            description\n              \"A test uint16 that has a restricted range\";\n        }\n\n        leaf thirtytworestricted {\n            type int32 {\n                range -42..500000;\n            }\n            description\n              \"A test uint32 that has a restricted range\";\n        }\n\n        leaf sixtyfourrestricted {\n            type int64 {\n                range -42..72036854775807;\n            }\n            description\n              \"A test uint32 that has a restricted range\";\n        }\n\n        leaf restricted-ueight-max {\n            type int8 {\n                range \"0..max\";\n            }\n        }\n\n        leaf restricted-ueight-min {\n            type int8 {\n                range \"-128..max\";\n            }\n        }\n\n        leaf restricted-ueight-min-alias {\n            type int8 {\n                range \"min..max\";\n            }\n        }\n\n        leaf complex-range {\n            type int8 {\n                range \"0..10|15..20\";\n            }\n        }\n\n        leaf complex-range-two {\n            type int8 {\n                range \"0..10 | 11..12 | 16..18\";\n            }\n        }\n\n        leaf complex-range-with-negative {\n            type int8 {\n                range \"-10..20 | 25..30\";\n            }\n        }\n\n        leaf intLeafWithRange {\n            type int8 {\n                range \"-10 .. -5 | 10 .. 30\";\n            }\n        }\n\n        leaf complex-range-with-equals-case {\n            type int16 {\n                range \"-42..42 | 100..154 | 255\";\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "tests/int/run.py",
    "content": "#!/usr/bin/env python\n\nimport unittest\n\nfrom tests.base import PyangBindTestCase\n\n\nclass IntTests(PyangBindTestCase):\n    yang_files = [\"int.yang\"]\n\n    def setUp(self):\n        self.int_obj = self.bindings.int_()\n\n    def test_all_leafs_are_present(self):\n        for name in [\"eight\", \"sixteen\", \"thirtytwo\", \"sixtyfour\"]:\n            for subname in [\"\", \"default\", \"result\", \"restricted\"]:\n                with self.subTest(name=name, subname=subname):\n                    self.assertTrue(\n                        hasattr(self.int_obj.int_container, \"%s%s\" % (name, subname)),\n                        \"missing %s%s from container\" % (name, subname),\n                    )\n\n    def test_default_values(self):\n        for pair in [\n            (\"eightdefault\", 2**7 - 1),\n            (\"sixteendefault\", 2**15 - 1),\n            (\"thirtytwodefault\", 2**31 - 1),\n            (\"sixtyfourdefault\", 2**63 - 1),\n        ]:\n            with self.subTest(pair=pair):\n                default_val = getattr(self.int_obj.int_container, pair[0])._default\n                self.assertEqual(\n                    default_val,\n                    pair[1],\n                    \"default incorrectly set for %s, expected: %d, got %d\" % (pair[0], pair[1], default_val),\n                )\n\n    def test_set_int_values(self):\n        for leaf in [\"eight\", \"sixteen\", \"thirtytwo\", \"sixtyfour\"]:\n            with self.subTest(leaf=leaf):\n                setattr(self.int_obj.int_container, leaf, 42)\n                value = getattr(self.int_obj.int_container, leaf)\n                self.assertEqual(value, 42, \"incorrectly set %s, expected 42, got %d\" % (leaf, value))\n\n    def test_set_int_values_marks_changed(self):\n        for leaf in [\"eight\", \"sixteen\", \"thirtytwo\", \"sixtyfour\"]:\n            with self.subTest(leaf=leaf):\n                setattr(self.int_obj.int_container, leaf, 42)\n                leaf_ref = getattr(self.int_obj.int_container, leaf)\n                self.assertTrue(leaf_ref._changed(), \"incorrect changed flag for %s\" % leaf)\n\n    def test_leaf_math_and_negatives(self):\n        leafs = [\"eight\", \"sixteen\", \"thirtytwo\", \"sixtyfour\"]\n        for leaf in leafs:\n            setattr(self.int_obj.int_container, leaf, 42)\n\n        # Setting these dynamically in a loop just doesn't seem worth the effort of figuring out.\n        self.int_obj.int_container.eight *= -1\n        self.int_obj.int_container.sixteen *= -1\n        self.int_obj.int_container.thirtytwo *= -1\n        self.int_obj.int_container.sixtyfour *= -1\n\n        for leaf in leafs:\n            with self.subTest(leaf=leaf):\n                value = getattr(self.int_obj.int_container, leaf)\n                self.assertEqual(value, -42, \"Incorrectly set %s, expected 42, got %d\" % (leaf, value))\n\n    def test_set_restricted_values_within_allowed_range(self):\n        for leaf in [\"eightrestricted\", \"sixteenrestricted\", \"thirtytworestricted\", \"sixtyfourrestricted\"]:\n            with self.subTest(leaf=leaf):\n                allowed = True\n                try:\n                    setattr(self.int_obj.int_container, leaf, 10)\n                except ValueError:\n                    allowed = False\n                self.assertTrue(allowed, \"Could not set value of %s to 10\" % leaf)\n\n    def test_set_values_outside_allowed_range(self):\n        for pair in [\n            (\"eightrestricted\", -100),\n            (\"eightrestricted\", 1001),\n            (\"sixteenrestricted\", -43),\n            (\"sixteenrestricted\", 1001),\n            (\"thirtytworestricted\", 500001),\n            (\"thirtytworestricted\", -43),\n            (\"sixtyfourrestricted\", 72036854775809),\n            (\"sixtyfourrestricted\", -43),\n        ]:\n            with self.subTest(pair=pair):\n                allowed = True\n                try:\n                    setattr(self.int_obj.int_container, pair[0], pair[1])\n                except ValueError:\n                    allowed = False\n                self.assertFalse(\n                    allowed, \"Incorrectly allowed value outside of range for %s (%d)\" % (pair[0], pair[1])\n                )\n\n    def test_int8_max_range(self):\n        for pair in [(0, True), (10, True), (-10, False)]:\n            with self.subTest(pair=pair):\n                allowed = True\n                try:\n                    self.int_obj.int_container.restricted_ueight_max = pair[0]\n                except ValueError:\n                    allowed = False\n                self.assertEqual(\n                    allowed,\n                    pair[1],\n                    \"Restricted range using max was not set correctly (%d -> %s != %s)\" % (pair[0], allowed, pair[1]),\n                )\n\n    def test_int8_min_range(self):\n        for pair in [(0, True), (10, True), (-128, True), (-300, False)]:\n            with self.subTest(pair=pair):\n                allowed = True\n                try:\n                    self.int_obj.int_container.restricted_ueight_min = pair[0]\n                except ValueError:\n                    allowed = False\n                self.assertEqual(\n                    allowed,\n                    pair[1],\n                    \"Restricted range using min was not set correctly (%s -> %s != %s)\" % (pair[0], allowed, pair[1]),\n                )\n\n    def test_int8_min_range_alias(self):\n        for pair in [(0, True), (10, True), (-128, True), (-300, False)]:\n            with self.subTest(pair=pair):\n                allowed = True\n                try:\n                    self.int_obj.int_container.restricted_ueight_min = pair[0]\n                except ValueError:\n                    allowed = False\n                self.assertEqual(\n                    allowed,\n                    pair[1],\n                    \"Restricted range using min alias was not set correctly (%s -> %s != %s)\"\n                    % (pair[0], allowed, pair[1]),\n                )\n\n    def test_complex_range_with_two_segments(self):\n        for pair in [(0, True), (13, False), (-20, False), (5, True), (16, True)]:\n            with self.subTest(pair=pair):\n                allowed = True\n                try:\n                    self.int_obj.int_container.complex_range = pair[0]\n                except ValueError:\n                    allowed = False\n                self.assertEqual(\n                    allowed,\n                    pair[1],\n                    \"Complex range was not set correctly (%d -> %s != %s)\" % (pair[0], allowed, pair[1]),\n                )\n\n    def test_complex_range_with_three_segments_and_spaces(self):\n        for pair in [(0, True), (13, False), (-20, False), (5, True), (16, True)]:\n            with self.subTest(pair=pair):\n                allowed = True\n                try:\n                    self.int_obj.int_container.complex_range_two = pair[0]\n                except ValueError:\n                    allowed = False\n                self.assertEqual(\n                    allowed,\n                    pair[1],\n                    \"Complex range with spaces and three elements was not set correctly (%d -> %s != %s)\"\n                    % (pair[0], allowed, pair[1]),\n                )\n\n    def test_complex_range_with_negative(self):\n        for pair in [(-2, True), (42, False)]:\n            with self.subTest(pair=pair):\n                allowed = True\n                try:\n                    self.int_obj.int_container.complex_range_with_negative = pair[0]\n                except ValueError:\n                    allowed = False\n                self.assertEqual(\n                    allowed,\n                    pair[1],\n                    \"Complex range with negatives was not set correctly (%d -> %s != %s)\"\n                    % (pair[0], allowed, pair[1]),\n                )\n\n    def test_int8_range_with_negatives_and_spaces(self):\n        for pair in [(-11, False), (-5, True), (0, False), (5, False), (10, True), (25, True), (31, False)]:\n            with self.subTest(pair=pair):\n                allowed = True\n                try:\n                    self.int_obj.int_container.intLeafWithRange = pair[0]\n                except ValueError:\n                    allowed = False\n                self.assertEqual(\n                    allowed,\n                    pair[1],\n                    \"Complex range with negatives and additional spaces was not set correctly (%d -> %s != %s)\"\n                    % (pair[0], allowed, pair[1]),\n                )\n\n    def test_complex_range_with_equals_case(self):\n        for pair in [(-43, False), (-40, True), (98, False), (122, True), (254, False), (255, True), (256, False)]:\n            with self.subTest(pair=pair):\n                allowed = True\n                try:\n                    self.int_obj.int_container.complex_range_with_equals_case = pair[0]\n                except ValueError:\n                    allowed = False\n                self.assertEqual(\n                    allowed,\n                    pair[1],\n                    \"Complex range with equals case was not set correctly (%d -> %s != %s)\"\n                    % (pair[0], allowed, pair[1]),\n                )\n\n    def test_setters_exist(self):\n        for leaf in [\"eight\", \"sixteen\", \"thirtytwo\", \"sixtyfour\"]:\n            with self.subTest(leaf=leaf):\n                setter = getattr(self.int_obj.int_container, \"_set_%s\" % leaf, None)\n                self.assertIsNotNone(setter, \"Could not find attribute setter for %s\" % leaf)\n\n    def test_set_int_sizes_at_lower_bounds(self):\n        bounds = {\"eight\": -(2**7), \"sixteen\": -(2**15), \"thirtytwo\": -(2**31), \"sixtyfour\": -(2**63)}\n        for leaf, value in bounds.items():\n            with self.subTest(leaf=leaf):\n                setter = getattr(self.int_obj.int_container, \"_set_%s\" % leaf)\n                allowed = True\n                try:\n                    setter(value)\n                except ValueError:\n                    allowed = False\n                self.assertTrue(allowed, \"Could not set int size %s to %d\" % (leaf, value))\n\n    def test_set_int_sizes_below_lower_bounds(self):\n        bounds = {\n            \"eight\": -(2**7) - 1,\n            \"sixteen\": -(2**15) - 1,\n            \"thirtytwo\": -(2**31) - 1,\n            \"sixtyfour\": -(2**63) - 1,\n        }\n        for leaf, value in bounds.items():\n            with self.subTest(leaf=leaf):\n                setter = getattr(self.int_obj.int_container, \"_set_%s\" % leaf)\n                allowed = True\n                try:\n                    setter(value)\n                except ValueError:\n                    allowed = False\n                self.assertFalse(allowed, \"Incorrectly set int size %s to %d\" % (leaf, value))\n\n    def test_set_int_sizes_at_upper_bounds(self):\n        bounds = {\"eight\": 2**7 - 1, \"sixteen\": 2**15 - 1, \"thirtytwo\": 2**31 - 1, \"sixtyfour\": 2**63 - 1}\n        for leaf, value in bounds.items():\n            with self.subTest(leaf=leaf):\n                setter = getattr(self.int_obj.int_container, \"_set_%s\" % leaf)\n                allowed = True\n                try:\n                    setter(value)\n                except ValueError:\n                    allowed = False\n                self.assertTrue(allowed, \"Could not set int size %s to %d\" % (leaf, value))\n\n    def test_set_int_sizes_above_upper_bounds(self):\n        bounds = {\"eight\": 2**7, \"sixteen\": 2**15, \"thirtytwo\": 2**31, \"sixtyfour\": 2**63}\n        for leaf, value in bounds.items():\n            with self.subTest(leaf=leaf):\n                setter = getattr(self.int_obj.int_container, \"_set_%s\" % leaf)\n                allowed = True\n                try:\n                    setter(value)\n                except ValueError:\n                    allowed = False\n                self.assertFalse(allowed, \"Incorrectly set int size %s to %d\" % (leaf, value))\n\n    def test_set_int8_range_with_min_max_alias_to_lower_bound(self):\n        allowed = True\n        try:\n            self.int_obj.int_container.restricted_ueight_min_alias = -(2**7)\n        except ValueError:\n            allowed = False\n        self.assertTrue(allowed, \"Could not set min..max int8 to minimum value\")\n\n    def test_set_int8_range_with_min_max_alias_to_upper_bound(self):\n        allowed = True\n        try:\n            self.int_obj.int_container.restricted_ueight_min_alias = 2**7 - 1\n        except ValueError:\n            allowed = False\n        self.assertTrue(allowed, \"Could not set min..max int8 to maximum value\")\n\n    def test_set_int8_range_with_min_max_alias_below_lower_bound(self):\n        allowed = True\n        try:\n            self.int_obj.int_container.restricted_ueight_min_alias = -(2**7) - 1\n        except ValueError:\n            allowed = False\n        self.assertFalse(allowed, \"Incorrectly set min..max int8 to the min value minus one\")\n\n    def test_set_int8_range_with_min_max_alias_above_upper_bound(self):\n        allowed = True\n        try:\n            self.int_obj.int_container.restricted_ueight_min_alias = 2**7 + 1\n        except ValueError:\n            allowed = False\n        self.assertFalse(allowed, \"Incorrectly set min..max int8 to the max value plus one\")\n\n\nif __name__ == \"__main__\":\n    unittest.main()\n"
  },
  {
    "path": "tests/integration/__init__.py",
    "content": ""
  },
  {
    "path": "tests/integration/openconfig-interfaces/__init__.py",
    "content": ""
  },
  {
    "path": "tests/integration/openconfig-interfaces/run.py",
    "content": "#!/usr/bin/env python\n\nimport os.path\nimport unittest\n\nfrom pyangbind.lib.xpathhelper import YANGPathHelper\nfrom tests.base import PyangBindTestCase\n\n\nclass OpenconfigInterfacesTests(PyangBindTestCase):\n    yang_files = [\n        os.path.join(\"openconfig\", \"%s.yang\" % fname) for fname in [\"openconfig-interfaces\", \"openconfig-if-aggregate\"]\n    ]\n    pyang_flags = [\n        \"-p %s\" % os.path.join(os.path.dirname(__file__), \"include\"),\n        \"--use-xpathhelper\",\n        \"--lax-quote-checks\",\n    ]\n    split_class_dir = True\n    module_name = \"ocbind\"\n\n    remote_yang_files = [\n        {\n            \"local_path\": \"include\",\n            \"remote_prefix\": \"https://raw.githubusercontent.com/robshakir/yang/master/standard/ietf/RFC/\",\n            \"files\": [\"ietf-inet-types.yang\", \"ietf-yang-types.yang\", \"ietf-interfaces.yang\"],\n        },\n        {\n            \"local_path\": \"include\",\n            \"remote_prefix\": \"https://raw.githubusercontent.com/openconfig/public/master/release/models/\",\n            \"files\": [\n                \"openconfig-extensions.yang\",\n                \"types/openconfig-types.yang\",\n                \"types/openconfig-yang-types.yang\",\n                \"optical-transport/openconfig-transport-types.yang\",\n                \"platform/openconfig-platform-types.yang\",\n            ],\n        },\n        {\n            \"local_path\": \"openconfig\",\n            \"remote_prefix\": \"https://raw.githubusercontent.com/openconfig/public/master/release/models/\",\n            \"files\": [\n                \"interfaces/openconfig-if-ethernet.yang\",\n                \"interfaces/openconfig-if-aggregate.yang\",\n                \"interfaces/openconfig-interfaces.yang\",\n            ],\n        },\n    ]\n\n    def setUp(self):\n        self.yang_helper = YANGPathHelper()\n        self.instance = self.ocbind.openconfig_interfaces(path_helper=self.yang_helper)\n\n    def test_001_populated_intf_type(self):\n        i0 = self.instance.interfaces.interface.add(\"eth0\")\n        self.assertEqual(len(i0.config.type._restriction_dict), 1)\n\n\nif __name__ == \"__main__\":\n    unittest.main()\n"
  },
  {
    "path": "tests/leaf-list/__init__.py",
    "content": ""
  },
  {
    "path": "tests/leaf-list/leaflist.yang",
    "content": "module leaflist {\n    yang-version \"1\";\n    namespace \"http://rob.sh/yang/test/leaflist\";\n    prefix \"foo\";\n    organization \"BugReports Inc\";\n    contact \"A bug reporter\";\n\n    description\n        \"A test module\";\n    revision 2014-01-01 {\n        description \"april-fools\";\n        reference \"fooled-you\";\n    }\n\n    typedef union-key {\n        type union {\n            type string;\n            type int8;\n        }\n    }\n\n    typedef restricted-string {\n        type string {\n            pattern \"^a.*\";\n        }\n    }\n\n    container container {\n        description\n            \"A container\";\n\n        leaf-list leaflist {\n            type string;\n            description\n                \"A leaf list\";\n        }\n\n        leaf-list listtwo {\n            type restricted-string;\n            description\n                \"A restricted leaflist\";\n        }\n\n        leaf-list listthree {\n            type union-key;\n            description\n                \"A leaflist with a union key\";\n        }\n    }\n}\n"
  },
  {
    "path": "tests/leaf-list/run.py",
    "content": "#!/usr/bin/env python\nfrom __future__ import unicode_literals\n\nfrom tests.base import PyangBindTestCase\n\nimport unittest\n\n\nclass LeafListTests(PyangBindTestCase):\n    yang_files = [\"leaflist.yang\"]\n\n    def setUp(self):\n        self.leaflist_obj = self.bindings.leaflist()\n\n    def test_container_exists(self):\n        self.assertTrue(hasattr(self.leaflist_obj, \"container\"))\n\n    def test_leaflist_exists(self):\n        self.assertTrue(hasattr(self.leaflist_obj.container, \"leaflist\"))\n\n    def test_leaflist_length_is_zero(self):\n        self.assertEqual(len(self.leaflist_obj.container.leaflist), 0)\n\n    def test_append_to_leaflist(self):\n        self.leaflist_obj.container.leaflist.append(\"itemOne\")\n        self.assertEqual(len(self.leaflist_obj.container.leaflist), 1)\n\n    def test_retrieve_leaflist_item_value(self):\n        self.leaflist_obj.container.leaflist.append(\"itemOne\")\n        self.assertEqual(self.leaflist_obj.container.leaflist[0], \"itemOne\")\n\n    def test_append_int_to_string_leaflist(self):\n        with self.assertRaises(ValueError):\n            self.leaflist_obj.container.leaflist.append(1)\n\n    def test_getitem(self):\n        self.leaflist_obj.container.leaflist.append(\"itemOne\")\n        self.leaflist_obj.container.leaflist.append(\"itemTwo\")\n\n        self.assertEqual(self.leaflist_obj.container.leaflist[1], \"itemTwo\")\n\n    def test_setitem(self):\n        self.leaflist_obj.container.leaflist.append(\"itemOne\")\n        self.leaflist_obj.container.leaflist.append(\"itemTwo\")\n        self.leaflist_obj.container.leaflist[1] = \"indexOne\"\n\n        self.assertEqual(self.leaflist_obj.container.leaflist[1], \"indexOne\")\n\n    def test_insert(self):\n        self.leaflist_obj.container.leaflist.append(\"itemOne\")\n        self.leaflist_obj.container.leaflist.append(\"itemTwo\")\n        self.leaflist_obj.container.leaflist[1] = \"indexOne\"\n        self.leaflist_obj.container.leaflist.insert(0, \"indexZero\")\n\n        self.assertEqual(self.leaflist_obj.container.leaflist[0], \"indexZero\")\n\n    def test_leaflist_grows_from_various_modification_methods(self):\n        self.leaflist_obj.container.leaflist.append(\"itemOne\")\n        self.leaflist_obj.container.leaflist.append(\"itemTwo\")\n        self.leaflist_obj.container.leaflist[1] = \"indexOne\"\n        self.leaflist_obj.container.leaflist.insert(0, \"indexZero\")\n\n        self.assertEqual(len(self.leaflist_obj.container.leaflist), 4)\n\n    def test_delete_item_from_leaflist(self):\n        self.leaflist_obj.container.leaflist.append(\"itemOne\")\n        self.leaflist_obj.container.leaflist.append(\"itemTwo\")\n        self.leaflist_obj.container.leaflist[1] = \"indexOne\"\n        self.leaflist_obj.container.leaflist.insert(0, \"indexZero\")\n\n        del self.leaflist_obj.container.leaflist[0]\n\n        self.assertEqual(len(self.leaflist_obj.container.leaflist), 3)\n\n    def test_get_full_leaflist(self):\n        self.leaflist_obj.container.leaflist.append(\"itemOne\")\n        self.leaflist_obj.container.leaflist.append(\"itemTwo\")\n        self.leaflist_obj.container.leaflist[1] = \"indexOne\"\n        self.leaflist_obj.container.leaflist.insert(0, \"indexZero\")\n        del self.leaflist_obj.container.leaflist[0]\n\n        self.assertEqual(\n            self.leaflist_obj.get(),\n            {\"container\": {\"leaflist\": [\"itemOne\", \"indexOne\", \"itemTwo\"], \"listtwo\": [], \"listthree\": []}},\n        )\n\n    def test_leaflist_assignment(self):\n        self.leaflist_obj.container.leaflist = [\"itemOne\", \"itemTwo\"]\n\n        self.assertEqual(self.leaflist_obj.container.leaflist, [\"itemOne\", \"itemTwo\"])\n\n    def test_leaflist_assignment_of_wrong_type(self):\n        with self.assertRaises(ValueError):\n            self.leaflist_obj.container.leaflist = [1, 2]\n\n    def test_restricted_string(self):\n        self.leaflist_obj.container.listtwo.append(\"a-valid-string\")\n        self.assertEqual(len(self.leaflist_obj.container.listtwo), 1)\n\n    def test_restricted_string_invalid_value(self):\n        with self.assertRaises(ValueError):\n            self.leaflist_obj.container.listtwo.append(\"broken-string\")\n\n    def test_union_type(self):\n        for pair in [(1, True), (\"fish\", True), ([], False)]:\n            with self.subTest(pair=pair):\n                allowed = True\n                try:\n                    self.leaflist_obj.container.listthree.append(pair[0])\n                except ValueError:\n                    allowed = False\n                self.assertEqual(allowed, pair[1])\n\n    def test_leaf_lists_are_unique_after_assignment(self):\n        self.leaflist_obj.container.leaflist = [\"foo\", \"bar\", \"foo\"]\n        self.assertEqual(self.leaflist_obj.container.get(filter=True), {\"leaflist\": [\"foo\", \"bar\"]})\n\n    def test_leaf_lists_are_unique_after_append(self):\n        self.leaflist_obj.container.leaflist.append(\"foo\")\n        self.leaflist_obj.container.leaflist.append(\"bar\")\n        self.leaflist_obj.container.leaflist.append(\"foo\")\n        self.assertEqual(self.leaflist_obj.container.get(filter=True), {\"leaflist\": [\"foo\", \"bar\"]})\n\n    def test_leaf_lists_insert_non_unique_value_raises_keyerror(self):\n        self.leaflist_obj.container.leaflist[0] = \"foo\"\n        self.leaflist_obj.container.leaflist[1] = \"bar\"\n        with self.assertRaises(ValueError):\n            self.leaflist_obj.container.leaflist[2] = \"foo\"\n\n\nif __name__ == \"__main__\":\n    unittest.main()\n"
  },
  {
    "path": "tests/list/__init__.py",
    "content": ""
  },
  {
    "path": "tests/list/list.yang",
    "content": "module list {\n    yang-version \"1\";\n    namespace \"http://rob.sh/yang/test/list\";\n    prefix \"foo\";\n    organization \"BugReports Inc\";\n    contact \"A bug reporter\";\n\n    description\n        \"A test module\";\n    revision 2014-01-01 {\n        description \"april-fools\";\n        reference \"fooled-you\";\n    }\n\n    typedef string-with-a-or-b-first {\n        type union {\n            type string {\n                pattern \"^a.*\";\n            }\n            type string {\n                pattern \"^b.*\";\n            }\n        }\n    }\n\n    container list-container {\n        description\n            \"A container\";\n\n        list list-element {\n            key \"keyval\";\n\n            leaf keyval {\n                type uint8;\n                description\n                    \"a key value for the list\";\n            }\n\n            leaf another-value {\n                type string;\n                description\n                    \"a string value within the list\";\n                default\n                    \"defaultValue\";\n            }\n\n            description\n                \"A test list.\";\n        }\n\n        list list-two {\n            key \"keyval\";\n\n            leaf keyval {\n                type string {\n                    pattern \"^a.*\";\n                }\n            }\n\n            leaf another-value {\n                type string;\n                description \"another value\";\n            }\n        }\n\n        list list-three {\n            key \"keyval\";\n\n            leaf keyval {\n                type string-with-a-or-b-first;\n            }\n\n            leaf another-value {\n                type string;\n            }\n        }\n\n        list list-four {\n            key \"valone valtwo\";\n\n            leaf valone {\n                type string-with-a-or-b-first;\n            }\n\n            leaf valtwo {\n                type uint8 {\n                    range 1..10;\n                }\n            }\n        }\n\n        list list-five {\n            key \"val\";\n            ordered-by user;\n\n            leaf val {\n                type uint8;\n            }\n\n            leaf adjunct {\n                type uint8;\n            }\n        }\n\n        list list-six {\n            config false;\n\n            leaf val {\n                type uint8;\n            }\n        }\n\n        list list-seven {\n            key val;\n            leaf val {\n                type int8;\n            }\n\n            container under {\n                leaf aval {\n                    type int8;\n                }\n            }\n        }\n\n        list list-eight {\n            key \"val additional\";\n\n            leaf val {\n                type string;\n            }\n\n            leaf additional {\n                type string;\n            }\n\n            leaf numeric {\n                type int8;\n            }\n        }\n    }\n\n    list list-nine {\n        key kv;\n\n        leaf kv {\n            type union {\n                type int8;\n                type string;\n            }\n        }\n\n        leaf lv {\n            type string;\n        }\n    }\n\n    list list-ten {\n        key \"kv kvtwo\";\n\n        leaf kv {\n            type int8;\n        }\n\n        leaf kvtwo {\n            type int16;\n        }\n\n        leaf lv {\n            type string;\n        }\n    }\n\n    identity NATURAL_NUMBERS;\n\n    identity ONE {\n        base NATURAL_NUMBERS;\n    }\n\n    identity TWO {\n        base NATURAL_NUMBERS;\n    }\n\n    identity THREE {\n        base NATURAL_NUMBERS;\n    }\n\n    list list-eleven {\n        key \"kv\";\n\n        leaf kv {\n            type int32;\n        }\n\n        leaf number {\n            type identityref {\n                base NATURAL_NUMBERS;\n            }\n        }\n    }\n\n\n}\n"
  },
  {
    "path": "tests/list/run.py",
    "content": "#!/usr/bin/env python\nfrom __future__ import unicode_literals\n\nimport unittest\n\nfrom tests.base import PyangBindTestCase\n\n\nclass ListTests(PyangBindTestCase):\n    yang_files = [\"list.yang\"]\n    maxDiff = None\n\n    def setUp(self):\n        self.instance = self.bindings.list_()\n\n    def test_list_element_has_zero_members_by_default(self):\n        self.assertEqual(len(self.instance.list_container.list_element), 0)\n\n    def test_cant_add_list_item_with_wrong_type(self):\n        with self.assertRaises(KeyError):\n            self.instance.list_container.list_element.add(\"wrong-key-type\")\n\n    def test_cant_add_list_item_with_wrong_type_by_index(self):\n        with self.assertRaises(ValueError):\n            self.instance.list_container.list_element[2] = \"anInvalidType\"\n\n    def test_add_list_item_with_correct_type(self):\n        allowed = True\n        try:\n            self.instance.list_container.list_element.add(1)\n        except ValueError:\n            allowed = False\n        self.assertTrue(allowed)\n\n    def test_look_up_list_element(self):\n        self.instance.list_container.list_element.add(1)\n        self.assertEqual(self.instance.list_container.list_element[1].keyval, 1)\n\n    def test_list_value_does_not_get_default_value_when_not_set(self):\n        self.instance.list_container.list_element.add(1)\n        self.assertNotEqual(self.instance.list_container.list_element[1].another_value, \"defaultValue\")\n\n    def test_set_both_values_in_a_list_item(self):\n        self.instance.list_container.list_element.add(2)\n        self.instance.list_container.list_element[2].another_value = \"aSecondDefaultValue\"\n        self.assertEqual(\n            self.instance.list_container.list_element[2].get(), {\"keyval\": 2, \"another-value\": \"aSecondDefaultValue\"}\n        )\n\n    def test_list_get(self):\n        self.instance.list_container.list_element.add(1)\n        self.instance.list_container.list_element.add(2)\n        self.assertEqual(\n            self.instance.get(),\n            {\n                \"list-container\": {\n                    \"list-eight\": {},\n                    \"list-seven\": {},\n                    \"list-six\": {},\n                    \"list-five\": {},\n                    \"list-four\": {},\n                    \"list-three\": {},\n                    \"list-two\": {},\n                    \"list-element\": {\n                        1: {\"keyval\": 1, \"another-value\": \"defaultValue\"},\n                        2: {\"keyval\": 2, \"another-value\": \"defaultValue\"},\n                    },\n                },\n                \"list-eleven\": {},\n                \"list-ten\": {},\n                \"list-nine\": {},\n            },\n        )\n\n    def test_delete_list_items(self):\n        self.instance.list_container.list_element.add(1)\n        self.instance.list_container.list_element.add(2)\n        del self.instance.list_container.list_element[1]\n        del self.instance.list_container.list_element[2]\n        self.assertEqual(len(self.instance.list_container.list_element), 0)\n\n    def test_add_list_item_with_restricted_key(self):\n        for animal, valid in [(\"aardvark\", True), (\"bear\", False), (\"chicken\", False)]:\n            with self.subTest(animal=animal, valid=valid):\n                allowed = True\n                try:\n                    self.instance.list_container.list_two.add(animal)\n                except KeyError:\n                    allowed = False\n                self.assertEqual(valid, allowed)\n\n    def test_add_list_item_with_key_restricted_by_union_typedef(self):\n        for animal, valid in [(\"aardvark\", True), (\"bear\", True), (\"chicken\", False)]:\n            with self.subTest(animal=animal, valid=valid):\n                allowed = True\n                try:\n                    self.instance.list_container.list_three.add(animal)\n                except KeyError:\n                    allowed = False\n                self.assertEqual(valid, allowed)\n\n    def test_add_list_item_with_restricted_key_by_keyword(self):\n        for food, valid in [(\"broccoli\", False), (\"carrot\", False), (\"avocado\", True)]:\n            with self.subTest(food=food, valid=valid):\n                allowed = True\n                try:\n                    self.instance.list_container.list_two.add(keyval=food)\n                except KeyError:\n                    allowed = False\n                self.assertEqual(valid, allowed)\n\n    def test_list_item_key_value_is_read_only(self):\n        self.instance.list_container.list_element.add(22)\n        with self.assertRaises(AttributeError):\n            self.instance.list_container.list_element[22].keyval = 14\n\n    def test_adding_items_to_multi_key_list(self):\n        for animal, valid in [(\"aardvark 5\", True), (\"bear 7\", True), (\"chicken 5\", False), (\"bird 11\", False)]:\n            with self.subTest(animal=animal, valid=valid):\n                allowed = True\n                try:\n                    self.instance.list_container.list_four.add(animal)\n                except KeyError:\n                    allowed = False\n                self.assertEqual(valid, allowed)\n\n    def test_ordered_list_maintains_order(self):\n        for i in range(1, 10):\n            self.instance.list_container.list_five.add(i)\n\n        for key, match in zip(list(self.instance.list_container.list_five.keys()), range(1, 10)):\n            with self.subTest(key=key, match=match):\n                self.assertEqual(key, match)\n\n    def test_cant_add_empty_item_to_list_with_key(self):\n        with self.assertRaises(KeyError):\n            self.instance.list_container.list_five.add()\n\n    def test_set_value_on_list_item_with_no_key(self):\n        leaf = self.instance.list_container.list_six.add()\n        self.instance.list_container.list_six[leaf]._set_val(10)\n        self.assertEqual(self.instance.list_container.list_six[leaf].val, 10)\n\n    def test_retrieve_compound_key_with_spaces(self):\n        self.instance.list_container.list_eight.add(val=\"value one\", additional=\"value two\")\n        self.assertEqual(self.instance.list_container.list_eight[\"value one value two\"].val, \"value one\")\n\n    def test_retrieve_compound_key_with_spaces_using_item(self):\n        self.instance.list_container.list_eight.add(val=\"value one\", additional=\"value two\")\n        self.assertEqual(\n            self.instance.list_container.list_eight._item(val=\"value one\", additional=\"value two\").val, \"value one\"\n        )\n\n    def test_delete_list_item_with_keyword_arguments(self):\n        self.instance.list_container.list_eight.add(val=\"one\", additional=\"ten\")\n        allowed = True\n        try:\n            self.instance.list_container.list_eight.delete(val=\"one\", additional=\"ten\")\n        except Exception:\n            allowed = False\n        self.assertTrue(allowed)\n\n    def test_list_item_is_removed_when_deleted(self):\n        self.instance.list_container.list_eight.add(val=\"value one\", additional=\"value two\")\n        self.instance.list_container.list_eight.add(val=\"one\", additional=\"ten\")\n        self.instance.list_container.list_eight.add(val=\"two\", additional=\"twenty\")\n        self.instance.list_container.list_eight.delete(val=\"one\", additional=\"ten\")\n        self.assertEqual(list(self.instance.list_container.list_eight.keys()), [\"value one value two\", \"two twenty\"])\n\n    def test_cant_delete_nonexistent_list_item_by_keywords(self):\n        self.instance.list_container.list_eight.add(val=\"two\", additional=\"twenty\")\n        with self.assertRaises(KeyError):\n            self.instance.list_container.list_eight.delete(val=\"two\", additional=\"two\")\n\n    def test_add_list_item_with_specified_value(self):\n        list_class = self.instance.list_container.list_eight._contained_class()\n        list_class.val = \"three\"\n        list_class.additional = \"forty-two\"\n        list_class.numeric = -42\n\n        allowed = True\n        try:\n            self.instance.list_container.list_eight.add(\n                val=list_class.val, additional=list_class.additional, _v=list_class\n            )\n        except Exception:\n            allowed = False\n        self.assertTrue(allowed)\n\n    def test_retrieve_list_item_which_was_set_with_v(self):\n        list_class = self.instance.list_container.list_eight._contained_class()\n        list_class.val = \"three\"\n        list_class.additional = \"forty-two\"\n        list_class.numeric = -42\n        self.instance.list_container.list_eight.add(\n            val=list_class.val, additional=list_class.additional, _v=list_class\n        )\n        self.assertEqual(self.instance.list_container.list_eight[\"three forty-two\"].numeric, -42)\n\n    def test_retrieve_list_element_with_value_set_by_setitem(self):\n        list_class = self.instance.list_container.list_eight._contained_class()\n        list_class.val = \"four\"\n        list_class.additional = \"forty-four\"\n        list_class.numeric = 44\n        self.instance.list_container.list_eight[\"four forty-four\"] = list_class\n        self.assertEqual(self.instance.list_container.list_eight[\"four forty-four\"].numeric, 44)\n\n    def test_retrieve_list_element_with_value_set_by_setitem_using_named_getitem(self):\n        list_class = self.instance.list_container.list_eight._contained_class()\n        list_class.val = \"four\"\n        list_class.additional = \"forty-four\"\n        list_class.numeric = 44\n        self.instance.list_container.list_eight[\"four forty-four\"] = list_class\n        self.assertEqual(\n            self.instance.list_container.list_eight._item(val=\"four\", additional=\"forty-four\").numeric, 44\n        )\n\n    def test_cant_set_key_on_already_instantiated_list_item(self):\n        list_class = self.instance.list_container.list_eight._contained_class()\n        list_class.val = \"four\"\n        list_class.additional = \"forty-four\"\n        list_class.numeric = 44\n        self.instance.list_container.list_eight[\"four forty-four\"] = list_class\n        with self.assertRaises(AttributeError):\n            self.instance.list_container.list_eight[\"four forty-four\"].val = \"ten\"\n\n    def test_append_new_list_item(self):\n        item = self.instance.list_nine._new_item()\n        item.kv = 13\n        item.lv = \"thirteen\"\n        self.instance.list_nine.append(item)\n        self.assertEqual(self.instance.list_nine[13].lv, \"thirteen\")\n\n    def test_list_append_new_items_updates_keys(self):\n        for key, val in [(13, \"thirteen\"), (\"fourteen\", \"14\")]:\n            item = self.instance.list_nine._new_item()\n            item.kv = key\n            item.lv = val\n            self.instance.list_nine.append(item)\n        self.assertEqual(list(self.instance.list_nine.keys()), [13, \"fourteen\"])\n\n    def test_append_new_list_item_with_compound_key(self):\n        item = self.instance.list_ten._new_item()\n        item.kv = 12\n        item.kvtwo = 13\n        item.lv = \"THIRTEEN\"\n        self.instance.list_ten.append(item)\n        self.assertEqual(self.instance.list_ten[\"12 13\"].lv, \"THIRTEEN\")\n\n    def test_list_append_new_items_with_compound_keys_updates_keys(self):\n        for key1, key2, val in [(12, 13, \"THIRTEEN\"), (13, 14, \"FOURTEEN\")]:\n            item = self.instance.list_ten._new_item()\n            item.kv = key1\n            item.kvtwo = key2\n            item.lv = val\n            self.instance.list_ten.append(item)\n        self.assertEqual(list(self.instance.list_ten.keys()), [\"12 13\", \"13 14\"])\n\n    def test_append_new_list_item_with_identityref(self):\n        item = self.instance.list_eleven._new_item()\n        item.kv = 1\n        item.number = \"ONE\"\n        self.instance.list_eleven.append(item)\n        self.assertEqual(self.instance.list_eleven[1].number, \"ONE\")\n\n    def test_append_new_list_item_with_identityref_doesnt_set_unchanged_elements(self):\n        item = self.instance.list_eleven._new_item()\n        item.kv = 1\n        self.instance.list_eleven.append(item)\n        self.assertEqual(self.instance.list_eleven[1].number, \"\")\n\n    def test_cant_set_nonexistent_item(self):\n        item = self.instance.list_eleven._new_item()\n        item.kv = 1\n        item.number = \"ONE\"\n        self.instance.list_eleven.append(item)\n        with self.assertRaises(AttributeError):\n            self.instance.list_eleven[1].nonexistent = False\n\n\nif __name__ == \"__main__\":\n    unittest.main()\n"
  },
  {
    "path": "tests/misc/__init__.py",
    "content": ""
  },
  {
    "path": "tests/misc/misc.yang",
    "content": "module misc {\n    yang-version \"1\";\n    namespace \"http://rob.sh/yang/test/misc\";\n    prefix \"foo\";\n    organization \"BugReports Inc\";\n    contact \"A bug reporter\";\n\n    description\n        \"Ä ţêśŧ ɱɵđůŀę\";\n    revision 2014-01-01 {\n        description \"april-fools\";\n        reference \"fooled-you\";\n    }\n\n    list a {\n        key \"foo\";\n\n        leaf foo {\n            type leafref {\n                path \"../config/foo\";\n            }\n        }\n\n        container config {\n            leaf foo { type string; }\n        }\n    }\n\n    list b {\n        key \"foo bar\";\n\n        leaf foo {\n            type leafref {\n                path \"../config/foo\";\n            }\n        }\n\n        leaf bar {\n            type leafref {\n                path \"../config/bar\";\n            }\n        }\n\n        container config {\n            leaf foo { type string; }\n            leaf bar { type string; }\n        }\n    }\n\n    list c {\n        key \"one\";\n\n        leaf one {\n            type leafref {\n                path \"../config/one\";\n            }\n        }\n\n        container config {\n            leaf one { type uint8; }\n        }\n    }\n}\n"
  },
  {
    "path": "tests/misc/run.py",
    "content": "#!/usr/bin/env python\nfrom __future__ import unicode_literals\n\nimport unittest\n\nfrom pyangbind.lib.xpathhelper import YANGPathHelper\nfrom tests.base import PyangBindTestCase\n\n\nclass MiscTests(PyangBindTestCase):\n    yang_files = [\"misc.yang\"]\n    split_class_dir = True\n    pyang_flags = [\"--use-extmethods\", \"--use-xpathhelper\"]\n\n    def setUp(self):\n        self.path_helper = YANGPathHelper()\n        self.instance = self.bindings.misc(path_helper=self.path_helper)\n\n    # Check that we can ingest an OpenConfig style list entry\n    # with a leafref to the key\n    def test_001_setleafref(self):\n        import bindings.a as misca\n\n        a = misca.a()\n        a.foo = \"stringval\"\n\n        self.instance.a.append(a)\n        self.assertEqual(str(self.instance.a[\"stringval\"].foo), \"stringval\")\n        self.assertEqual(self.instance.a[\"stringval\"].config.foo, \"stringval\")\n\n    def test_002_checklistkeytype(self):\n        import bindings.b as miscb\n\n        b = miscb.b()\n        b.foo = \"stringvalone\"\n        b.bar = \"stringvaltwo\"\n\n        self.instance.b.append(b)\n        self.assertIsInstance(list(self.instance.b.keys())[0], str)\n\n    def test_003_checklistkeytype(self):\n        import bindings.c as miscc\n\n        c = miscc.c()\n        c.one = 42\n\n        self.instance.c.append(c)\n        self.assertIsInstance(list(self.instance.c.keys())[0], int)\n\n\nif __name__ == \"__main__\":\n    unittest.main(exit=False)\n"
  },
  {
    "path": "tests/nested-containers/__init__.py",
    "content": ""
  },
  {
    "path": "tests/nested-containers/nested.yang",
    "content": "module nested {\n    yang-version \"1\";\n    namespace \"http://rob.sh/yang/test/nested\";\n    prefix \"foo\";\n    organization \"BugReports Inc\";\n    contact \"A bug reporter\";\n\n    description\n        \"A test module\";\n    revision 2014-01-01 {\n        description \"april-fools\";\n        reference \"fooled-you\";\n    }\n\n    container container {\n        description\n            \"A container\";\n\n        container subcontainer {\n            description\n                \"A nested container\";\n\n            leaf a-leaf {\n                type uint8;\n                description\n                    \"A test leaf\";\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "tests/nested-containers/run.py",
    "content": "#!/usr/bin/env python\n\nimport unittest\n\nfrom tests.base import PyangBindTestCase\n\n\nclass NestedContainerTests(PyangBindTestCase):\n    yang_files = [\"nested.yang\"]\n\n    def setUp(self):\n        self.nested_obj = self.bindings.nested()\n\n    def test_subcontainer_is_not_changed_by_default(self):\n        self.assertFalse(self.nested_obj.container.subcontainer._changed(), \"subcontainer was marked to changed\")\n\n    def test_container_is_not_changed_by_default(self):\n        self.assertFalse(self.nested_obj.container._changed(), \"container was marked to changed\")\n\n    def test_subcontainer_marked_changed(self):\n        self.nested_obj.container.subcontainer.a_leaf = 1\n        self.assertTrue(\n            self.nested_obj.container.subcontainer._changed(), \"subcontainer not marked to changed after change\"\n        )\n\n    def test_subcontainer_get(self):\n        self.nested_obj.container.subcontainer.a_leaf = 1\n        self.assertEqual(self.nested_obj.container.subcontainer.get(), {\"a-leaf\": 1}, \"subcontainer get not correct\")\n\n    def test_container_get(self):\n        self.nested_obj.container.subcontainer.a_leaf = 1\n        self.assertEqual(self.nested_obj.container.get(), {\"subcontainer\": {\"a-leaf\": 1}}, \"container get not correct\")\n\n    def test_full_get(self):\n        self.nested_obj.container.subcontainer.a_leaf = 1\n        self.assertEqual(\n            self.nested_obj.get(), {\"container\": {\"subcontainer\": {\"a-leaf\": 1}}}, \"instance get not correct\"\n        )\n\n\nif __name__ == \"__main__\":\n    unittest.main()\n"
  },
  {
    "path": "tests/notification/__init__.py",
    "content": ""
  },
  {
    "path": "tests/notification/notification.yang",
    "content": "module notification {\n    yang-version \"1.1\";\n    namespace \"http://rob.sh/yang/test/notification\";\n    prefix \"foo\";\n    organization \"BugReports Inc\";\n    contact \"A bug reporter\";\n\n    description\n        \"A test module\";\n    revision 2016-07-17 {\n        description \"april-fools\";\n        reference \"fooled-you\";\n    }\n\n    notification alert-one {\n        description\n            \"Basic notification with a single argument\";\n        leaf argument {\n            type string;\n        }\n    }\n\n    notification alert-two {\n        description\n            \"Basic notification with two arguments\";\n        leaf arg-one {\n            type int8;\n        }\n\n        leaf arg-two {\n            type int8;\n        }\n    }\n\n    notification alert-three {\n        description\n            \"Notification with a container\";\n        container arguments {\n            leaf arg-one {\n                type string;\n            }\n\n            leaf arg-two {\n                type string;\n            }\n        }\n    }\n\n    notification alert-four {\n        description\n            \"Notification with multiple containers\";\n        container arguments-one {\n            leaf arg-one {\n                type string;\n            }\n        }\n\n        container arguments-two {\n            leaf arg-two {\n                type string;\n            }\n        }\n    }\n\n    notification alert-five {\n        description\n            \"Notification with values using a leafref which\n            requires use of the XPATHHELPER\";\n\n        leaf argument {\n            type leafref {\n                path \"/test/reference-target\";\n                require-instance true;\n            }\n        }\n    }\n\n    container test {\n        leaf-list reference-target {\n            type string;\n        }\n    }\n}\n"
  },
  {
    "path": "tests/notification/run.py",
    "content": "#!/usr/bin/env python\n\nimport unittest\n\nfrom pyangbind.lib.xpathhelper import YANGPathHelper\nfrom tests.base import PyangBindTestCase\n\n\nclass NotificationTests(PyangBindTestCase):\n    yang_files = [\"notification.yang\"]\n    split_class_dir = True\n    pyang_flags = [\"--use-xpathhelper\", \"--build-notifications\"]\n\n    def setUp(self):\n        self.path_helper = YANGPathHelper()\n\n    # Note that these tests need to import from the filesystem rather than using\n    #  the bound module, so that they can directly reference the sub-modules\n    def test_set_leaf_inside_notification(self):\n        from bindings.notification_notification.alert_one import alert_one\n\n        instance = alert_one(path_helper=self.path_helper)\n        allowed = True\n        try:\n            instance.argument = \"test\"\n        except ValueError:\n            allowed = False\n        self.assertTrue(allowed)\n\n    def test_set_multiple_leafs_inside_notification(self):\n        from bindings.notification_notification.alert_two import alert_two\n\n        instance = alert_two(path_helper=self.path_helper)\n        allowed = True\n        try:\n            instance.arg_one = 10\n            instance.arg_two = 20\n        except ValueError:\n            allowed = False\n        self.assertTrue(allowed)\n\n    def test_set_leafs_on_a_container_inside_a_notification(self):\n        from bindings.notification_notification.alert_three import alert_three\n\n        instance = alert_three(path_helper=self.path_helper)\n        allowed = True\n        try:\n            instance.arguments.arg_one = \"test string\"\n            instance.arguments.arg_two = \"test string\"\n        except ValueError:\n            allowed = False\n        self.assertTrue(allowed)\n\n    def test_set_leafs_on_multiple_containers_inside_a_notification(self):\n        from bindings.notification_notification.alert_four import alert_four\n\n        instance = alert_four(path_helper=self.path_helper)\n        allowed = True\n        try:\n            instance.arguments_one.arg_one = \"test string\"\n            instance.arguments_two.arg_two = \"test string\"\n        except ValueError:\n            allowed = False\n        self.assertTrue(allowed)\n\n    def test_set_leafref_inside_notification(self):\n        from bindings import notification\n        from bindings.notification_notification.alert_five import alert_five\n\n        instance = alert_five(path_helper=self.path_helper)\n        parent = notification(path_helper=self.path_helper)\n\n        parent.test.reference_target.append(\"five\")\n\n        for value, valid in [(\"five\", True), (\"fish\", False)]:\n            with self.subTest(value=value, valid=valid):\n                allowed = True\n                try:\n                    instance.argument = value\n                except ValueError:\n                    allowed = False\n                self.assertEqual(allowed, valid)\n\n\nif __name__ == \"__main__\":\n    unittest.main()\n"
  },
  {
    "path": "tests/openconfig-bgp-juniper/__init__.py",
    "content": ""
  },
  {
    "path": "tests/openconfig-bgp-juniper/openconfig-bgp-juniper.yang",
    "content": "module openconfig-bgp-juniper {\nnamespace \"http://juniper.net/opencfg/bgp\";\n  prefix opencfg-bgp;\n  description \"Example BGP.\";\n  revision 2015-03-02 {\n    description \"Initial revision.\";\n  }\n  container juniper-config {\n    container bgp {\n      description \"Openconfig BGP implementation\";\n      container global {\n        description \"Global options\";\n        leaf as {\n          type string;\n          description \"Autonomous system number\";\n        }\n      }\n      list peer-group {\n        key \"group-name\";\n        description \"List of peer groups\";\n        leaf group-name {\n          type string;\n          description \"Peer group name\";\n        }\n        leaf peer-type {\n          description \"Select type of the peer\";\n          mandatory true;\n          type enumeration {\n            enum \"internal\" {\n              description\n                \"IBGP group\";\n            }\n            enum \"external\" {\n              description\n                \"EBGP group\";\n            }\n          }\n        }\n        list neighbor {\n          description \"Neighbor configuration\";\n          key \"neighbor-name\";\n          leaf neighbor-name {\n            description \"Neighbor name\";\n            type string;\n          }\n          leaf peer-as {\n            description \"Neighbor autonomous system number\";\n            type string;\n          }\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "tests/openconfig-bgp-juniper/run.py",
    "content": "#!/usr/bin/env python\n\nimport unittest\n\nfrom tests.base import PyangBindTestCase\n\n\nclass OpenconfigBGPJuniperTests(PyangBindTestCase):\n    yang_files = [\"openconfig-bgp-juniper.yang\"]\n\n    global_config = {\"my_as\": 2856}\n    peer_group_list = [\"groupA\", \"groupB\"]\n    peers = [\n        (\"1.1.1.1\", \"groupA\", 3741),\n        (\"1.1.1.2\", \"groupA\", 5400),\n        (\"1.1.1.3\", \"groupA\", 29636),\n        (\"2.2.2.2\", \"groupB\", 12767),\n    ]\n\n    def setUp(self):\n        self.bgp_obj = self.bindings.openconfig_bgp_juniper()\n\n        # Pre-populate our data\n        self.bgp_obj.juniper_config.bgp.global_.as_ = self.global_config[\"my_as\"]\n        for peer_group in self.peer_group_list:\n            self.bgp_obj.juniper_config.bgp.peer_group.add(peer_group)\n\n        for peer in self.peers:\n            self.bgp_obj.juniper_config.bgp.peer_group[peer[1]].neighbor.add(peer[0])\n            self.bgp_obj.juniper_config.bgp.peer_group[peer[1]].neighbor[peer[0]].peer_as = peer[2]\n\n    def test_set_unknown_element(self):\n        allowed = True\n        try:\n            self.bgp_obj.system = False\n        except AttributeError:\n            allowed = False\n        self.assertFalse(allowed, \"Trying to set a missing container did not result in an attribute error\")\n\n    def test_get(self):\n        self.assertEqual(\n            self.bgp_obj.get(),\n            {\n                \"juniper-config\": {\n                    \"bgp\": {\n                        \"global\": {\"as\": \"2856\"},\n                        \"peer-group\": {\n                            \"groupA\": {\n                                \"group-name\": \"groupA\",\n                                \"neighbor\": {\n                                    \"1.1.1.1\": {\"neighbor-name\": \"1.1.1.1\", \"peer-as\": \"3741\"},\n                                    \"1.1.1.2\": {\"neighbor-name\": \"1.1.1.2\", \"peer-as\": \"5400\"},\n                                    \"1.1.1.3\": {\"neighbor-name\": \"1.1.1.3\", \"peer-as\": \"29636\"},\n                                },\n                                \"peer-type\": \"\",\n                            },\n                            \"groupB\": {\n                                \"group-name\": \"groupB\",\n                                \"neighbor\": {\"2.2.2.2\": {\"neighbor-name\": \"2.2.2.2\", \"peer-as\": \"12767\"}},\n                                \"peer-type\": \"\",\n                            },\n                        },\n                    }\n                }\n            },\n            \"Unfiltered get response did not match\",\n        )\n\n    def test_filtered_get(self):\n        self.assertEqual(\n            self.bgp_obj.get(filter=True),\n            {\n                \"juniper-config\": {\n                    \"bgp\": {\n                        \"global\": {\"as\": \"2856\"},\n                        \"peer-group\": {\n                            \"groupA\": {\n                                \"group-name\": \"groupA\",\n                                \"neighbor\": {\n                                    \"1.1.1.1\": {\"neighbor-name\": \"1.1.1.1\", \"peer-as\": \"3741\"},\n                                    \"1.1.1.2\": {\"neighbor-name\": \"1.1.1.2\", \"peer-as\": \"5400\"},\n                                    \"1.1.1.3\": {\"neighbor-name\": \"1.1.1.3\", \"peer-as\": \"29636\"},\n                                },\n                            },\n                            \"groupB\": {\n                                \"group-name\": \"groupB\",\n                                \"neighbor\": {\"2.2.2.2\": {\"neighbor-name\": \"2.2.2.2\", \"peer-as\": \"12767\"}},\n                            },\n                        },\n                    }\n                }\n            },\n            \"filtered get response did not match\",\n        )\n\n\nif __name__ == \"__main__\":\n    unittest.main()\n"
  },
  {
    "path": "tests/presence/__init__.py",
    "content": ""
  },
  {
    "path": "tests/presence/presence.yang",
    "content": "module presence {\n    yang-version \"1\";\n    namespace \"http://rob.sh/yang/test/presence\";\n    prefix \"foo\";\n    organization \"BugReports Inc\";\n    contact \"A bug reporter\";\n\n    description\n        \"A test module\";\n    revision 2014-01-01 {\n        description \"april-fools\";\n        reference \"fooled-you\";\n    }\n\n    grouping a-grouping {\n        leaf s {\n            type string;\n        }\n    }\n\n    container empty-container {\n        presence \"something implied implicity\";\n    }\n\n    container parent {\n        container child {\n            presence \"something else implicit\";\n        }\n    }\n\n    container np-container {\n\n        leaf s {\n            type string;\n        }\n    }\n\n    container p-container {\n        presence \"implicit\";\n\n        leaf s {\n            type string;\n        }\n    }\n\n    container p-container-grouping {\n        presence \"implicit\";\n\n        uses a-grouping;\n    }\n}\n"
  },
  {
    "path": "tests/presence/run.py",
    "content": "#!/usr/bin/env python\n\nimport json\nimport unittest\n\nimport pyangbind.lib.pybindJSON as pbJ\nfrom pyangbind.lib.yangtypes import safe_name\nfrom tests.base import PyangBindTestCase\n\n\nclass PresenceTests(PyangBindTestCase):\n    yang_files = [\"presence.yang\"]\n    pyang_flags = [\"--use-extmethods\", \"--presence\"]\n\n    def setUp(self):\n        self.instance = self.bindings.presence()\n\n    def test_001_check_containers(self):\n        for attr in [\n            \"empty-container\",\n            \"parent\",\n            [\"parent\", \"child\"],\n            \"np_container\",\n            \"p_container\",\n            \"p_container_grouping\",\n        ]:\n            with self.subTest(attr=attr):\n                if isinstance(attr, list):\n                    parent = self.instance\n                    for v in attr:\n                        parent = getattr(parent, v, None)\n                        self.assertIsNot(parent, None)\n                else:\n                    elem = getattr(self.instance, safe_name(attr), None)\n                    self.assertIsNot(elem, None)\n\n    def test_002_check_leafs(self):\n        for attr in [\n            (\"np_container\", \"s\"),\n            (\"p_container\", \"s\"),\n            (\"p_container_grouping\", \"s\"),\n        ]:\n            with self.subTest(attr=attr):\n                container, leaf = attr\n                cont_elem = getattr(self.instance, container, None)\n                leaf_elem = getattr(cont_elem, leaf, None)\n                self.assertIsNotNone(leaf_elem, \"Missing leaf %s in container %s\" % (leaf, container))\n\n    def test_003_check_presence(self):\n        self.assertIs(self.instance.empty_container._presence, True)\n        self.assertIs(self.instance.empty_container._cpresent, False)\n        self.assertIs(self.instance.empty_container._present(), False)\n\n    def test_004_check_set_present(self):\n        smt = getattr(self.instance.empty_container, \"_set_present\", None)\n        self.assertIsNot(smt, None)\n        smt()\n        self.assertIs(self.instance.empty_container._cpresent, True)\n        self.assertIs(self.instance.empty_container._present(), True)\n\n    def test_005_check_np(self):\n        self.assertIs(self.instance.parent._presence, False)\n        self.assertIs(self.instance.np_container._presence, False)\n        self.assertIs(self.instance.np_container.s._presence, None)\n\n    def test_006_check_hierarchy(self):\n        self.assertIs(self.instance.p_container._presence, True)\n        self.assertIs(self.instance.p_container._present(), False)\n        self.assertIs(self.instance.p_container._changed(), False)\n        self.instance.p_container.s = \"teststring\"\n        self.assertIs(self.instance.p_container._present(), True)\n        self.assertIs(self.instance.p_container._changed(), True)\n\n    def test_007_check_invalid_hierarchy(self):\n        self.assertIs(self.instance.parent._presence, False)\n        self.assertIs(self.instance.parent.child._presence, True)\n        self.assertIs(self.instance.parent.child._present(), False)\n        self.instance.parent.child._set_present()\n        self.assertIs(self.instance.parent.child._present(), True)\n        self.assertIs(self.instance.parent._present(), None)\n\n    def test_008_set_not_present(self):\n        self.instance.parent.child._set_present()\n        self.assertIs(self.instance.parent.child._present(), True)\n        self.instance.parent.child._set_present(present=False)\n        self.assertIs(self.instance.parent.child._present(), False)\n\n    def test_009_presence_get(self):\n        self.instance.parent.child._set_present(True)\n        self.assertIs(self.instance.empty_container._present(), False)\n        self.assertIs(self.instance.parent.child._present(), True)\n        self.assertIs(self.instance.p_container._present(), False)\n        self.assertEqual(self.instance.get(filter=True), {\"parent\": {\"child\": {}}})\n        self.instance.parent.child._set_present(False)\n        self.assertIs(self.instance.parent.child._present(), False)\n        self.assertEqual(self.instance.get(filter=True), {})\n\n    def test_010_presence_serialise(self):\n        self.instance.parent.child._set_present()\n        self.instance.p_container._set_present()\n        expectedJ = \"\"\"\n                {\n                    \"parent\": {\n                        \"child\": {}\n                    },\n                    \"p-container\": {}\n                }\"\"\"\n        self.assertEqual(json.loads(pbJ.dumps(self.instance)), json.loads(expectedJ))\n        self.instance.parent.child._set_present(False)\n        expectedJ = \"\"\"\n        {\n            \"p-container\": {}\n        }\"\"\"\n        self.assertEqual(json.loads(pbJ.dumps(self.instance)), json.loads(expectedJ))\n\n    def test_011_presence_serialise_ietf(self):\n        self.instance.parent.child._set_present()\n        self.instance.p_container._set_present()\n        expectedJ = \"\"\"\n                {\n                    \"presence:parent\": {\n                        \"child\": {}\n                    },\n                    \"presence:p-container\": {}\n                }\"\"\"\n        self.assertEqual(json.loads(pbJ.dumps(self.instance, mode=\"ietf\")), json.loads(expectedJ))\n        self.instance.parent.child._set_present(False)\n        expectedJ = \"\"\"{\"presence:p-container\": {}}\"\"\"\n        self.assertEqual(json.loads(pbJ.dumps(self.instance, mode=\"ietf\")), json.loads(expectedJ))\n\n    def test_012_presence_deserialise(self):\n        inputJ = \"\"\"\n              {\n                \"parent\": {\n                  \"child\": {}\n                },\n                \"p-container\": {}\n              }\"\"\"\n        x = pbJ.loads(inputJ, self.bindings, \"presence\")\n        self.assertIs(x.parent.child._present(), True)\n        self.assertIs(x.p_container._present(), True)\n\n    def test_013_presence_deserialise(self):\n        inputJ = \"\"\"\n              {\n                \"presence:parent\": {\n                  \"child\": {}\n                },\n                \"presence:p-container\": {}\n              }\"\"\"\n        x = pbJ.loads_ietf(inputJ, self.bindings, \"presence\")\n        self.assertIs(x.parent.child._present(), True)\n        self.assertIs(x.p_container._present(), True)\n\n\nclass SplitClassesPresenceTests(PresenceTests):\n    split_class_dir = True\n\n\nif __name__ == \"__main__\":\n    unittest.main()\n"
  },
  {
    "path": "tests/rpc/__init__.py",
    "content": ""
  },
  {
    "path": "tests/rpc/rpc.yang",
    "content": "module rpc {\n    yang-version \"1.1\";\n    namespace \"http://rob.sh/yang/test/rpc\";\n    prefix \"foo\";\n    organization \"BugReports Inc\";\n    contact \"A bug reporter\";\n\n    description\n        \"A test module\";\n    revision 2014-01-01 {\n        description \"april-fools\";\n        reference \"fooled-you\";\n    }\n\n    rpc check {\n        description\n            \"Basic RPC with a single input argument\";\n        input {\n            leaf argument {\n                type string;\n            }\n        }\n    }\n\n    rpc check-two {\n        description\n            \"Basic RPC check with two leaves in output\";\n        output {\n            leaf arg-one {\n                type int8;\n            }\n\n            leaf arg-two {\n                type int8;\n            }\n        }\n    }\n\n    rpc check-three {\n        description\n            \"RPC check with a container under input\";\n        input {\n            container arguments {\n                leaf arg-one {\n                    type string;\n                }\n\n                leaf arg-two {\n                    type string;\n                }\n            }\n        }\n    }\n\n\n    rpc check-four {\n        description\n            \"RPC check with multiple containers under output\";\n        output {\n            container arguments {\n                leaf arg-one {\n                    type string;\n                }\n            }\n\n            container arguments-two {\n                leaf arg-two {\n                    type string;\n                }\n            }\n        }\n    }\n\n    rpc check-five {\n        description\n            \"RPC check with input and output structures\";\n\n        input {\n            container arguments {\n                leaf arg-one {\n                    type string;\n                }\n            }\n        }\n\n        output {\n            container return-values {\n                leaf return-val {\n                    type int8;\n                }\n            }\n        }\n    }\n\n    rpc check-six {\n        description\n            \"RPC check with input and output values using a leafref which\n            requires use of the XPATHHELPER\";\n\n        input {\n            leaf argument {\n                type leafref {\n                    path \"/test/reference-target\";\n                    require-instance true;\n                }\n            }\n        }\n\n        output {\n            leaf out {\n                type string;\n            }\n        }\n    }\n\n    container test {\n        leaf-list reference-target {\n            type string;\n        }\n    }\n}\n"
  },
  {
    "path": "tests/rpc/run.py",
    "content": "#!/usr/bin/env python\n\nimport unittest\n\nfrom pyangbind.lib.xpathhelper import YANGPathHelper\nfrom tests.base import PyangBindTestCase\n\n\nclass RPCTests(PyangBindTestCase):\n    yang_files = [\"rpc.yang\"]\n    split_class_dir = True\n    pyang_flags = [\"--use-xpathhelper\", \"--build-rpc\"]\n\n    def setUp(self):\n        self.path_helper = YANGPathHelper()\n\n    def test_set_input_argument(self):\n        from bindings.rpc_rpc.check import check\n\n        instance = check(path_helper=self.path_helper)\n\n        allowed = True\n        try:\n            instance.input.argument = \"test\"\n        except ValueError:\n            allowed = False\n        self.assertTrue(allowed)\n\n    def test_set_output_arguments(self):\n        from bindings.rpc_rpc.check_two import output\n\n        instance = output.output(path_helper=self.path_helper)\n        allowed = True\n        try:\n            instance.arg_one = 10\n            instance.arg_two = 20\n        except ValueError:\n            allowed = False\n        self.assertTrue(allowed)\n\n    def test_set_input_arguments_inside_container(self):\n        from bindings.rpc_rpc.check_three import check_three\n\n        instance = check_three(path_helper=self.path_helper)\n        allowed = True\n        try:\n            instance.input.arguments.arg_one = \"test string\"\n            instance.input.arguments.arg_two = \"another test string\"\n        except ValueError:\n            allowed = False\n        self.assertTrue(allowed)\n\n    def test_set_output_arguments_with_multiple_containers(self):\n        from bindings.rpc_rpc.check_four import check_four\n\n        instance = check_four(path_helper=self.path_helper)\n        allowed = True\n        try:\n            instance.output.arguments.arg_one = \"test string\"\n            instance.output.arguments_two.arg_two = \"another test string\"\n        except ValueError:\n            allowed = False\n        self.assertTrue(allowed)\n\n    def set_input_and_output_arguments_on_a_single_rpc_check(self):\n        from bindings.rpc_rpc.check_five import check_five\n\n        instance = check_five(path_helper=self.path_helper)\n        allowed = True\n        try:\n            instance.input.arguments.arg_one = \"test string\"\n            instance.output.return_values.return_val = 10\n        except ValueError:\n            allowed = False\n        self.assertTrue(allowed)\n\n    def test_rpc_attributes_do_not_register_in_path_helper(self):\n        from bindings.rpc_rpc.check import check\n\n        instance = check(path_helper=self.path_helper)\n        instance.input.argument = \"test string\"\n        self.assertEqual(dict(self.path_helper.get_unique(\"/\")), {})\n\n    def test_set_input_argument_to_valid_leafref(self):\n        from bindings import rpc\n        from bindings.rpc_rpc.check_six import check_six\n\n        base_instance = rpc(path_helper=self.path_helper)\n        instance = check_six(path_helper=self.path_helper)\n\n        base_instance.test.reference_target.append(\"six\")\n        allowed = True\n        try:\n            instance.input.argument = \"six\"\n        except ValueError:\n            allowed = False\n        self.assertTrue(allowed)\n\n    def test_set_input_argument_to_invalid_leafref(self):\n        from bindings import rpc\n        from bindings.rpc_rpc.check_six import check_six\n\n        base_instance = rpc(path_helper=self.path_helper)\n        instance = check_six(path_helper=self.path_helper)\n\n        base_instance.test.reference_target.append(\"six\")\n        with self.assertRaises(ValueError):\n            instance.input.argument = \"fish\"\n\n\nif __name__ == \"__main__\":\n    unittest.main()\n"
  },
  {
    "path": "tests/serialise/__init__.py",
    "content": ""
  },
  {
    "path": "tests/serialise/ietf-json-deserialise/__init__.py",
    "content": ""
  },
  {
    "path": "tests/serialise/ietf-json-deserialise/ietf-json-deserialise.yang",
    "content": "module ietf-json-deserialise {\n  yang-version \"1\";\n  namespace \"http://rob.sh/yang/test/serialise/ietf-json-deserialise\";\n  prefix \"foo\";\n\n  import remote { prefix \"remote\"; }\n\n  organization \"BugReports Inc\";\n  contact \"A bug reporter\";\n\n  description\n      \"A test module\";\n  revision 2014-01-01 {\n      description \"april-fools\";\n      reference \"fooled-you\";\n  }\n\n  identity fish {\n    description \"base\";\n  }\n\n  identity haddock {\n    base \"fish\";\n  }\n\n  list mkey {\n    key \"leaf-one leaf-two\";\n\n    leaf leaf-one {\n      type string;\n    }\n\n    leaf leaf-two {\n      type int8;\n    }\n  }\n\n  list skey {\n    key \"leaf-one\";\n\n    leaf leaf-one {\n      type string;\n    }\n  }\n\n  list chlist {\n    key \"keyleaf\";\n\n    leaf keyleaf {\n      type int8;\n    }\n\n    container child {\n      leaf number {\n        type int8;\n      }\n\n      leaf string {\n        type string;\n      }\n    }\n  }\n\n  identity id-base {\n    description\n      \"base identity\";\n  }\n\n  identity idone {\n    base id-base;\n  }\n\n  identity idtwo {\n    base id-base;\n  }\n\n  typedef definedtype {\n    type string;\n  }\n\n  typedef definedtypeunion {\n    type union {\n      type uint8;\n      type string;\n    }\n  }\n\n  container c1 {\n    list l1 {\n      key \"k1\";\n\n      leaf k1 {\n          type uint32;\n      }\n\n      leaf empty {\n        type empty;\n      }\n\n      leaf uint8 {\n        type uint8;\n      }\n\n      leaf uint16 {\n        type uint16;\n      }\n\n      leaf uint32 {\n        type uint32;\n      }\n\n      leaf uint64 {\n        type uint64;\n      }\n\n      leaf int8 {\n        type int8;\n      }\n\n      leaf int16 {\n        type int16;\n      }\n\n      leaf int32 {\n        type int32;\n      }\n\n      leaf decimal {\n        type decimal64 {\n          fraction-digits 2;\n        }\n      }\n\n      leaf int64 {\n        type int64;\n      }\n\n      leaf restricted-integer {\n        type int8 {\n          range \"5..10\";\n        }\n      }\n\n      leaf string {\n        type string;\n      }\n\n      leaf restricted-string {\n        type string {\n          pattern \"a.*\";\n        }\n      }\n\n      leaf union {\n        type union {\n          type string;\n          type uint32;\n        }\n      }\n\n      leaf-list union-list {\n        type union {\n          type uint32;\n          type string;\n        }\n      }\n\n      leaf leafref {\n        type leafref {\n          path \"/c1/t1/target\";\n        }\n      }\n\n      leaf binary {\n        type binary;\n      }\n\n      leaf boolean {\n        type boolean;\n      }\n\n      leaf enumeration {\n        type enumeration {\n          enum one;\n          enum two;\n        }\n      }\n\n      leaf identityref {\n        type identityref {\n          base id-base;\n        }\n      }\n\n      leaf remote-identityref {\n        type identityref {\n          base remote:cheese;\n        }\n      }\n\n      leaf typedef-one {\n        type definedtype;\n      }\n\n      leaf typedef-two {\n        type definedtypeunion;\n      }\n\n      choice test-choice {\n        case one {\n          leaf one-leaf {\n            type string;\n          }\n        }\n        case two {\n          leaf two-leaf {\n            type string;\n          }\n        }\n      }\n    }\n\n    list l2 {\n      key \"k1\";\n      ordered-by user;\n\n      leaf k1 {\n       type uint32;\n      }\n    }\n\n    list t1 {\n      key \"target\";\n      leaf target {\n        type string;\n      }\n    }\n  }\n\n\n}\n"
  },
  {
    "path": "tests/serialise/ietf-json-deserialise/json/chlist.json",
    "content": "{\n  \"chlist\": [\n    {\n      \"keyleaf\": 1,\n      \"child\": {\n        \"number\": 1,\n        \"string\": \"one\"\n      }\n    },\n    {\n      \"keyleaf\": 2,\n      \"child\": {\n        \"number\": 2,\n        \"string\": \"two\"\n      }\n    }\n  ]\n}"
  },
  {
    "path": "tests/serialise/ietf-json-deserialise/json/complete-obj.json",
    "content": "{\n    \"ietf-json-serialise:c1\": {\n        \"t1\": [\n            {\n                \"target\": \"16\"\n            }, \n            {\n                \"target\": \"32\"\n            }\n        ], \n        \"l2\": [\n            {\n                \"k1\": 1\n            }, \n            {\n                \"k1\": 2\n            }, \n            {\n                \"k1\": 3\n            }, \n            {\n                \"k1\": 4\n            }, \n            {\n                \"k1\": 5\n            }, \n            {\n                \"k1\": 6\n            }, \n            {\n                \"k1\": 7\n            }, \n            {\n                \"k1\": 8\n            }, \n            {\n                \"k1\": 9\n            }\n        ], \n        \"l1\": [\n            {\n                \"one-leaf\": \"hi\", \n                \"typedef-one\": \"test\", \n                \"boolean\": true, \n                \"binary\": \"eWFuZw==\",\n                \"union\": \"16\", \n                \"k1\": 1, \n                \"enumeration\": \"one\", \n                \"identityref\": \"ietf-json-serialise:idone\", \n                \"uint16\": 1, \n                \"union-list\": [\n                    16, \n                    \"chicken\"\n                ], \n                \"uint32\": 1, \n                \"empty\": [\n                    null\n                ], \n                \"decimal\": 16.32,\n                \"int32\": 1, \n                \"int16\": 1, \n                \"string\": \"bear\", \n                \"typedef-two\": 8, \n                \"uint8\": 1, \n                \"restricted-integer\": 6, \n                \"leafref\": \"16\", \n                \"int8\": 1, \n                \"uint64\": \"1\", \n                \"remote-identityref\": \"remote:stilton\", \n                \"int64\": \"1\", \n                \"restricted-string\": \"aardvark\"\n            }\n        ]\n    }\n}\n"
  },
  {
    "path": "tests/serialise/ietf-json-deserialise/json/mkeylist.json",
    "content": "{\n  \"mkey\": [\n    {\n      \"leaf-one\": \"one\",\n      \"leaf-two\": 1\n    },\n    {\n      \"leaf-one\": \"three\",\n      \"leaf-two\": 2\n    }\n  ]\n}"
  },
  {
    "path": "tests/serialise/ietf-json-deserialise/json/nonexistkey.json",
    "content": "{\n  \"skey\": [\n    {\n      \"leaf-one\": \"one\",\n      \"non-exist\": true\n    },\n    {\n      \"leaf-one\": \"two\",\n      \"non-exist\": false\n    },\n    {\n      \"leaf-one\": \"three\",\n      \"non-exist\": true\n    }\n  ]\n}"
  },
  {
    "path": "tests/serialise/ietf-json-deserialise/json/skeylist.json",
    "content": "{\n  \"skey\": [\n    {\n      \"leaf-one\": \"one\"\n    },\n    {\n      \"leaf-one\": \"two\"\n    },\n    {\n      \"leaf-one\": \"three\"\n    }\n  ]\n}"
  },
  {
    "path": "tests/serialise/ietf-json-deserialise/remote.yang",
    "content": "module remote {\n  yang-version \"1\";\n  namespace \"http://rob.sh/yang/test/serialise/ietf-json-deserialise/remote\";\n  prefix \"prefix-for-remote\";\n  organization \"BugReports Inc\";\n  contact \"A bug reporter\";\n\n  description\n      \"A test module\";\n  revision 2014-01-01 {\n      description \"april-fools\";\n      reference \"fooled-you\";\n  }\n\n  identity cheese {\n    description \"base\";\n  }\n\n  identity stilton {\n    base cheese;\n  }\n}\n"
  },
  {
    "path": "tests/serialise/ietf-json-deserialise/run.py",
    "content": "#!/usr/bin/env python\nfrom __future__ import unicode_literals\n\nimport json\nimport os.path\nimport unittest\nfrom collections import OrderedDict\nfrom decimal import Decimal\n\nfrom pyangbind.lib.serialise import pybindJSONDecoder\nfrom tests.base import PyangBindTestCase\n\n\nclass IETFJSONDeserialiseTests(PyangBindTestCase):\n    yang_files = [\"ietf-json-deserialise.yang\"]\n    maxDiff = None\n\n    def test_multi_key_list_load(self):\n        expected_json = {\n            \"mkey\": {\"one 1\": {\"leaf-two\": 1, \"leaf-one\": \"one\"}, \"three 2\": {\"leaf-two\": 2, \"leaf-one\": \"three\"}}\n        }\n        with open(os.path.join(os.path.dirname(__file__), \"json\", \"mkeylist.json\")) as fp:\n            actual_json = pybindJSONDecoder.load_ietf_json(json.load(fp), self.bindings, \"ietf_json_deserialise\").get(\n                filter=True\n            )\n        self.assertEqual(actual_json, expected_json, \"Multikey list load did not return expected JSON\")\n\n    def test_single_key_list_load(self):\n        expected_json = {\n            \"skey\": {\"one\": {\"leaf-one\": \"one\"}, \"three\": {\"leaf-one\": \"three\"}, \"two\": {\"leaf-one\": \"two\"}}\n        }\n        with open(os.path.join(os.path.dirname(__file__), \"json\", \"skeylist.json\")) as fp:\n            actual_json = pybindJSONDecoder.load_ietf_json(json.load(fp), self.bindings, \"ietf_json_deserialise\").get(\n                filter=True\n            )\n        self.assertEqual(actual_json, expected_json, \"Single key list load did not return expected JSON\")\n\n    def test_list_with_children_load(self):\n        expected_json = {\n            \"chlist\": {\n                1: {\"keyleaf\": 1, \"child\": {\"number\": 1, \"string\": \"one\"}},\n                2: {\"keyleaf\": 2, \"child\": {\"number\": 2, \"string\": \"two\"}},\n            }\n        }\n        with open(os.path.join(os.path.dirname(__file__), \"json\", \"chlist.json\")) as fp:\n            actual_json = pybindJSONDecoder.load_ietf_json(json.load(fp), self.bindings, \"ietf_json_deserialise\").get(\n                filter=True\n            )\n        self.assertEqual(actual_json, expected_json, \"List with children load did not return expected JSON\")\n\n    def test_all_the_types(self):\n        expected_json = {\n            \"c1\": {\n                \"l1\": {\n                    1: {\n                        \"one-leaf\": \"hi\",\n                        \"typedef-one\": \"test\",\n                        \"boolean\": True,\n                        \"binary\": b\"yang\",\n                        \"union\": \"16\",\n                        \"identityref\": \"idone\",\n                        \"enumeration\": \"one\",\n                        \"k1\": 1,\n                        \"uint16\": 1,\n                        \"union-list\": [16, \"chicken\"],\n                        \"uint32\": 1,\n                        \"int32\": 1,\n                        \"int16\": 1,\n                        \"string\": \"bear\",\n                        \"typedef-two\": 8,\n                        \"uint8\": 1,\n                        \"restricted-integer\": 6,\n                        \"leafref\": \"16\",\n                        \"int8\": 1,\n                        \"uint64\": 1,\n                        \"remote-identityref\": \"stilton\",\n                        \"int64\": 1,\n                        \"restricted-string\": \"aardvark\",\n                        \"decimal\": Decimal(\"16.32\"),\n                        \"empty\": True,\n                    }\n                },\n                \"l2\": OrderedDict(\n                    [\n                        (1, {\"k1\": 1}),\n                        (2, {\"k1\": 2}),\n                        (3, {\"k1\": 3}),\n                        (4, {\"k1\": 4}),\n                        (5, {\"k1\": 5}),\n                        (6, {\"k1\": 6}),\n                        (7, {\"k1\": 7}),\n                        (8, {\"k1\": 8}),\n                        (9, {\"k1\": 9}),\n                    ]\n                ),\n                \"t1\": {\"32\": {\"target\": \"32\"}, \"16\": {\"target\": \"16\"}},\n            }\n        }\n        with open(os.path.join(os.path.dirname(__file__), \"json\", \"complete-obj.json\")) as fp:\n            actual_json = pybindJSONDecoder.load_ietf_json(json.load(fp), self.bindings, \"ietf_json_deserialise\").get(\n                filter=True\n            )\n        self.assertEqual(actual_json, expected_json, \"Deserialisation of complete object not as expected.\")\n\n    def test_skip_unknown_keys(self):\n        allowed = True\n        try:\n            with open(os.path.join(os.path.dirname(__file__), \"json\", \"nonexistkey.json\")) as fp:\n                pybindJSONDecoder.load_ietf_json(\n                    json.load(fp), self.bindings, \"ietf_json_deserialise\", skip_unknown=True\n                )\n        except AttributeError:\n            allowed = False\n        self.assertTrue(allowed, \"Skipping keys that did not exist was not successfully handled.\")\n\n    def test_dont_skip_unknown_keys(self):\n        allowed = True\n        try:\n            with open(os.path.join(os.path.dirname(__file__), \"json\", \"nonexistkey.json\")) as fp:\n                pybindJSONDecoder.load_ietf_json(\n                    json.load(fp), self.bindings, \"ietf_json_deserialise\", skip_unknown=False\n                )\n        except AttributeError:\n            allowed = False\n        self.assertFalse(allowed, \"Skipping keys that did not exist was not successfully handled.\")\n\n\nif __name__ == \"__main__\":\n    unittest.main()\n"
  },
  {
    "path": "tests/serialise/ietf-json-serialise/__init__.py",
    "content": ""
  },
  {
    "path": "tests/serialise/ietf-json-serialise/augment.yang",
    "content": "module augment {\n  yang-version \"1\";\n  namespace \"http://rob.sh/yang/test/serialise/ietf-json-serialise/augment\";\n  prefix \"augpfx\";\n\n  import ietf-json-serialise { prefix ijs; }\n\n  organization \"BugReports Inc\";\n  contact \"A bug reporter\";\n\n  description\n      \"A test module\";\n  revision 2014-01-01 {\n      description \"april-fools\";\n      reference \"fooled-you\";\n  }\n\n  augment \"/ijs:augtarget\" {\n    leaf augleaf {\n      type string;\n    }\n  }\n\n}\n"
  },
  {
    "path": "tests/serialise/ietf-json-serialise/ietf-json-serialise.yang",
    "content": "module ietf-json-serialise {\n  yang-version \"1\";\n  namespace \"http://rob.sh/yang/test/serialise/ietf-json\";\n  prefix \"foo\";\n\n  import remote { prefix \"remote\"; }\n\n  organization \"BugReports Inc\";\n  contact \"A bug reporter\";\n\n  description\n      \"A test module\";\n  revision 2014-01-01 {\n      description \"april-fools\";\n      reference \"fooled-you\";\n  }\n\n  identity id-base {\n    description\n      \"base identity\";\n  }\n\n  identity idone {\n    base id-base;\n  }\n\n  identity idtwo {\n    base id-base;\n  }\n\n  typedef derived64 {\n    type uint64;\n  }\n\n  typedef decimaldefinedtype {\n    type decimal64 {\n      fraction-digits 2;\n    }\n  }\n\n  typedef decimalrangetype {\n    type decimal64 {\n      fraction-digits 10;\n      range \"1..max\";\n    }\n  }\n\n  typedef definedtype {\n    type string;\n  }\n\n  typedef definedtypeunion {\n    type union {\n      type uint8;\n      type string;\n    }\n  }\n\n  typedef nhopenum {\n    type enumeration {\n      enum DROP {}\n    }\n  }\n\n  typedef regexstring {\n    type string {\n      pattern '[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+';\n    }\n  }\n\n  container c1 {\n    list l1 {\n      key \"k1\";\n\n      leaf k1 {\n          type uint32;\n      }\n\n      leaf empty {\n        type empty;\n      }\n\n      leaf uint8 {\n        type uint8;\n      }\n\n      leaf uint16 {\n        type uint16;\n      }\n\n      leaf uint32 {\n        type uint32;\n      }\n\n      leaf uint64 {\n        type uint64;\n      }\n\n      leaf int8 {\n        type int8;\n      }\n\n      leaf int16 {\n        type int16;\n      }\n\n      leaf int32 {\n        type int32;\n      }\n\n      leaf int64 {\n        type int64;\n      }\n\n      leaf decleaf {\n        type decimal64 {\n          fraction-digits 4;\n        }\n      }\n\n      leaf restricted-integer {\n        type int8 {\n          range \"5..10\";\n        }\n      }\n\n      leaf string {\n        type string;\n      }\n\n      leaf restricted-string {\n        type string {\n          pattern \"a.*\";\n        }\n      }\n\n      leaf union {\n        type union {\n          type string;\n          type uint32;\n        }\n      }\n\n      leaf-list union-list {\n        type union {\n          type uint32;\n          type string;\n        }\n      }\n\n      leaf leafref {\n        type leafref {\n          path \"/c1/t1/target\";\n        }\n      }\n\n      leaf binary {\n        type binary;\n      }\n\n      leaf boolean {\n        type boolean;\n      }\n\n      leaf enumeration {\n        type enumeration {\n          enum one;\n          enum two;\n        }\n      }\n\n      leaf identityref {\n        type identityref {\n          base id-base;\n        }\n      }\n\n      leaf remote-identityref {\n        type identityref {\n          base remote:cheese;\n        }\n      }\n\n      leaf typedef-one {\n        type definedtype;\n      }\n\n      leaf typedef-two {\n        type definedtypeunion;\n      }\n\n      choice test-choice {\n        case one {\n          leaf one-leaf {\n            type string;\n          }\n        }\n        case two {\n          leaf two-leaf {\n            type string;\n          }\n        }\n      }\n      leaf-list ll {\n        type string;\n      }\n\n      leaf-list next-hop {\n        type union {\n          type regexstring;\n          type nhopenum;\n          type string;\n        }\n      }\n\n      leaf typedef-decimal {\n        type decimaldefinedtype;\n      }\n\n      leaf range-decimal {\n        type decimal64 {\n          fraction-digits 10;\n          range \"1..10\";\n        }\n      }\n\n      leaf typedef-decimalrange {\n        type decimalrangetype;\n      }\n\n      leaf uint64type {\n        type derived64;\n      }\n    }\n\n    list l2 {\n      key \"k1\";\n      ordered-by user;\n\n      leaf k1 {\n       type uint32;\n      }\n    }\n\n    list t1 {\n      key \"target\";\n      leaf target {\n        type string;\n      }\n    }\n  }\n\n  container augtarget {\n\n  }\n}\n"
  },
  {
    "path": "tests/serialise/ietf-json-serialise/json/obj.json",
    "content": "{\n    \"ietf-json-serialise:c1\": {\n        \"t1\": [\n            {\n                \"target\": \"16\"\n            }, \n            {\n                \"target\": \"32\"\n            }\n        ], \n        \"l2\": [\n            {\n                \"k1\": 1\n            }, \n            {\n                \"k1\": 2\n            }, \n            {\n                \"k1\": 3\n            }, \n            {\n                \"k1\": 4\n            }, \n            {\n                \"k1\": 5\n            }, \n            {\n                \"k1\": 6\n            }, \n            {\n                \"k1\": 7\n            }, \n            {\n                \"k1\": 8\n            }, \n            {\n                \"k1\": 9\n            }\n        ], \n        \"l1\": [\n            {\n                \"one-leaf\": \"hi\", \n                \"typedef-one\": \"test\", \n                \"boolean\": true, \n                \"binary\": \"eWFuZw==\",\n                \"union\": \"16\", \n                \"k1\": 1, \n                \"enumeration\": \"one\", \n                \"identityref\": \"idone\", \n                \"uint16\": 1, \n                \"union-list\": [\n                    16, \n                    \"chicken\"\n                ], \n                \"uint32\": 1, \n                \"empty\": [null], \n                \"int32\": 1, \n                \"int16\": 1, \n                \"string\": \"bear\", \n                \"typedef-two\": 8, \n                \"uint8\": 1, \n                \"restricted-integer\": 6, \n                \"leafref\": \"16\", \n                \"int8\": 1, \n                \"uint64\": \"1\", \n                \"remote-identityref\": \"remote:stilton\", \n                \"int64\": \"1\", \n                \"restricted-string\": \"aardvark\",\n                \"ll\": [\"1\", \"2\", \"3\", \"4\"],\n                \"next-hop\": [\"DROP\", \"192.0.2.1\", \"TEST\"],\n                \"uint64type\": \"4194304\",\n                \"decleaf\": \"42.4422\",\n                \"typedef-decimal\": \"32.29\",\n                \"range-decimal\": \"4.44443322\",\n                \"typedef-decimalrange\": \"33.44\"\n            }\n        ]\n    },\n    \"ietf-json-serialise:augtarget\": {\n        \"augment:augleaf\": \"teststring\"\n    }\n}\n"
  },
  {
    "path": "tests/serialise/ietf-json-serialise/remote.yang",
    "content": "module remote {\n  yang-version \"1\";\n  namespace \"http://rob.sh/yang/test/serialise/ietf-json-serialise/remote\";\n  prefix \"remote\";\n  organization \"BugReports Inc\";\n  contact \"A bug reporter\";\n\n  description\n      \"A test module\";\n  revision 2014-01-01 {\n      description \"april-fools\";\n      reference \"fooled-you\";\n  }\n\n  identity cheese {\n    description \"base\";\n  }\n\n  identity stilton {\n    base cheese;\n  }\n}\n"
  },
  {
    "path": "tests/serialise/ietf-json-serialise/run.py",
    "content": "#!/usr/bin/env python\n\nimport json\nimport os.path\nimport unittest\nfrom decimal import Decimal\n\nfrom pyangbind.lib.serialise import pybindIETFJSONEncoder\nfrom pyangbind.lib.xpathhelper import YANGPathHelper\nfrom tests.base import PyangBindTestCase\n\n\nclass IETFJSONDeserialiseTests(PyangBindTestCase):\n    yang_files = [\"ietf-json-serialise.yang\", \"augment.yang\"]\n    maxDiff = None\n\n    def setUp(self):\n        self.yang_helper = YANGPathHelper()\n        self.serialise_obj = self.bindings.ietf_json_serialise(path_helper=self.yang_helper)\n\n    def test_serialise_single_object(self):\n        self.serialise_obj.c1.l1.add(1)\n        self.serialise_obj.c1.l1[1].string = \"bear\"\n        actual_json = json.loads(\n            json.dumps(\n                pybindIETFJSONEncoder.generate_element(self.serialise_obj.c1.l1[1].string, flt=True),\n                cls=pybindIETFJSONEncoder,\n                indent=4,\n            )\n        )\n        self.assertEqual(actual_json, \"bear\", \"Single element JSON did not match the expected output\")\n\n    def test_serialise_full_container(self):\n        self.serialise_obj.c1.l1.add(1)\n        for signed in [\"int\", \"uint\"]:\n            for size in [8, 16, 32, 64]:\n                name = \"%s%s\" % (signed, size)\n                setter = getattr(self.serialise_obj.c1.l1[1], \"_set_%s\" % name)\n                setter(1)\n        self.serialise_obj.c1.l1[1].restricted_integer = 6\n        self.serialise_obj.c1.l1[1].string = \"bear\"\n        self.serialise_obj.c1.l1[1].restricted_string = \"aardvark\"\n        self.serialise_obj.c1.l1[1].union = 16\n        self.serialise_obj.c1.l1[1].union_list.append(16)\n        self.serialise_obj.c1.l1[1].union_list.append(\"chicken\")\n        self.serialise_obj.c1.l1[1].empty = True\n\n        self.serialise_obj.c1.t1.add(16)\n        self.serialise_obj.c1.t1.add(32)\n        self.serialise_obj.c1.l1[1].leafref = 16\n\n        self.serialise_obj.c1.l1[1].binary = b\"yang\"\n        self.serialise_obj.c1.l1[1].boolean = True\n        self.serialise_obj.c1.l1[1].enumeration = \"one\"\n        self.serialise_obj.c1.l1[1].identityref = \"idone\"\n        self.serialise_obj.c1.l1[1].remote_identityref = \"stilton\"\n        self.serialise_obj.c1.l1[1].typedef_one = \"test\"\n        self.serialise_obj.c1.l1[1].typedef_two = 8\n        self.serialise_obj.c1.l1[1].one_leaf = \"hi\"\n        self.serialise_obj.c1.l1[1].uint64type = 2**22\n        self.serialise_obj.c1.l1[1].typedef_decimal = 32.29\n        self.serialise_obj.c1.l1[1].typedef_decimalrange = Decimal(\"33.44\")\n        self.serialise_obj.c1.l1[1].range_decimal = Decimal(\"4.44443322\")\n        for i in range(1, 5):\n            self.serialise_obj.c1.l1[1].ll.append(str(i))\n        self.serialise_obj.c1.l1[1].next_hop.append(\"DROP\")\n        self.serialise_obj.c1.l1[1].next_hop.append(\"192.0.2.1\")\n        self.serialise_obj.c1.l1[1].next_hop.append(\"TEST\")\n        self.serialise_obj.augtarget.augleaf = \"teststring\"\n        self.serialise_obj.c1.l1[1].decleaf = Decimal(\"42.4422\")\n        for i in range(1, 10):\n            self.serialise_obj.c1.l2.add(i)\n\n        pybind_json = json.loads(\n            json.dumps(\n                pybindIETFJSONEncoder.generate_element(self.serialise_obj, flt=True),\n                cls=pybindIETFJSONEncoder,\n                indent=4,\n            )\n        )\n        with open(os.path.join(os.path.dirname(__file__), \"json\", \"obj.json\"), \"r\") as fp:\n            external_json = json.load(fp)\n\n        self.assertEqual(pybind_json, external_json, \"JSON did not match the expected output.\")\n\n\nif __name__ == \"__main__\":\n    unittest.main()\n"
  },
  {
    "path": "tests/serialise/json-deserialise/__init__.py",
    "content": ""
  },
  {
    "path": "tests/serialise/json-deserialise/json/alltypes.json",
    "content": "{\n    \"c1\": {\n        \"t1\": {\n            \"16\": {\n                \"target\": \"16\"\n            }, \n            \"32\": {\n                \"target\": \"32\"\n            }\n        }, \n        \"l1\": {\n            \"1\": {\n                \"one-leaf\": \"hi\", \n                \"typedef-one\": \"test\", \n                \"boolean\": true, \n                \"binary\": \"eWFuZw==\",\n                \"union\": \"16\", \n                \"k1\": 1, \n                \"enumeration\": \"one\", \n                \"identityref\": \"idone\", \n                \"uint16\": 1, \n                \"union-list\": [\n                    16, \n                    \"chicken\"\n                ], \n                \"decimal\": 42.42,\n                \"uint32\": 1, \n                \"int32\": 1, \n                \"int16\": 1, \n                \"string\": \"bear\", \n                \"typedef-two\": 8, \n                \"uint8\": 1, \n                \"restricted-integer\": 6, \n                \"leafref\": \"16\", \n                \"int8\": 1, \n                \"uint64\": 1, \n                \"int64\": 1, \n                \"restricted-string\": \"aardvark\",\n\t\t\"bits\": \"flag1 flag2\"\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "tests/serialise/json-deserialise/json/list-items.json",
    "content": "{\n    \"load-list\": {\n        \"4\": {\n            \"index\": 4,\n            \"value\": \"four\"\n        },\n        \"5\": {\n            \"index\": 5,\n            \"value\": \"five\"\n        }\n    }\n}"
  },
  {
    "path": "tests/serialise/json-deserialise/json/list.json",
    "content": "{\n    \"load-list\": {\n        \"1\": {\n            \"index\": 1,\n            \"value\": \"one\"\n        },\n        \"2\": {\n            \"index\": 2,\n            \"value\": \"two\"\n        },\n        \"3\": {\n            \"index\": 3,\n            \"value\": \"three\"\n        }\n    }\n}"
  },
  {
    "path": "tests/serialise/json-deserialise/json/nonexist.json",
    "content": "{\n    \"load-list\": {\n        \"1\": {\n            \"index\": 1,\n            \"value\": \"one\",\n            \"no\": \"thisdoesnotexist\"\n        },\n        \"2\": {\n            \"index\": 2,\n            \"value\": \"two\",\n            \"fourtytwo\": 42\n        },\n        \"3\": {\n            \"index\": 3,\n            \"value\": \"three\"\n        }\n    }\n}"
  },
  {
    "path": "tests/serialise/json-deserialise/json/orderedlist-no-order.json",
    "content": "{\n\t\"ordered\": {\n\t\t\"one\": {\"index\": \"one\"},\n\t\t\"two\": {\"index\": \"two\"}\n\t}\n}"
  },
  {
    "path": "tests/serialise/json-deserialise/json/orderedlist-order.json",
    "content": "{\n\t\"ordered\": {\n\t\t\"one\": {\"index\": \"one\", \"__yang_order\": 2},\n\t\t\"two\": {\"index\": \"two\", \"__yang_order\": 1}\n\t}\n}"
  },
  {
    "path": "tests/serialise/json-deserialise/json-deserialise.yang",
    "content": "module json-deserialise {\n  yang-version \"1\";\n  namespace \"http://rob.sh/yang/test/serialise/json-deserialise\";\n  prefix \"foo\";\n  organization \"BugReports Inc\";\n  contact \"A bug reporter\";\n\n  description\n      \"A test module\";\n  revision 2014-01-01 {\n      description \"april-fools\";\n      reference \"fooled-you\";\n  }\n\n  list load-list {\n    key index;\n\n    leaf index {\n      type int8;\n    }\n\n    leaf value {\n      type string;\n    }\n  }\n\n  identity id-base {\n    description\n      \"base identity\";\n  }\n\n  identity idone {\n    base id-base;\n  }\n\n  identity idtwo {\n    base id-base;\n  }\n\n  typedef definedtype {\n    type string;\n  }\n\n  typedef definedtypeunion {\n    type union {\n      type uint8;\n      type string;\n    }\n  }\n\n  container c1 {\n    list l1 {\n      key \"k1\";\n\n      leaf k1 {\n          type uint32;\n      }\n\n      leaf uint8 {\n        type uint8;\n      }\n\n      leaf uint16 {\n        type uint16;\n      }\n\n      leaf uint32 {\n        type uint32;\n      }\n\n      leaf uint64 {\n        type uint64;\n      }\n\n      leaf int8 {\n        type int8;\n      }\n\n      leaf int16 {\n        type int16;\n      }\n\n      leaf int32 {\n        type int32;\n      }\n\n      leaf int64 {\n        type int64;\n      }\n\n      leaf restricted-integer {\n        type int8 {\n          range \"5..10\";\n        }\n      }\n\n      leaf string {\n        type string;\n      }\n\n      leaf restricted-string {\n        type string {\n          pattern \"a.*\";\n        }\n      }\n\n      leaf union {\n        type union {\n          type string;\n          type uint32;\n        }\n      }\n\n      leaf-list union-list {\n        type union {\n          type uint32;\n          type string;\n        }\n      }\n\n      leaf leafref {\n        type leafref {\n          path \"/c1/t1/target\";\n        }\n      }\n\n      leaf binary {\n        type binary;\n      }\n\n      leaf boolean {\n        type boolean;\n      }\n\n      leaf enumeration {\n        type enumeration {\n          enum one;\n          enum two;\n        }\n      }\n\n      leaf decimal {\n        type decimal64 {\n          fraction-digits 2;\n        }\n      }\n\n      leaf identityref {\n        type identityref {\n          base id-base;\n        }\n      }\n\n      leaf typedef-one {\n        type definedtype;\n      }\n\n      leaf typedef-two {\n        type definedtypeunion;\n      }\n\n      choice test-choice {\n        case one {\n          leaf one-leaf {\n            type string;\n          }\n        }\n        case two {\n          leaf two-leaf {\n            type string;\n          }\n        }\n      }\n\n      leaf bits {\n        type bits {\n\t  bit flag1 {\n\t    position 1;\n\t  }\n\t  bit flag2 {\n\t    position 2;\n\t  }\n\t  bit flag3 {\n\t    position 3;\n\t  }\n\t}\n      }\n    }\n\n    list t1 {\n      key \"target\";\n      leaf target {\n        type string;\n      }\n    }\n  }\n\n  list ordered {\n    ordered-by user;\n    key \"index\";\n\n    leaf index {\n      type string;\n    }\n  }\n}\n"
  },
  {
    "path": "tests/serialise/json-deserialise/run.py",
    "content": "#!/usr/bin/env python\nfrom __future__ import unicode_literals\n\nimport json\nimport os.path\nimport unittest\nfrom decimal import Decimal\n\nimport pyangbind.lib.pybindJSON as pbJ\nimport pyangbind.lib.serialise as pbS\nfrom pyangbind.lib.serialise import pybindJSONDecoder\nfrom pyangbind.lib.xpathhelper import YANGPathHelper\n\nfrom tests.base import PyangBindTestCase\n\n\nclass JSONDeserialiseTests(PyangBindTestCase):\n    yang_files = [\"json-deserialise.yang\"]\n    maxDiff = None\n\n    def setUp(self):\n        self.yang_helper = YANGPathHelper()\n        self.deserialise_obj = self.bindings.json_deserialise(path_helper=self.yang_helper)\n\n    def test_load_full_object(self):\n        expected_json = {\n            \"load-list\": {\n                \"1\": {\"index\": 1, \"value\": \"one\"},\n                \"3\": {\"index\": 3, \"value\": \"three\"},\n                \"2\": {\"index\": 2, \"value\": \"two\"},\n            }\n        }\n        # For developers looking for examples, note that the arguments here are:\n        #   - the file that we're trying to read\n        #   - the bindings module that we generated\n        #   - the name of the class within that module\n        #   kwargs (path_helper, overwrite, etc.)\n        actual_json = pbJ.load(\n            os.path.join(os.path.dirname(__file__), \"json\", \"list.json\"),\n            self.bindings,\n            \"json_deserialise\",\n            path_helper=self.yang_helper,\n        ).get(filter=True)\n\n        self.assertEqual(actual_json, expected_json, \"Whole object load did not return the correct list.\")\n\n    def test_load_into_existing_object(self):\n        with open(os.path.join(os.path.dirname(__file__), \"json\", \"list-items.json\"), \"r\") as fp:\n            pbS.pybindJSONDecoder.load_json(json.load(fp), None, None, obj=self.deserialise_obj)\n        expected_json = {\"load-list\": {\"5\": {\"index\": 5, \"value\": \"five\"}, \"4\": {\"index\": 4, \"value\": \"four\"}}}\n        actual_json = self.deserialise_obj.get(filter=True)\n        self.assertEqual(actual_json, expected_json, \"Existing object load did not return the correct list.\")\n\n    def test_all_the_types(self):\n        expected_json = {\n            \"c1\": {\n                \"l1\": {\n                    \"1\": {\n                        \"one-leaf\": \"hi\",\n                        \"typedef-one\": \"test\",\n                        \"boolean\": True,\n                        \"binary\": b\"yang\",\n                        \"union\": \"16\",\n                        \"identityref\": \"idone\",\n                        \"enumeration\": \"one\",\n                        \"k1\": 1,\n                        \"uint16\": 1,\n                        \"union-list\": [16, \"chicken\"],\n                        \"uint32\": 1,\n                        \"int32\": 1,\n                        \"int16\": 1,\n                        \"string\": \"bear\",\n                        \"decimal\": Decimal(\"42.42\"),\n                        \"typedef-two\": 8,\n                        \"uint8\": 1,\n                        \"restricted-integer\": 6,\n                        \"leafref\": \"16\",\n                        \"int8\": 1,\n                        \"uint64\": 1,\n                        \"int64\": 1,\n                        \"restricted-string\": \"aardvark\",\n                        \"bits\": set([\"flag1\", \"flag2\"]),\n                    }\n                },\n                \"t1\": {\"32\": {\"target\": \"32\"}, \"16\": {\"target\": \"16\"}},\n            }\n        }\n        actual_json = pbJ.load(\n            os.path.join(os.path.dirname(__file__), \"json\", \"alltypes.json\"),\n            self.bindings,\n            \"json_deserialise\",\n            path_helper=self.yang_helper,\n        ).get(filter=True)\n\n        self.assertEqual(actual_json, expected_json, \"Load of object with all items not correct.\")\n\n    def test_load_user_ordered_list(self):\n        actual_json = pbJ.load(\n            os.path.join(os.path.dirname(__file__), \"json\", \"orderedlist-order.json\"),\n            self.bindings,\n            \"json_deserialise\",\n            path_helper=self.yang_helper,\n        )\n        self.assertEqual(list(actual_json.ordered.keys()), [\"two\", \"one\"])\n\n    def test_load_json_ordered_list(self):\n        actual_json = pbJ.load(\n            os.path.join(os.path.dirname(__file__), \"json\", \"orderedlist-no-order.json\"),\n            self.bindings,\n            \"json_deserialise\",\n            path_helper=self.yang_helper,\n        )\n        self.assertEqual(list(actual_json.ordered.keys()), [\"two\", \"one\"])\n\n    def test_skip_unknown_keys(self):\n        allowed = True\n        try:\n            with open(os.path.join(os.path.dirname(__file__), \"json\", \"nonexist.json\"), \"r\") as fp:\n                pybindJSONDecoder.load_ietf_json(json.load(fp), self.bindings, \"json_deserialise\", skip_unknown=True)\n        except AttributeError:\n            allowed = False\n        self.assertTrue(allowed, \"Skipping keys that did not exist was not successfully handled.\")\n\n    def test_dont_skip_unknown_keys(self):\n        allowed = True\n        try:\n            with open(os.path.join(os.path.dirname(__file__), \"json\", \"nonexist.json\"), \"r\") as fp:\n                pybindJSONDecoder.load_ietf_json(json.load(fp), self.bindings, \"json_deserialise\", skip_unknown=False)\n        except AttributeError:\n            allowed = False\n        self.assertFalse(allowed, \"Skipping keys that did not exist was not successfully handled.\")\n\n\nif __name__ == \"__main__\":\n    unittest.main()\n"
  },
  {
    "path": "tests/serialise/json-serialise/__init__.py",
    "content": ""
  },
  {
    "path": "tests/serialise/json-serialise/json/container.json",
    "content": "{\n    \"string-test\": \"twenty-two\"\n}\n"
  },
  {
    "path": "tests/serialise/json-serialise/json/expected-output.json",
    "content": "{\n    \"c1\": {\n        \"t1\": {\n            \"16\": {\n                \"target\": \"16\"\n            }, \n            \"32\": {\n                \"target\": \"32\"\n            }\n        }, \n        \"l2\": {\n            \"1\": {\n                \"__yang_order\": 0, \n                \"k1\": 1\n            }, \n            \"2\": {\n                \"__yang_order\": 1, \n                \"k1\": 2\n            }, \n            \"3\": {\n                \"__yang_order\": 2, \n                \"k1\": 3\n            }, \n            \"4\": {\n                \"__yang_order\": 3, \n                \"k1\": 4\n            }, \n            \"5\": {\n                \"__yang_order\": 4, \n                \"k1\": 5\n            }, \n            \"6\": {\n                \"__yang_order\": 5, \n                \"k1\": 6\n            }, \n            \"7\": {\n                \"__yang_order\": 6, \n                \"k1\": 7\n            }, \n            \"8\": {\n                \"__yang_order\": 7, \n                \"k1\": 8\n            }, \n            \"9\": {\n                \"__yang_order\": 8, \n                \"k1\": 9\n            }\n        }, \n        \"l1\": {\n            \"1\": {\n                \"one-leaf\": \"hi\", \n                \"typedef-one\": \"test\", \n                \"boolean\": true, \n                \"binary\": \"eWFuZw==\",\n                \"union\": \"16\", \n                \"k1\": 1, \n                \"enumeration\": \"one\", \n                \"identityref\": \"idone\", \n                \"uint16\": 1, \n                \"union-list\": [\n                    16, \n                    \"chicken\"\n                ], \n                \"uint32\": 1, \n                \"int32\": 1, \n                \"int16\": 1, \n                \"string\": \"bear\", \n                \"typedef-two\": 8, \n                \"uint8\": 1, \n                \"restricted-integer\": 6, \n                \"leafref\": \"16\", \n                \"int8\": 1, \n                \"uint64\": 1, \n                \"int64\": 1, \n                \"restricted-string\": \"aardvark\",\n                \"ll\": [\"1\", \"2\", \"3\", \"4\"],\n                \"next-hop\": [\"DROP\", \"192.0.2.1\", \"fish\"],\n                \"decleaf\": 42.4422,\n                \"typedef-decimal\": 21.21,\n                \"range-decimal\": 4.44443322,\n                \"typedef-decimalrange\": 42.42,\n                \"bits\": \"flag1 flag3\"\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "tests/serialise/json-serialise/json-serialise.yang",
    "content": "module json-serialise {\n  yang-version \"1\";\n  namespace \"http://rob.sh/yang/test/serialise/json\";\n  prefix \"foo\";\n  organization \"BugReports Inc\";\n  contact \"A bug reporter\";\n\n  description\n      \"A test module\";\n  revision 2014-01-01 {\n      description \"april-fools\";\n      reference \"fooled-you\";\n  }\n\n  identity id-base {\n    description\n      \"base identity\";\n  }\n\n  identity idone {\n    base id-base;\n  }\n\n  identity idtwo {\n    base id-base;\n  }\n\n  typedef definedtype {\n    type string;\n  }\n\n  typedef decimaldefinedtype {\n    type decimal64 {\n      fraction-digits 2;\n    }\n  }\n\n  typedef decimalrangetype {\n    type decimal64 {\n      fraction-digits 10;\n      range \"1..max\";\n    }\n  }\n\n  typedef definedtypeunion {\n    type union {\n      type uint8;\n      type string;\n    }\n  }\n\n  typedef nhopenum {\n    type enumeration {\n      enum DROP {}\n    }\n  }\n\n  typedef regexstring {\n    type string {\n      pattern '[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+';\n    }\n  }\n\n  container c1 {\n    list l1 {\n      key \"k1\";\n\n      leaf k1 {\n          type uint32;\n      }\n\n      leaf uint8 {\n        type uint8;\n      }\n\n      leaf uint16 {\n        type uint16;\n      }\n\n      leaf uint32 {\n        type uint32;\n      }\n\n      leaf uint64 {\n        type uint64;\n      }\n\n      leaf int8 {\n        type int8;\n      }\n\n      leaf int16 {\n        type int16;\n      }\n\n      leaf int32 {\n        type int32;\n      }\n\n      leaf int64 {\n        type int64;\n      }\n\n      leaf restricted-integer {\n        type int8 {\n          range \"5..10\";\n        }\n      }\n\n      leaf string {\n        type string;\n      }\n\n      leaf restricted-string {\n        type string {\n          pattern \"a.*\";\n        }\n      }\n\n      leaf union {\n        type union {\n          type string;\n          type uint32;\n        }\n      }\n\n      leaf-list union-list {\n        type union {\n          type uint32;\n          type string;\n        }\n      }\n\n      leaf leafref {\n        type leafref {\n          path \"/c1/t1/target\";\n        }\n      }\n\n      leaf binary {\n        type binary;\n      }\n\n      leaf boolean {\n        type boolean;\n      }\n\n      leaf decleaf {\n        type decimal64 {\n          fraction-digits 4;\n        }\n      }\n\n      leaf enumeration {\n        type enumeration {\n          enum one;\n          enum two;\n        }\n      }\n\n      leaf identityref {\n        type identityref {\n          base id-base;\n        }\n      }\n\n      leaf typedef-one {\n        type definedtype;\n      }\n\n      leaf typedef-two {\n        type definedtypeunion;\n      }\n\n      leaf-list ll {\n        type string;\n      }\n\n      leaf-list next-hop {\n        type union {\n          type regexstring;\n          type nhopenum;\n          type string;\n        }\n      }\n\n      leaf typedef-decimal {\n        type decimaldefinedtype;\n      }\n\n      leaf range-decimal {\n        type decimal64 {\n          fraction-digits 10;\n          range \"1..10\";\n        }\n      }\n\n      leaf typedef-decimalrange {\n        type decimalrangetype;\n      }\n\n      leaf bits {\n        type bits {\n\t  bit flag1 {\n\t    position 1;\n\t  }\n\t  bit flag2 {\n\t    position 2;\n\t  }\n\t  bit flag3 {\n\t    position 3;\n\t  }\n\t}\n      }\n\n      choice test-choice {\n        case one {\n          leaf one-leaf {\n            type string;\n          }\n        }\n        case two {\n          leaf two-leaf {\n            type string;\n          }\n        }\n      }\n    }\n\n    list l2 {\n      key \"k1\";\n      ordered-by user;\n\n      leaf k1 {\n       type uint32;\n      }\n    }\n\n    list t1 {\n      key \"target\";\n      leaf target {\n        type string;\n      }\n    }\n  }\n\n  container two {\n    leaf string-test {\n      type string;\n    }\n  }\n}\n"
  },
  {
    "path": "tests/serialise/json-serialise/run.py",
    "content": "#!/usr/bin/env python\n\nimport json\nimport os.path\nimport unittest\nfrom decimal import Decimal\n\nfrom pyangbind.lib.pybindJSON import dumps\nfrom pyangbind.lib.xpathhelper import YANGPathHelper\nfrom tests.base import PyangBindTestCase\n\n\nclass JSONSerialiseTests(PyangBindTestCase):\n    yang_files = [\"json-serialise.yang\"]\n    pyang_flags = [\"--use-xpathhelper\"]\n\n    def setUp(self):\n        self.yang_helper = YANGPathHelper()\n        self.serialise_obj = self.bindings.json_serialise(path_helper=self.yang_helper)\n\n    def test_serialise_container(self):\n        self.serialise_obj.two.string_test = \"twenty-two\"\n        with open(os.path.join(os.path.dirname(__file__), \"json\", \"container.json\"), \"r\") as fp:\n            self.assertEqual(\n                json.loads(dumps(self.yang_helper.get(\"/two\")[0])),\n                json.load(fp),\n                \"Invalid output returned when serialising a container.\",\n            )\n\n    def test_full_serialise(self):\n        self.serialise_obj.c1.l1.add(1)\n        for signed in [\"int\", \"uint\"]:\n            for size in [8, 16, 32, 64]:\n                name = \"%s%s\" % (signed, size)\n                setter = getattr(self.serialise_obj.c1.l1[1], \"_set_%s\" % name)\n                setter(1)\n        self.serialise_obj.c1.l1[1].restricted_integer = 6\n        self.serialise_obj.c1.l1[1].string = \"bear\"\n        self.serialise_obj.c1.l1[1].restricted_string = \"aardvark\"\n        self.serialise_obj.c1.l1[1].union = 16\n        self.serialise_obj.c1.l1[1].union_list.append(16)\n        self.serialise_obj.c1.l1[1].union_list.append(\"chicken\")\n\n        self.serialise_obj.c1.t1.add(16)\n        self.serialise_obj.c1.t1.add(32)\n        self.serialise_obj.c1.l1[1].leafref = 16\n\n        self.serialise_obj.c1.l1[1].binary = b\"yang\"\n        self.serialise_obj.c1.l1[1].boolean = True\n        self.serialise_obj.c1.l1[1].enumeration = \"one\"\n        self.serialise_obj.c1.l1[1].identityref = \"idone\"\n        self.serialise_obj.c1.l1[1].typedef_one = \"test\"\n        self.serialise_obj.c1.l1[1].typedef_two = 8\n        self.serialise_obj.c1.l1[1].one_leaf = \"hi\"\n        for i in range(1, 5):\n            self.serialise_obj.c1.l1[1].ll.append(str(i))\n        self.serialise_obj.c1.l1[1].next_hop.append(\"DROP\")\n        self.serialise_obj.c1.l1[1].next_hop.append(\"192.0.2.1\")\n        self.serialise_obj.c1.l1[1].next_hop.append(\"fish\")\n        self.serialise_obj.c1.l1[1].typedef_decimal = Decimal(\"21.21\")\n        self.serialise_obj.c1.l1[1].range_decimal = Decimal(\"4.44443322\")\n        self.serialise_obj.c1.l1[1].typedef_decimalrange = Decimal(\"42.42\")\n        self.serialise_obj.c1.l1[1].decleaf = Decimal(\"42.4422\")\n        self.serialise_obj.c1.l1[1].bits.add(\"flag1\")\n        self.serialise_obj.c1.l1[1].bits.add(\"flag3\")\n\n        for i in range(1, 10):\n            self.serialise_obj.c1.l2.add(i)\n\n        pybind_json = json.loads(dumps(self.serialise_obj))\n        with open(os.path.join(os.path.dirname(__file__), \"json\", \"expected-output.json\"), \"r\") as fp:\n            external_json = json.load(fp)\n        self.assertEqual(pybind_json, external_json, \"JSON did not match expected output.\")\n\n\nif __name__ == \"__main__\":\n    unittest.main()\n"
  },
  {
    "path": "tests/serialise/openconfig-serialise/__init__.py",
    "content": ""
  },
  {
    "path": "tests/serialise/openconfig-serialise/json/interfaces_ph.False-flt.False-m.default.json",
    "content": "{\n    \"interfaces\": {\n        \"interface\": {\n            \"eth0\": {\n                \"hold-time\": {\n                    \"state\": {\n                        \"down\": 0, \n                        \"up\": 0\n                    }, \n                    \"config\": {\n                        \"down\": 0, \n                        \"up\": 0\n                    }\n                }, \n                \"config\": {\n                    \"enabled\": true, \n                    \"loopback-mode\": \"NONE\",\n                    \"type\": \"\", \n                    \"description\": \"\", \n                    \"name\": \"\", \n                    \"mtu\": 0\n                },\n                \"penalty-based-aied\": {\n                    \"config\": {\n                        \"decay-half-life\": 0,\n                        \"flap-penalty\": 0,\n                        \"max-suppress-time\": 0,\n                        \"reuse-threshold\": 0,\n                        \"suppress-threshold\": 0\n                    },\n                    \"state\": {\n                        \"decay-half-life\": 0,\n                        \"flap-penalty\": 0,\n                        \"max-suppress-time\": 0,\n                        \"reuse-threshold\": 0,\n                        \"suppress-threshold\": 0\n                    }\n                }, \n                \"state\": {\n                    \"name\": \"\", \n                    \"type\": \"\", \n                    \"description\": \"\", \n                    \"enabled\": true, \n                    \"loopback-mode\": \"NONE\",\n                    \"cpu\": false,\n                    \"management\": false,\n                    \"logical\": false,\n                    \"admin-status\": \"\", \n                    \"mtu\": 0, \n                    \"ifindex\": 0, \n                    \"last-change\": 0, \n                    \"oper-status\": \"\", \n                    \"counters\": {\n                        \"in-fcs-errors\": 0, \n                        \"in-errors\": 0, \n                        \"in-discards\": 0, \n                        \"out-broadcast-pkts\": 0, \n                        \"out-errors\": 0, \n                        \"out-multicast-pkts\": 0, \n                        \"in-multicast-pkts\": 0, \n                        \"last-clear\": 0, \n                        \"out-octets\": 0, \n                        \"in-unicast-pkts\": 0, \n                        \"out-unicast-pkts\": 0, \n                        \"out-discards\": 0, \n                        \"in-broadcast-pkts\": 0, \n                        \"carrier-transitions\": 0,\n                        \"interface-transitions\": 0,\n                        \"link-transitions\": 0,\n                        \"in-unknown-protos\": 0, \n                        \"in-octets\": 0,\n                        \"in-pkts\": 0,\n                        \"out-pkts\": 0,\n                        \"resets\": 0\n                    }\n                }, \n                \"name\": \"eth0\", \n                \"subinterfaces\": {\n                    \"subinterface\": {}\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "tests/serialise/openconfig-serialise/json/interfaces_ph.False-flt.False-m.ietf.json",
    "content": "{\n    \"openconfig-interfaces:interfaces\": {\n        \"interface\": [\n            {\n                \"hold-time\": {\n                    \"state\": {\n                        \"down\": 0, \n                        \"up\": 0\n                    }, \n                    \"config\": {\n                        \"down\": 0, \n                        \"up\": 0\n                    }\n                }, \n                \"config\": {\n                    \"enabled\": false, \n                    \"loopback-mode\": \"\",\n                    \"type\": \"\", \n                    \"description\": \"\", \n                    \"name\": \"\", \n                    \"mtu\": 0\n                },\n                \"penalty-based-aied\": {\n                    \"config\": {\n                        \"decay-half-life\": 0,\n                        \"flap-penalty\": 0,\n                        \"max-suppress-time\": 0,\n                        \"reuse-threshold\": 0,\n                        \"suppress-threshold\": 0\n                    },\n                    \"state\": {\n                        \"decay-half-life\": 0,\n                        \"flap-penalty\": 0,\n                        \"max-suppress-time\": 0,\n                        \"reuse-threshold\": 0,\n                        \"suppress-threshold\": 0\n                    }\n                }, \n                \"state\": {\n                    \"name\": \"\", \n                    \"type\": \"\", \n                    \"description\": \"\", \n                    \"enabled\": false, \n                    \"cpu\": false,\n                    \"loopback-mode\": \"\",\n                    \"logical\":false,\n                    \"management\": false,\n                    \"admin-status\": \"\", \n                    \"mtu\": 0, \n                    \"ifindex\": 0, \n                    \"last-change\": \"0\", \n                    \"oper-status\": \"\", \n                    \"counters\": {\n                        \"in-fcs-errors\": \"0\", \n                        \"in-errors\": \"0\", \n                        \"in-discards\": \"0\", \n                        \"out-broadcast-pkts\": \"0\", \n                        \"out-errors\": \"0\", \n                        \"out-multicast-pkts\": \"0\", \n                        \"in-multicast-pkts\": \"0\", \n                        \"last-clear\": \"0\", \n                        \"out-octets\": \"0\", \n                        \"in-unicast-pkts\": \"0\", \n                        \"out-unicast-pkts\": \"0\", \n                        \"out-discards\": \"0\", \n                        \"in-broadcast-pkts\": \"0\", \n                        \"carrier-transitions\": \"0\",\n                        \"interface-transitions\": \"0\",\n                        \"link-transitions\": \"0\",\n                        \"in-unknown-protos\": \"0\", \n                        \"in-octets\": \"0\",\n                        \"in-pkts\": \"0\",\n                        \"out-pkts\": \"0\",\n                        \"resets\": \"0\"\n                    }\n                }, \n                \"name\": \"eth0\"\n            }\n        ]\n    }\n}\n"
  },
  {
    "path": "tests/serialise/openconfig-serialise/json/interfaces_ph.False-flt.True-m.default.json",
    "content": "{\n    \"interfaces\": {\n        \"interface\": {\n            \"eth0\": {\n                \"name\": \"eth0\"\n            }\n        }\n    }\n}"
  },
  {
    "path": "tests/serialise/openconfig-serialise/json/interfaces_ph.False-flt.True-m.ietf.json",
    "content": "{\n    \"openconfig-interfaces:interfaces\": {\n        \"interface\": [\n            {\n                \"name\": \"eth0\"\n            }\n        ]\n    }\n}"
  },
  {
    "path": "tests/serialise/openconfig-serialise/json/interfaces_ph.True-flt.False-m.default.json",
    "content": "{\n    \"interfaces\": {\n        \"interface\": {\n            \"eth0\": {\n                \"hold-time\": {\n                    \"state\": {\n                        \"down\": 0, \n                        \"up\": 0\n                    }, \n                    \"config\": {\n                        \"down\": 0, \n                        \"up\": 0\n                    }\n                }, \n                \"config\": {\n                    \"enabled\": true, \n                    \"loopback-mode\": \"NONE\",\n                    \"type\": \"\", \n                    \"description\": \"\", \n                    \"name\": \"eth0\", \n                    \"mtu\": 0\n                },\n                \"penalty-based-aied\": {\n                    \"config\": {\n                        \"decay-half-life\": 0,\n                        \"flap-penalty\": 0,\n                        \"max-suppress-time\": 0,\n                        \"reuse-threshold\": 0,\n                        \"suppress-threshold\": 0\n                    },\n                    \"state\": {\n                        \"decay-half-life\": 0,\n                        \"flap-penalty\": 0,\n                        \"max-suppress-time\": 0,\n                        \"reuse-threshold\": 0,\n                        \"suppress-threshold\": 0\n                    }\n                }, \n                \"state\": {\n                    \"name\": \"\", \n                    \"type\": \"\", \n                    \"description\": \"\", \n                    \"enabled\": true, \n                    \"loopback-mode\": \"NONE\",\n                    \"logical\": false,\n                    \"management\": false,\n                    \"cpu\": false,\n                    \"admin-status\": \"\", \n                    \"mtu\": 0, \n                    \"ifindex\": 0, \n                    \"last-change\": 0, \n                    \"oper-status\": \"\", \n                    \"counters\": {\n                        \"in-fcs-errors\": 0, \n                        \"in-errors\": 0, \n                        \"in-discards\": 0, \n                        \"out-broadcast-pkts\": 0, \n                        \"out-errors\": 0, \n                        \"out-multicast-pkts\": 0, \n                        \"in-multicast-pkts\": 0, \n                        \"last-clear\": 0, \n                        \"out-octets\": 0, \n                        \"in-unicast-pkts\": 0, \n                        \"out-unicast-pkts\": 0, \n                        \"out-discards\": 0, \n                        \"in-broadcast-pkts\": 0, \n                        \"carrier-transitions\": 0,\n                        \"interface-transitions\": 0,\n                        \"link-transitions\": 0,\n                        \"in-unknown-protos\": 0, \n                        \"in-octets\": 0,\n                        \"in-pkts\":0,\n                        \"out-pkts\": 0,\n                        \"resets\": 0\n                    }\n                }, \n                \"name\": \"eth0\", \n                \"subinterfaces\": {\n                    \"subinterface\": {}\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "tests/serialise/openconfig-serialise/json/interfaces_ph.True-flt.False-m.ietf.json",
    "content": "{\n    \"openconfig-interfaces:interfaces\": {\n        \"interface\": [\n            {\n                \"hold-time\": {\n                    \"state\": {\n                        \"down\": 0, \n                        \"up\": 0\n                    }, \n                    \"config\": {\n                        \"down\": 0, \n                        \"up\": 0\n                    }\n                }, \n                \"config\": {\n                    \"enabled\": false, \n                    \"loopback-mode\": \"\",\n                    \"type\": \"\", \n                    \"description\": \"\", \n                    \"name\": \"eth0\", \n                    \"mtu\": 0\n                },\n                \"penalty-based-aied\": {\n                    \"config\": {\n                        \"decay-half-life\": 0,\n                        \"flap-penalty\": 0,\n                        \"max-suppress-time\": 0,\n                        \"reuse-threshold\": 0,\n                        \"suppress-threshold\": 0\n                    },\n                    \"state\": {\n                        \"decay-half-life\": 0,\n                        \"flap-penalty\": 0,\n                        \"max-suppress-time\": 0,\n                        \"reuse-threshold\": 0,\n                        \"suppress-threshold\": 0\n                    }\n                }, \n                \"state\": {\n                    \"name\": \"\", \n                    \"type\": \"\", \n                    \"description\": \"\", \n                    \"enabled\": false, \n                    \"logical\": false,\n                    \"loopback-mode\": \"\",\n                    \"management\": false,\n                    \"cpu\": false,\n                    \"admin-status\": \"\", \n                    \"mtu\": 0, \n                    \"ifindex\": 0, \n                    \"last-change\": \"0\", \n                    \"oper-status\": \"\", \n                    \"counters\": {\n                        \"in-fcs-errors\": \"0\", \n                        \"in-errors\": \"0\", \n                        \"in-discards\": \"0\", \n                        \"out-broadcast-pkts\": \"0\", \n                        \"out-errors\": \"0\", \n                        \"out-multicast-pkts\": \"0\", \n                        \"in-multicast-pkts\": \"0\", \n                        \"last-clear\": \"0\", \n                        \"out-octets\": \"0\", \n                        \"in-unicast-pkts\": \"0\", \n                        \"out-unicast-pkts\": \"0\", \n                        \"out-discards\": \"0\", \n                        \"in-broadcast-pkts\": \"0\", \n                        \"carrier-transitions\": \"0\",\n                        \"interface-transitions\": \"0\",\n                        \"link-transitions\": \"0\",\n                        \"in-unknown-protos\": \"0\", \n                        \"in-octets\": \"0\",\n                        \"in-pkts\": \"0\",\n                        \"out-pkts\": \"0\",\n                        \"resets\": \"0\"\n                    }\n                }, \n                \"name\": \"eth0\"\n            }\n        ]\n    }\n}\n"
  },
  {
    "path": "tests/serialise/openconfig-serialise/json/interfaces_ph.True-flt.True-m.default.json",
    "content": "{\n    \"interfaces\": {\n        \"interface\": {\n            \"eth0\": {\n                \"config\": {\n                    \"name\": \"eth0\"\n                }, \n                \"name\": \"eth0\"\n            }\n        }\n    }\n}"
  },
  {
    "path": "tests/serialise/openconfig-serialise/json/interfaces_ph.True-flt.True-m.ietf.json",
    "content": "{\n    \"openconfig-interfaces:interfaces\": {\n        \"interface\": [\n            {\n                \"config\": {\n                    \"name\": \"eth0\"\n                }, \n                \"name\": \"eth0\"\n            }\n        ]\n    }\n}"
  },
  {
    "path": "tests/serialise/openconfig-serialise/run.py",
    "content": "#!/usr/bin/env python\n\nimport json\nimport os.path\nimport unittest\n\nimport regex\n\nfrom pyangbind.lib.pybindJSON import dumps\nfrom pyangbind.lib.serialise import pybindIETFJSONEncoder, pybindJSONEncoder\nfrom pyangbind.lib.xpathhelper import YANGPathHelper\nfrom pyangbind.lib.yangtypes import YANGBool\nfrom tests.base import PyangBindTestCase\n\nTESTNAME = \"json-serialise\"\n\n\nclass OpenconfigSerialiseTests(PyangBindTestCase):\n    maxDiff = None\n    split_class_dir = True\n    module_name = \"ocbind\"\n    yang_files = [os.path.join(os.path.dirname(__file__), \"openconfig\", \"openconfig-interfaces.yang\")]\n    pyang_flags = [\"--use-xpathhelper\", \"-p %s\" % os.path.join(os.path.dirname(__file__), \"include\")]\n\n    remote_yang_files = [\n        {\n            \"local_path\": \"include\",\n            \"remote_prefix\": \"https://raw.githubusercontent.com/openconfig/public/master/release/models/\",\n            \"files\": [\n                \"openconfig-extensions.yang\",\n                \"types/openconfig-types.yang\",\n                \"types/openconfig-yang-types.yang\",\n                \"types/openconfig-inet-types.yang\",\n                \"optical-transport/openconfig-transport-types.yang\",\n                \"platform/openconfig-platform-types.yang\",\n            ],\n        },\n        {\n            \"local_path\": \"include\",\n            \"remote_prefix\": \"https://raw.githubusercontent.com/robshakir/yang/master/standard/ietf/RFC/\",\n            \"files\": [\"ietf-inet-types.yang\", \"ietf-yang-types.yang\"],\n        },\n        {\n            \"local_path\": \"openconfig\",\n            \"remote_prefix\": \"https://raw.githubusercontent.com/openconfig/public/master/release/models/\",\n            \"files\": [\"interfaces/openconfig-interfaces.yang\"],\n        },\n    ]\n\n    def setUp(self):\n        self.yang_helper = YANGPathHelper()\n\n    def test_json_generation(self):\n        json_dir = os.path.join(os.path.dirname(__file__), \"json\")\n        for file_name in os.listdir(json_dir):\n            with self.subTest(json_file=file_name), open(os.path.join(json_dir, file_name), \"r\") as file_handle:\n                parameters = regex.sub(\n                    \"interfaces\\_ph\\.(?P<pathhelper>[a-zA-Z]+)\\-flt\\.(?P<filter>[a-zA-Z]+)\\-m\\.(?P<mode>[a-zA-Z]+)\\.json\",\n                    \"\\g<pathhelper>||\\g<filter>||\\g<mode>\",\n                    file_name,\n                ).split(\"||\")\n                path_helper, config_filter, mode = (YANGBool(parameters[0]), YANGBool(parameters[1]), parameters[2])\n                if path_helper:\n                    instance = self.ocbind.openconfig_interfaces(path_helper=self.yang_helper)\n                else:\n                    instance = self.ocbind.openconfig_interfaces()\n\n                instance.interfaces.interface.add(\"eth0\")\n\n                expected_json = json.load(file_handle)\n                actual_json = json.loads(dumps(instance, filter=bool(config_filter), mode=mode))\n\n                self.assertEqual(\n                    expected_json, actual_json, \"Generated JSON did not match expected object for %s\" % file_name\n                )\n\n    def test_pybind_ietf_json_encoder_serialisation_with_path_helper(self):\n        instance = self.ocbind.openconfig_interfaces(path_helper=self.yang_helper)\n        instance.interfaces.interface.add(\"eth0\")\n\n        passed = True\n        try:\n            json.loads(json.dumps(pybindIETFJSONEncoder.generate_element(instance), cls=pybindIETFJSONEncoder))\n        except Exception:\n            passed = False\n\n        self.assertTrue(passed, \"Serialisation test for object with pybindIETFJSONEncoder threw an error\")\n\n    def test_pybind_ietf_json_encoder_serialisation_without_path_helper(self):\n        instance = self.ocbind.openconfig_interfaces()\n        instance.interfaces.interface.add(\"eth0\")\n\n        passed = True\n        try:\n            json.loads(json.dumps(pybindIETFJSONEncoder.generate_element(instance), cls=pybindIETFJSONEncoder))\n        except Exception:\n            passed = False\n\n        self.assertTrue(passed, \"Serialisation test for object with pybindIETFJSONEncoder threw an error\")\n\n    def test_direct_json_serialisation_of_instance_with_path_helper(self):\n        instance = self.ocbind.openconfig_interfaces(path_helper=self.yang_helper)\n        instance.interfaces.interface.add(\"eth0\")\n\n        passed = True\n        try:\n            json.loads(json.dumps(instance, cls=pybindJSONEncoder))\n        except Exception:\n            passed = False\n\n        self.assertTrue(passed)\n\n    def test_direct_json_serialisation_of_instance_without_path_helper(self):\n        instance = self.ocbind.openconfig_interfaces()\n        instance.interfaces.interface.add(\"eth0\")\n\n        passed = True\n        try:\n            json.loads(json.dumps(instance, cls=pybindJSONEncoder))\n        except Exception:\n            passed = False\n\n        self.assertTrue(passed)\n\n\nif __name__ == \"__main__\":\n    unittest.main()\n"
  },
  {
    "path": "tests/serialise/roundtrip/__init__.py",
    "content": ""
  },
  {
    "path": "tests/serialise/roundtrip/remote.yang",
    "content": "module remote {\n  yang-version \"1\";\n  namespace \"http://rob.sh/yang/test/serialise/remote\";\n  prefix \"foo\";\n\n  import roundtrip { prefix \"rt\"; }\n\n  organization \"BugReports Inc\";\n  contact \"A bug reporter\";\n\n\n  description\n      \"A test module\";\n  revision 2014-01-01 {\n      description \"april-fools\";\n      reference \"fooled-you\";\n  }\n  \n  identity VALUE_TWO {\n    base rt:BASE;\n  }\n  \n}\n\n"
  },
  {
    "path": "tests/serialise/roundtrip/roundtrip.yang",
    "content": "module roundtrip {\n  yang-version \"1\";\n  namespace \"http://rob.sh/yang/test/serialise/roundtrip\";\n  prefix \"foo\";\n  organization \"BugReports Inc\";\n  contact \"A bug reporter\";\n\n  description\n      \"A test module\";\n  revision 2014-01-01 {\n      description \"april-fools\";\n      reference \"fooled-you\";\n  }\n  \n  identity BASE;\n  identity VALUE_ONE { base BASE; }\n\n  container a {\n    leaf idref {\n      type identityref {\n        base BASE;\n      }\n    }\n  }\n}\n\n"
  },
  {
    "path": "tests/serialise/roundtrip/run.py",
    "content": "#!/usr/bin/env python\nfrom __future__ import unicode_literals\n\nimport json\nimport unittest\n\nimport pyangbind.lib.pybindJSON as pbJ\nimport pyangbind.lib.serialise as pbS\nfrom pyangbind.lib.xpathhelper import YANGPathHelper\n\nfrom tests.base import PyangBindTestCase\n\n\nclass RoundtripTests(PyangBindTestCase):\n    yang_files = [\"roundtrip.yang\", \"remote.yang\"]\n    maxDiff = None\n\n    def setUp(self):\n        self.yang_helper = YANGPathHelper()\n        self.rt_obj = self.bindings.roundtrip(path_helper=self.yang_helper)\n\n    def test_ietf_roundtrip_simple(self):\n        self.rt_obj.a.idref = \"VALUE_TWO\"\n        j = pbJ.dumps(self.rt_obj, mode=\"ietf\")\n        pbS.pybindJSONDecoder.load_ietf_json(json.loads(j), None, None, obj=self.rt_obj)\n\n    def test_roundtrip_simple(self):\n        self.rt_obj.a.idref = \"VALUE_TWO\"\n        j = pbJ.dumps(self.rt_obj)\n        pbS.pybindJSONDecoder.load_json(json.loads(j), None, None, obj=self.rt_obj)\n\n\nif __name__ == \"__main__\":\n    unittest.main()\n"
  },
  {
    "path": "tests/serialise/xml-deserialise/__init__.py",
    "content": ""
  },
  {
    "path": "tests/serialise/xml-deserialise/augment.yang",
    "content": "module augment {\n  yang-version \"1\";\n  namespace \"http://rob.sh/yang/test/deserialise/ietf-xml-deserialise/augment\";\n  prefix \"augpfx\";\n\n  import ietf-xml-deserialise { prefix ixd; }\n\n  organization \"BugReports Inc\";\n  contact \"A bug reporter\";\n\n  description\n      \"A test module\";\n  revision 2014-01-01 {\n      description \"april-fools\";\n      reference \"fooled-you\";\n  }\n\n  augment \"/ixd:augtarget\" {\n    leaf augleaf {\n      type string;\n    }\n  }\n\n}\n"
  },
  {
    "path": "tests/serialise/xml-deserialise/ietf-xml-deserialise.yang",
    "content": "module ietf-xml-deserialise {\n  yang-version \"1\";\n  namespace \"http://rob.sh/yang/test/deserialise/ietf-xml\";\n  prefix \"foo\";\n\n  import remote { prefix \"remote\"; }\n\n  organization \"BugReports Inc\";\n  contact \"A bug reporter\";\n\n  description\n      \"A test module\";\n  revision 2014-01-01 {\n      description \"april-fools\";\n      reference \"fooled-you\";\n  }\n\n  identity id-base {\n    description\n      \"base identity\";\n  }\n\n  identity idone {\n    base id-base;\n  }\n\n  identity idtwo {\n    base id-base;\n  }\n\n  typedef derived64 {\n    type uint64;\n  }\n\n  typedef decimaldefinedtype {\n    type decimal64 {\n      fraction-digits 2;\n    }\n  }\n\n  typedef decimalrangetype {\n    type decimal64 {\n      fraction-digits 10;\n      range \"1..max\";\n    }\n  }\n\n  typedef definedtype {\n    type string;\n  }\n\n  typedef definedtypeunion {\n    type union {\n      type uint8;\n      type string;\n    }\n  }\n\n  typedef nhopenum {\n    type enumeration {\n      enum DROP {}\n    }\n  }\n\n  typedef regexstring {\n    type string {\n      pattern '[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+';\n    }\n  }\n\n  typedef typedef-bits {\n    type bits {\n      bit bit1 {\n\tposition 1;\n      }\n      bit bit2 {\n\tposition 2;\n      }\n    }\n  }\n\n  container c1 {\n    list l1 {\n      key \"k1\";\n\n      leaf k1 {\n          type uint32;\n      }\n\n      leaf empty {\n        type empty;\n      }\n\n      leaf uint8 {\n        type uint8;\n      }\n\n      leaf uint16 {\n        type uint16;\n      }\n\n      leaf uint32 {\n        type uint32;\n      }\n\n      leaf uint64 {\n        type uint64;\n      }\n\n      leaf int8 {\n        type int8;\n      }\n\n      leaf int16 {\n        type int16;\n      }\n\n      leaf int32 {\n        type int32;\n      }\n\n      leaf int64 {\n        type int64;\n      }\n\n      leaf decleaf {\n        type decimal64 {\n          fraction-digits 4;\n        }\n      }\n\n      leaf restricted-integer {\n        type int8 {\n          range \"5..10\";\n        }\n      }\n\n      leaf string {\n        type string;\n      }\n\n      leaf restricted-string {\n        type string {\n          pattern \"a.*\";\n        }\n      }\n\n      leaf union {\n        type union {\n          type string;\n          type uint32;\n        }\n      }\n\n      leaf-list union-list {\n        type union {\n          type int32;\n          type string;\n        }\n      }\n\n      leaf leafref {\n        type leafref {\n          path \"/c1/t1/target\";\n        }\n      }\n\n      leaf binary {\n        type binary;\n      }\n\n      leaf boolean {\n        type boolean;\n      }\n\n      leaf enumeration {\n        type enumeration {\n          enum one;\n          enum two;\n        }\n      }\n\n      leaf identityref {\n        type identityref {\n          base id-base;\n        }\n      }\n\n      leaf unset-identityref {\n        type identityref {\n          base id-base;\n        }\n      }\n\n      leaf remote-identityref {\n        type identityref {\n          base remote:cheese;\n        }\n      }\n\n      leaf typedef-one {\n        type definedtype;\n      }\n\n      leaf typedef-two {\n        type definedtypeunion;\n      }\n\n      choice test-choice {\n        case one {\n          leaf one-leaf {\n            type string;\n          }\n        }\n        case two {\n          leaf two-leaf {\n            type string;\n          }\n        }\n      }\n\n      leaf-list ll {\n        type string;\n      }\n\n      leaf-list next-hop {\n        type union {\n          type regexstring;\n          type nhopenum;\n          type string;\n        }\n      }\n\n      leaf typedef-decimal {\n        type decimaldefinedtype;\n      }\n\n      leaf range-decimal {\n        type decimal64 {\n          fraction-digits 10;\n          range \"1..10\";\n        }\n      }\n\n      leaf typedef-decimalrange {\n        type decimalrangetype;\n      }\n\n      leaf uint64type {\n        type derived64;\n      }\n\n      leaf typedefed-bits {\n\ttype typedef-bits;\n      }\n\n      leaf leaf-bits {\n\ttype bits {\n\t  bit b1 {\n\t    position 1;\n\t  }\n\t  bit b2 {\n\t    position 2;\n\t  }\n\t  bit b3 {\n\t    position 3;\n\t  }\n\t}\n      }\n    }\n\n    list l2 {\n      key \"k1\";\n      ordered-by user;\n\n      leaf k1 {\n       type uint32;\n      }\n    }\n\n    list t1 {\n      key \"target\";\n      leaf target {\n        type string;\n      }\n    }\n\n    list mkey {\n      key \"leaf-one leaf-two\";\n\n      leaf leaf-one {\n        type string;\n      }\n\n      leaf leaf-two {\n        type int8;\n      }\n    }\n\n    list patternkey {\n      key \"name\";\n\n      leaf name {\n        type string {\n          pattern 'n.*';\n        }\n      }\n\n      leaf uid {\n        type string {\n          length 5..10;\n        }\n      }\n    }\n  }\n\n  container augtarget {\n\n  }\n}\n"
  },
  {
    "path": "tests/serialise/xml-deserialise/remote.yang",
    "content": "module remote {\n  yang-version \"1\";\n  namespace \"http://rob.sh/yang/test/deserialise/ietf-xml-deserialise/remote\";\n  prefix \"remote\";\n  organization \"BugReports Inc\";\n  contact \"A bug reporter\";\n\n  description\n      \"A test module\";\n  revision 2014-01-01 {\n      description \"april-fools\";\n      reference \"fooled-you\";\n  }\n\n  identity cheese {\n    description \"base\";\n  }\n\n  identity stilton {\n    base cheese;\n  }\n}\n"
  },
  {
    "path": "tests/serialise/xml-deserialise/run.py",
    "content": "#!/usr/bin/env python\n\nimport os.path\nimport unittest\n\nfrom lxml import objectify\n\nfrom pyangbind.lib.serialise import pybindIETFXMLDecoder, pybindIETFXMLEncoder\nfrom tests.base import PyangBindTestCase\nfrom tests.serialise.xml_utils import xml_tree_equivalence\n\n\nclass XMLDeserialiseTests(PyangBindTestCase):\n    yang_files = [\"ietf-xml-deserialise.yang\", \"augment.yang\"]\n    maxDiff = None\n\n    def test_deserialise_full_container_roundtrip(self):\n        with open(os.path.join(os.path.dirname(__file__), \"xml\", \"obj.xml\"), \"r\") as fp:\n            external_xml = fp.read()\n            existing_doc = objectify.fromstring(external_xml)\n\n        result = pybindIETFXMLDecoder().decode(external_xml, self.bindings, \"ietf_xml_deserialise\")\n        doc = pybindIETFXMLEncoder().encode(result)\n\n        self.assertTrue(xml_tree_equivalence(doc, existing_doc), \"Generated XML did not match the expected output.\")\n\n\nif __name__ == \"__main__\":\n    unittest.main()\n"
  },
  {
    "path": "tests/serialise/xml-deserialise/xml/obj.xml",
    "content": "<ietf-xml-deserialise xmlns=\"http://rob.sh/yang/test/deserialise/ietf-xml\">\n  <c1>\n    <l1>\n      <k1>1</k1>\n      <empty/>\n      <uint8>1</uint8>\n      <uint16>1</uint16>\n      <uint32>1</uint32>\n      <uint64>1</uint64>\n      <int8>1</int8>\n      <int16>1</int16>\n      <int32>1</int32>\n      <int64>1</int64>\n      <decleaf>42.4422</decleaf>\n      <restricted-integer>6</restricted-integer>\n      <string>bear</string>\n      <restricted-string>aardvark</restricted-string>\n      <union>16</union>\n      <union-list>16</union-list>\n      <union-list>chicken</union-list>\n      <union-list>-100</union-list>\n      <leafref>16</leafref>\n      <binary>eWFuZw==</binary>\n      <boolean>true</boolean>\n      <enumeration>one</enumeration>\n      <identityref>idone</identityref>\n      <remote-identityref xmlns:remote=\"http://rob.sh/yang/test/deserialise/ietf-xml-deserialise/remote\">remote:stilton</remote-identityref>\n      <typedef-one>test</typedef-one>\n      <typedef-two>8</typedef-two>\n      <one-leaf>hi</one-leaf>\n      <ll>1</ll>\n      <ll>2</ll>\n      <ll>3.0</ll>\n      <ll>4</ll>\n      <next-hop>DROP</next-hop>\n      <next-hop>192.0.2.1</next-hop>\n      <next-hop>TEST</next-hop>\n      <typedef-decimal>32.29</typedef-decimal>\n      <range-decimal>4.44443322</range-decimal>\n      <typedef-decimalrange>33.44</typedef-decimalrange>\n      <uint64type>4194304</uint64type>\n      <typedefed-bits>bit1 bit2</typedefed-bits>\n      <leaf-bits>b1 b3</leaf-bits>\n    </l1>\n    <l2>\n      <k1>1</k1>\n    </l2>\n    <l2>\n      <k1>2</k1>\n    </l2>\n    <l2>\n      <k1>3</k1>\n    </l2>\n    <l2>\n      <k1>4</k1>\n    </l2>\n    <l2>\n      <k1>5</k1>\n    </l2>\n    <l2>\n      <k1>6</k1>\n    </l2>\n    <l2>\n      <k1>7</k1>\n    </l2>\n    <l2>\n      <k1>8</k1>\n    </l2>\n    <l2>\n      <k1>9</k1>\n    </l2>\n    <t1>\n      <target>16</target>\n    </t1>\n    <t1>\n      <target>32</target>\n    </t1>\n    <mkey>\n      <leaf-one>foo</leaf-one>\n      <leaf-two>1</leaf-two>\n    </mkey>\n    <mkey>\n      <leaf-one>bar</leaf-one>\n      <leaf-two>2</leaf-two>\n    </mkey>\n    <patternkey>\n      <name>not_a_name</name>\n      <uid>abcdef-hij</uid>\n    </patternkey>\n    <patternkey>\n      <name>name_me_not</name>\n      <uid>01234-45</uid>\n    </patternkey>\n  </c1>\n  <augtarget>\n    <augleaf xmlns=\"http://rob.sh/yang/test/deserialise/ietf-xml-deserialise/augment\">teststring</augleaf>\n  </augtarget>\n</ietf-xml-deserialise>\n"
  },
  {
    "path": "tests/serialise/xml-serialise/__init__.py",
    "content": ""
  },
  {
    "path": "tests/serialise/xml-serialise/augment.yang",
    "content": "module augment {\n  yang-version \"1\";\n  namespace \"http://rob.sh/yang/test/serialise/ietf-xml-serialise/augment\";\n  prefix \"augpfx\";\n\n  import ietf-xml-serialise { prefix ixs; }\n\n  organization \"BugReports Inc\";\n  contact \"A bug reporter\";\n\n  description\n      \"A test module\";\n  revision 2014-01-01 {\n      description \"april-fools\";\n      reference \"fooled-you\";\n  }\n\n  augment \"/ixs:augtarget\" {\n    leaf augleaf {\n      type string;\n    }\n  }\n\n}\n"
  },
  {
    "path": "tests/serialise/xml-serialise/ietf-xml-serialise.yang",
    "content": "module ietf-xml-serialise {\n  yang-version \"1\";\n  namespace \"http://rob.sh/yang/test/serialise/ietf-xml\";\n  prefix \"foo\";\n\n  import remote { prefix \"remote\"; }\n\n  organization \"BugReports Inc\";\n  contact \"A bug reporter\";\n\n  description\n      \"A test module\";\n  revision 2014-01-01 {\n      description \"april-fools\";\n      reference \"fooled-you\";\n  }\n\n  identity id-base {\n    description\n      \"base identity\";\n  }\n\n  identity idone {\n    base id-base;\n  }\n\n  identity idtwo {\n    base id-base;\n  }\n\n  typedef derived64 {\n    type uint64;\n  }\n\n  typedef decimaldefinedtype {\n    type decimal64 {\n      fraction-digits 2;\n    }\n  }\n\n  typedef decimalrangetype {\n    type decimal64 {\n      fraction-digits 10;\n      range \"1..max\";\n    }\n  }\n\n  typedef definedtype {\n    type string;\n  }\n\n  typedef definedtypeunion {\n    type union {\n      type uint8;\n      type string;\n    }\n  }\n\n  typedef nhopenum {\n    type enumeration {\n      enum DROP {}\n    }\n  }\n\n  typedef regexstring {\n    type string {\n      pattern '[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+';\n    }\n  }\n\n  typedef typedef-bits {\n    type bits {\n      bit bit1 {\n\tposition 1;\n      }\n      bit bit2 {\n\tposition 2;\n      }\n    }\n  }\n\n  container c1 {\n    list l1 {\n      key \"k1\";\n\n      leaf k1 {\n          type uint32;\n      }\n\n      leaf empty {\n        type empty;\n      }\n\n      leaf uint8 {\n        type uint8;\n      }\n\n      leaf uint16 {\n        type uint16;\n      }\n\n      leaf uint32 {\n        type uint32;\n      }\n\n      leaf uint64 {\n        type uint64;\n      }\n\n      leaf int8 {\n        type int8;\n      }\n\n      leaf int16 {\n        type int16;\n      }\n\n      leaf int32 {\n        type int32;\n      }\n\n      leaf int64 {\n        type int64;\n      }\n\n      leaf decleaf {\n        type decimal64 {\n          fraction-digits 4;\n        }\n      }\n\n      leaf restricted-integer {\n        type int8 {\n          range \"5..10\";\n        }\n      }\n\n      leaf string {\n        type string;\n      }\n\n      leaf restricted-string {\n        type string {\n          pattern \"a.*\";\n        }\n      }\n\n      leaf union {\n        type union {\n          type string;\n          type uint32;\n        }\n      }\n\n      leaf-list union-list {\n        type union {\n          type uint32;\n          type string;\n        }\n      }\n\n      leaf leafref {\n        type leafref {\n          path \"/c1/t1/target\";\n        }\n      }\n\n      leaf binary {\n        type binary;\n      }\n\n      leaf boolean {\n        type boolean;\n      }\n\n      leaf enumeration {\n        type enumeration {\n          enum one;\n          enum two;\n        }\n      }\n\n      leaf identityref {\n        type identityref {\n          base id-base;\n        }\n      }\n\n      leaf unset-identityref {\n        type identityref {\n          base id-base;\n        }\n      }\n\n      leaf remote-identityref {\n        type identityref {\n          base remote:cheese;\n        }\n      }\n\n      leaf typedef-one {\n        type definedtype;\n      }\n\n      leaf typedef-two {\n        type definedtypeunion;\n      }\n\n      choice test-choice {\n        case one {\n          leaf one-leaf {\n            type string;\n          }\n        }\n        case two {\n          leaf two-leaf {\n            type string;\n          }\n        }\n      }\n      leaf-list ll {\n        type string;\n      }\n\n      leaf-list next-hop {\n        type union {\n          type regexstring;\n          type nhopenum;\n          type string;\n        }\n      }\n\n      leaf typedef-decimal {\n        type decimaldefinedtype;\n      }\n\n      leaf range-decimal {\n        type decimal64 {\n          fraction-digits 10;\n          range \"1..10\";\n        }\n      }\n\n      leaf typedef-decimalrange {\n        type decimalrangetype;\n      }\n\n      leaf uint64type {\n        type derived64;\n      }\n\n      leaf typedefed-bits {\n\ttype typedef-bits;\n      }\n\n      leaf leaf-bits {\n\ttype bits {\n\t  bit b1 {\n\t    position 1;\n\t  }\n\t  bit b2 {\n\t    position 2;\n\t  }\n\t  bit b3 {\n\t    position 3;\n\t  }\n\t}\n      }\n    }\n\n    list l2 {\n      key \"k1\";\n      ordered-by user;\n\n      leaf k1 {\n       type uint32;\n      }\n    }\n\n    list t1 {\n      key \"target\";\n      leaf target {\n        type string;\n      }\n    }\n  }\n\n  container augtarget {\n\n  }\n}\n"
  },
  {
    "path": "tests/serialise/xml-serialise/remote.yang",
    "content": "module remote {\n  yang-version \"1\";\n  namespace \"http://rob.sh/yang/test/serialise/ietf-xml-serialise/remote\";\n  prefix \"remote\";\n  organization \"BugReports Inc\";\n  contact \"A bug reporter\";\n\n  description\n      \"A test module\";\n  revision 2014-01-01 {\n      description \"april-fools\";\n      reference \"fooled-you\";\n  }\n\n  identity cheese {\n    description \"base\";\n  }\n\n  identity stilton {\n    base cheese;\n  }\n}\n"
  },
  {
    "path": "tests/serialise/xml-serialise/run.py",
    "content": "#!/usr/bin/env python\n\nimport os.path\nimport unittest\nfrom decimal import Decimal\n\nfrom lxml import objectify\n\nfrom pyangbind.lib.serialise import pybindIETFXMLEncoder\nfrom pyangbind.lib.xpathhelper import YANGPathHelper\nfrom tests.base import PyangBindTestCase\nfrom tests.serialise.xml_utils import xml_tree_equivalence\n\n\nclass XMLSerialiseTests(PyangBindTestCase):\n    yang_files = [\"ietf-xml-serialise.yang\", \"augment.yang\"]\n    maxDiff = None\n\n    def setUp(self):\n        self.yang_helper = YANGPathHelper()\n        self.serialise_obj = self.bindings.ietf_xml_serialise(path_helper=self.yang_helper)\n\n    def test_serialise_full_container(self):\n        self.serialise_obj.c1.l1.add(1)\n        for signed in [\"int\", \"uint\"]:\n            for size in [8, 16, 32, 64]:\n                name = \"%s%s\" % (signed, size)\n                setter = getattr(self.serialise_obj.c1.l1[1], \"_set_%s\" % name)\n                setter(1)\n        self.serialise_obj.c1.l1[1].restricted_integer = 6\n        self.serialise_obj.c1.l1[1].string = \"bear\"\n        self.serialise_obj.c1.l1[1].restricted_string = \"aardvark\"\n        self.serialise_obj.c1.l1[1].union = 16\n        self.serialise_obj.c1.l1[1].union_list.append(16)\n        self.serialise_obj.c1.l1[1].union_list.append(\"chicken\")\n        self.serialise_obj.c1.l1[1].empty = True\n\n        self.serialise_obj.c1.t1.add(16)\n        self.serialise_obj.c1.t1.add(32)\n        self.serialise_obj.c1.l1[1].leafref = 16\n\n        self.serialise_obj.c1.l1[1].binary = b\"yang\"\n        self.serialise_obj.c1.l1[1].boolean = True\n        self.serialise_obj.c1.l1[1].enumeration = \"one\"\n        self.serialise_obj.c1.l1[1].identityref = \"idone\"\n        self.serialise_obj.c1.l1[1].remote_identityref = \"stilton\"\n        self.serialise_obj.c1.l1[1].typedef_one = \"test\"\n        self.serialise_obj.c1.l1[1].typedef_two = 8\n        self.serialise_obj.c1.l1[1].one_leaf = \"hi\"\n        self.serialise_obj.c1.l1[1].uint64type = 2**22\n        self.serialise_obj.c1.l1[1].typedef_decimal = 32.29\n        self.serialise_obj.c1.l1[1].typedef_decimalrange = Decimal(\"33.44\")\n        self.serialise_obj.c1.l1[1].range_decimal = Decimal(\"4.44443322\")\n        for i in range(1, 5):\n            self.serialise_obj.c1.l1[1].ll.append(str(i))\n        self.serialise_obj.c1.l1[1].next_hop.append(\"DROP\")\n        self.serialise_obj.c1.l1[1].next_hop.append(\"192.0.2.1\")\n        self.serialise_obj.c1.l1[1].next_hop.append(\"TEST\")\n        self.serialise_obj.augtarget.augleaf = \"teststring\"\n        self.serialise_obj.c1.l1[1].decleaf = Decimal(\"42.4422\")\n        self.serialise_obj.c1.l1[1].typedefed_bits = \"bit1 bit2\"\n        self.serialise_obj.c1.l1[1].leaf_bits = \"b1 b3\"\n        for i in range(1, 10):\n            self.serialise_obj.c1.l2.add(i)\n\n        doc = pybindIETFXMLEncoder().encode(self.serialise_obj)\n\n        with open(os.path.join(os.path.dirname(__file__), \"xml\", \"obj.xml\"), \"r\") as fp:\n            external_xml = fp.read()\n            existing_doc = objectify.fromstring(external_xml)\n\n        self.assertTrue(xml_tree_equivalence(doc, existing_doc), \"Generated XML did not match the expected output.\")\n\n\nif __name__ == \"__main__\":\n    unittest.main()\n"
  },
  {
    "path": "tests/serialise/xml-serialise/xml/obj.xml",
    "content": "<ietf-xml-serialise xmlns=\"http://rob.sh/yang/test/serialise/ietf-xml\">\n  <c1>\n    <l1>\n      <k1>1</k1>\n      <empty/>\n      <uint8>1</uint8>\n      <uint16>1</uint16>\n      <uint32>1</uint32>\n      <uint64>1</uint64>\n      <int8>1</int8>\n      <int16>1</int16>\n      <int32>1</int32>\n      <int64>1</int64>\n      <decleaf>42.4422</decleaf>\n      <restricted-integer>6</restricted-integer>\n      <string>bear</string>\n      <restricted-string>aardvark</restricted-string>\n      <union>16</union>\n      <union-list>16</union-list>\n      <union-list>chicken</union-list>\n      <leafref>16</leafref>\n      <binary>eWFuZw==</binary>\n      <boolean>true</boolean>\n      <enumeration>one</enumeration>\n      <identityref>idone</identityref>\n      <remote-identityref xmlns:remote=\"http://rob.sh/yang/test/serialise/ietf-xml-serialise/remote\">remote:stilton</remote-identityref>\n      <typedef-one>test</typedef-one>\n      <typedef-two>8</typedef-two>\n      <one-leaf>hi</one-leaf>\n      <ll>1</ll>\n      <ll>2</ll>\n      <ll>3</ll>\n      <ll>4</ll>\n      <next-hop>DROP</next-hop>\n      <next-hop>192.0.2.1</next-hop>\n      <next-hop>TEST</next-hop>\n      <typedef-decimal>32.29</typedef-decimal>\n      <range-decimal>4.44443322</range-decimal>\n      <typedef-decimalrange>33.44</typedef-decimalrange>\n      <uint64type>4194304</uint64type>\n      <typedefed-bits>bit1 bit2</typedefed-bits>\n      <leaf-bits>b1 b3</leaf-bits>\n    </l1>\n    <l2>\n      <k1>1</k1>\n    </l2>\n    <l2>\n      <k1>2</k1>\n    </l2>\n    <l2>\n      <k1>3</k1>\n    </l2>\n    <l2>\n      <k1>4</k1>\n    </l2>\n    <l2>\n      <k1>5</k1>\n    </l2>\n    <l2>\n      <k1>6</k1>\n    </l2>\n    <l2>\n      <k1>7</k1>\n    </l2>\n    <l2>\n      <k1>8</k1>\n    </l2>\n    <l2>\n      <k1>9</k1>\n    </l2>\n    <t1>\n      <target>16</target>\n    </t1>\n    <t1>\n      <target>32</target>\n    </t1>\n  </c1>\n  <augtarget>\n    <augleaf xmlns=\"http://rob.sh/yang/test/serialise/ietf-xml-serialise/augment\">teststring</augleaf>\n  </augtarget>\n</ietf-xml-serialise>\n"
  },
  {
    "path": "tests/serialise/xml_utils.py",
    "content": "def xml_tree_equivalence(e1, e2):\n    \"\"\"\n    Rough XML comparison function based on https://stackoverflow.com/a/24349916/1294458.\n    This is necessary to provide some sort of structural equivalence of a generated XML\n    tree; however there is no XML deserialisation implementation yet. A naive text comparison\n    fails because it seems it enforces ordering, which seems to vary between python versions\n    etc. Strictly speaking, I think, only the *leaf-list* element mandates ordering.. this\n    function uses simple sorting on tag name, which I think, should maintain the relative\n    order of these elements.\n    \"\"\"\n    if e1.tag != e2.tag:\n        return False\n    if e1.text != e2.text:\n        return False\n    if e1.tail != e2.tail:\n        return False\n    if e1.attrib != e2.attrib:\n        return False\n    if len(e1) != len(e2):\n        return False\n    e1_children = sorted(e1.getchildren(), key=lambda x: x.tag)\n    e2_children = sorted(e2.getchildren(), key=lambda x: x.tag)\n    if len(e1_children) != len(e2_children):\n        return False\n    return all(xml_tree_equivalence(c1, c2) for c1, c2 in zip(e1_children, e2_children))\n"
  },
  {
    "path": "tests/split-classes/__init__.py",
    "content": ""
  },
  {
    "path": "tests/split-classes/run.py",
    "content": "#!/usr/bin/env python\n\nimport unittest\n\nfrom tests.base import PyangBindTestCase\n\n\nclass SplitClassesTests(PyangBindTestCase):\n    yang_files = [\"split-classes.yang\"]\n    split_class_dir = True\n\n    def setUp(self):\n        self.instance = self.bindings.split_classes()\n\n    def test_first_container_name_matches_module_name(self):\n        allowed = True\n        try:\n            self.instance.split_classes.test = \"howdy\"\n        except ValueError:\n            allowed = False\n        self.assertTrue(allowed)\n\n    def test_hierarchy_with_repeating_name(self):\n        allowed = True\n        try:\n            self.instance.remote.remote.remote.remote = \"hi\"\n        except ValueError:\n            allowed = False\n        self.assertTrue(allowed)\n\n    def test_add_entry_to_case_one_container(self):\n        self.instance.choices.case_one_container.user.add(\"first\")\n        self.assertEqual(list(self.instance.choices.case_one_container.user.keys()), [\"first\"])\n\n    def test_adding_entry_to_other_case_after_first_case(self):\n        self.instance.choices.case_one_container.user.add(\"first\")\n        self.instance.choices.case_two_container.user.add(\"second\")\n        self.assertEqual(list(self.instance.choices.case_two_container.user.keys()), [\"second\"])\n\n    def test_adding_entry_to_other_case_clears_first_case(self):\n        self.instance.choices.case_one_container.user.add(\"first\")\n        self.instance.choices.case_two_container.user.add(\"second\")\n        self.assertEqual(list(self.instance.choices.case_one_container.user.keys()), [])\n\n\nif __name__ == \"__main__\":\n    unittest.main()\n"
  },
  {
    "path": "tests/split-classes/split-classes.yang",
    "content": "module split-classes {\n    yang-version \"1\";\n    namespace \"http://rob.sh/yang/test/split-classes\";\n    prefix \"remote\";\n    organization \"BugReports Inc\";\n    contact \"A bug reporter\";\n\n    description\n        \"A test module\";\n    revision 2014-01-01 {\n        description \"april-fools\";\n        reference \"fooled-you\";\n    }\n\n    container split-classes {\n        leaf test {\n            type string;\n        }\n    }\n\n    container remote {\n        container remote {\n            container remote {\n                leaf remote {\n                    type string;\n                }\n            }\n        }\n    }\n\n    container choices {\n        choice choice-one {\n            case case-one {\n                container case-one-container {\n                    list user {\n                        key \"username\";\n                        leaf username {\n                            type string;\n                        }\n                    }\n                }\n            }\n\n            case case-two {\n                container case-two-container {\n                    list user {\n                        key \"username\";\n                        leaf username {\n                            type string;\n                        }\n                    }\n                }\n            }\n        }\n    }\n}"
  },
  {
    "path": "tests/strings/__init__.py",
    "content": ""
  },
  {
    "path": "tests/strings/run.py",
    "content": "#!/usr/bin/env python\nfrom __future__ import unicode_literals\n\nimport unittest\n\nfrom tests.base import PyangBindTestCase\n\n\nclass StringTests(PyangBindTestCase):\n    yang_files = [\"string.yang\"]\n\n    def setUp(self):\n        self.instance = self.bindings.string()\n\n    def test_string_leaf_is_not_changed_by_default(self):\n        self.assertFalse(self.instance.string_container.string_leaf._changed())\n\n    def test_set_basic_string_value_on_string_leaf(self):\n        self.instance.string_container.string_leaf = \"TestValue\"\n        self.assertEqual(self.instance.string_container.string_leaf, \"TestValue\")\n\n    def test_integer_gets_cast_to_string(self):\n        self.instance.string_container.string_leaf = 1\n        self.assertEqual(self.instance.string_container.string_leaf, \"1\")\n\n    def test_string_leaf_gets_marked_as_changed(self):\n        self.instance.string_container.string_leaf = \"TestValue\"\n        self.assertTrue(self.instance.string_container.string_leaf._changed())\n\n    def test_concatenation_to_string_leaf(self):\n        self.instance.string_container.string_leaf = \"TestValue\"\n        self.instance.string_container.string_leaf += \"Addition\"\n        self.assertEqual(self.instance.string_container.string_leaf, \"TestValueAddition\")\n\n    def test_string_leaf_with_default_is_blank(self):\n        self.assertEqual(self.instance.string_container.string_default_leaf, \"\")\n\n    def test_string_leaf_with_default_has_correct_default_value_hidden(self):\n        self.assertEqual(self.instance.string_container.string_default_leaf._default, \"string\")\n\n    def test_string_leaf_with_default_and_pattern_has_correct_default_value_hidden(self):\n        self.assertEqual(self.instance.string_container.restricted_string_default._default, \"beep\")\n\n    def test_set_valid_value_on_restricted_string(self):\n        allowed = True\n        try:\n            self.instance.string_container.restricted_string = \"aardvark\"\n        except ValueError:\n            allowed = False\n        self.assertTrue(allowed)\n\n    def test_set_invalid_value_on_restricted_string(self):\n        with self.assertRaises(ValueError):\n            self.instance.string_container.restricted_string = \"bear\"\n\n    def test_fixed_length_string(self):\n        for value, valid in [(\"a\", False), (\"ab\", True), (\"abc\", False)]:\n            with self.subTest(value=value, valid=valid):\n                allowed = True\n                try:\n                    self.instance.string_container.restricted_length_string = value\n                except ValueError:\n                    allowed = False\n                self.assertEqual(allowed, valid)\n\n    def test_fixed_length_string_with_pattern(self):\n        for value, valid in [(\"a\", False), (\"ba\", False), (\"abc\", False), (\"ab\", True)]:\n            with self.subTest(value=value, valid=valid):\n                allowed = True\n                try:\n                    self.instance.string_container.restricted_length_and_pattern_string = value\n                except ValueError:\n                    allowed = False\n                self.assertEqual(allowed, valid)\n\n    def test_string_with_length_as_range_with_max(self):\n        for value, valid in [(\"short\", False), (\"loooooooong\", True)]:\n            with self.subTest(value=value, valid=valid):\n                allowed = True\n                try:\n                    self.instance.string_container.restricted_length_string_with_range = value\n                except ValueError:\n                    allowed = False\n                self.assertEqual(allowed, valid)\n\n    def test_string_with_length_as_range_with_upper_bound(self):\n        for value, valid in [(\"short\", False), (\"loooooooong\", True), (\"toooooooooolooooooooong\", False)]:\n            with self.subTest(value=value, valid=valid):\n                allowed = True\n                try:\n                    self.instance.string_container.restricted_length_string_range_two = value\n                except ValueError:\n                    allowed = False\n                self.assertEqual(allowed, valid)\n\n    def test_string_leaf_with_complex_length(self):\n        for value, valid in [\n            (\"strLength10\", True),\n            (\"LengthTwelve\", True),\n            (\"strTwentyOneCharsLong\", False),\n            (\"aReallyLongStringMoreThan30CharsLong\", True),\n            (\"anEvenLongerStringThatIsMoreThanFortyChars\", False),\n        ]:\n            with self.subTest(value=value, valid=valid):\n                allowed = True\n                try:\n                    self.instance.string_container.stringLeafWithComplexLength = value\n                except ValueError:\n                    allowed = False\n                self.assertEqual(allowed, valid)\n\n    def test_string_leaf_pattern_with_dollar(self):\n        for value, valid in [(\"fi$h\", True), (\"void\", False), (\"fi$ho\", True)]:\n            with self.subTest(value=value, valid=valid):\n                allowed = True\n                try:\n                    self.instance.string_container.stringLeafWithPatternWithDollar = value\n                except ValueError:\n                    allowed = False\n                self.assertEqual(allowed, valid)\n\n    def test_string_leaf_pattern_with_dollar_at_end(self):\n        for value, valid in [(\"fi$h\", True), (\"void\", False), (\"fi$ho\", False)]:\n            with self.subTest(value=value, valid=valid):\n                allowed = True\n                try:\n                    self.instance.string_container.dollarAtEnd = value\n                except ValueError:\n                    allowed = False\n                self.assertEqual(allowed, valid)\n\n\nif __name__ == \"__main__\":\n    unittest.main()\n"
  },
  {
    "path": "tests/strings/string.yang",
    "content": "module string {\n    yang-version \"1\";\n    namespace \"http://rob.sh/yang/test/string\";\n    prefix \"foo\";\n    organization \"BugReports Inc\";\n    contact \"A bug reporter\";\n\n    description\n        \"A test module\";\n    revision 2014-01-01 {\n        description \"april-fools\";\n        reference \"fooled-you\";\n    }\n\n    container string-container {\n        description\n            \"A container\";\n\n        leaf string-leaf {\n            type string;\n            description\n              \"A test leaf for a string test\";\n        }\n\n        leaf string-default-leaf {\n            type string;\n            default \"string\";\n            description\n              \"A string that has a default value\";\n        }\n\n        leaf restricted-string {\n            type string {\n                pattern \"^a.*\";\n            }\n            description\n                \"A test leaf with a restriction that\n                the string can only start with an a\";\n        }\n\n        leaf restricted-string-default {\n            type string {\n                pattern \"b.*\";\n            }\n            description\n                \"A test leaf with a restriction that\n                the string can only start with a b\n                and a default value\";\n            default \"beep\";\n        }\n\n        leaf restricted-length-string {\n            type string {\n                length 2;\n            }\n            description\n                \"A test leaf that has a restricted\n                length\";\n        }\n\n        leaf restricted-length-and-pattern-string {\n            type string {\n                length 2;\n                pattern \"a.*\";\n            }\n            description\n                \"A test leaf that has a restricted\n                length and pattern\";\n        }\n\n        leaf restricted-length-string-with-range {\n            type string {\n                length 10..max;\n            }\n            description\n                \"A test leaf that has a restricted\n                length that is specified as a range\";\n        }\n\n        leaf restricted-length-string-range-two {\n            type string {\n                length 10..20;\n            }\n            description\n                \"A test leaf that has a restricted\n                length that is a range but not max\";\n        }\n\n        leaf stringLeafWithComplexLength {\n            type string {\n                length \"1 .. 20 | 30 .. 40\";\n            }\n        }\n\n        leaf stringLeafWithPatternWithDollar {\n            type string {\n                pattern \"fi$h.*\";\n            }\n        }\n\n        leaf dollarAtEnd {\n            type string {\n                pattern \"fi$h$\";\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "tests/submodules/__init__.py",
    "content": ""
  },
  {
    "path": "tests/submodules/mod-a.yang",
    "content": "module mod-a {\n  namespace \"http://example.com/a\";\n  prefix a;\n  include subm-b;\n\n  container a {\n    uses TEST;\n  }\n\n  container q {\n    leaf idref {\n      type identityref {\n        base i;\n      }\n    }\n  }\n}"
  },
  {
    "path": "tests/submodules/mod-c.yang",
    "content": "module mod-c {\n  namespace \"http://example.com/c\";\n  prefix \"c\";\n\n  typedef t {\n    type string;\n  }\n}"
  },
  {
    "path": "tests/submodules/run.py",
    "content": "#!/usr/bin/env python\n\nimport unittest\n\nfrom tests.base import PyangBindTestCase\n\n\nclass PyangbindSubmoduleTests(PyangBindTestCase):\n    yang_files = [\"mod-a.yang\"]\n    pyang_flags = [\"--use-extmethods\"]\n\n    def setUp(self):\n        self.mod_a = self.bindings.mod_a()\n\n    def test_001_check_correct_import(self):\n        self.assertTrue(hasattr(self.mod_a, \"a\"))\n        self.assertTrue(hasattr(self.mod_a.a, \"b\"))\n\n    def test_002_identity_in_submodule(self):\n        self.assertTrue(hasattr(self.mod_a, \"q\"))\n        self.assertTrue(hasattr(self.mod_a.q, \"idref\"))\n\n    def test_assign_idref(self):\n        passed = True\n        try:\n            self.mod_a.q.idref = \"j\"\n        except ValueError:\n            passed = False\n\n        self.assertTrue(passed)\n\n\nif __name__ == \"__main__\":\n    unittest.main()\n"
  },
  {
    "path": "tests/submodules/subm-b.yang",
    "content": "submodule subm-b {\n  belongs-to mod-a { prefix a; }\n  import mod-c { prefix \"c\"; }\n\n  grouping TEST {\n    leaf b {\n      type c:t;\n    }\n  }\n\n  identity i { }\n  identity j { base i; }\n\n}"
  },
  {
    "path": "tests/typedef/__init__.py",
    "content": ""
  },
  {
    "path": "tests/typedef/remote.yang",
    "content": "module remote {\n    yang-version \"1\";\n    namespace \"http://rob.sh/yang/test/typedef/remote\";\n    prefix \"remote\";\n\n    import second-remote { prefix sr; }\n\n    organization \"BugReports Inc\";\n    contact \"A bug reporter\";\n\n    description\n        \"A test module\";\n    revision 2014-01-01 {\n        description \"april-fools\";\n        reference \"fooled-you\";\n    }\n\n    typedef remote-definition {\n        type string;\n    }\n\n    typedef remote-local-definition {\n        type local-definition;\n    }\n\n    typedef local-definition {\n        type string;\n    }\n\n    typedef hybrid-definition {\n        type sr:second-remote-definition;\n    }\n}\n"
  },
  {
    "path": "tests/typedef/run.py",
    "content": "#!/usr/bin/env python\n\nimport unittest\n\nfrom tests.base import PyangBindTestCase\n\n\nclass TypedefTests(PyangBindTestCase):\n    yang_files = [\"typedef.yang\"]\n\n    def setUp(self):\n        self.typedef = self.bindings.typedef()\n\n    def test_types(self):\n        for element in [\n            \"string\",\n            \"integer\",\n            \"stringdefault\",\n            \"integerdefault\",\n            \"new_string\",\n            \"remote_new_type\",\n            \"session_dir\",\n            \"remote_local_type\",\n        ]:\n            with self.subTest(element=element):\n                self.assertTrue(\n                    hasattr(self.typedef.container, element), \"element %s did not exist within the container\" % element\n                )\n\n    def test_string_container(self):\n        self.typedef.container.string = \"hello\"\n        self.assertEqual(\n            self.typedef.container.string,\n            \"hello\",\n            \"incorrect value set for the strong container (value: %s)\" % self.typedef.container.string,\n        )\n\n    def test_string_default(self):\n        self.assertEqual(\n            self.typedef.container.stringdefault._default,\n            \"aDefaultValue\",\n            \"incorrect default value for derived string type with a default (value: %s)\"\n            % self.typedef.container.stringdefault._default,\n        )\n\n    def test_string_default_from_typedef(self):\n        self.assertEqual(\n            self.typedef.container.new_string._default,\n            \"defaultValue\",\n            \"incorrect default value where derived from typedef (value: %s)\"\n            % self.typedef.container.new_string._default,\n        )\n\n    def test_int_value_can_be_updated(self):\n        self.typedef.container.integer = 1\n        self.assertEqual(self.typedef.container.integer, 1, \"integer value not correctly updated\")\n\n    def test_int_value_range_restriction(self):\n        with self.assertRaises(ValueError, msg=\"restricted int from typedef was set to invalue value\"):\n            self.typedef.container.integer = 65\n\n    def test_remote_definition(self):\n        self.typedef.container.remote_new_type = \"testString\"\n        self.assertEqual(\n            self.typedef.container.remote_new_type,\n            \"testString\",\n            \"incorrect value for the remote definition (%s)\" % self.typedef.container.remote_new_type,\n        )\n\n    def test_remote_local_definition(self):\n        self.typedef.container.remote_local_type = \"testString\"\n        self.assertEqual(\n            self.typedef.container.remote_local_type,\n            \"testString\",\n            \"incorrect value for remote definition which had local definition (%s)\"\n            % self.typedef.container.remote_local_type,\n        )\n\n    def test_inherited_patterns(self):\n        for pattern in [(\"aardvark\", True), (\"ant\", False), (\"duck\", False)]:\n            with self.subTest(pattern=pattern):\n                wset = True\n                try:\n                    self.typedef.container.inheritance = pattern[0]\n                except ValueError:\n                    wset = False\n                self.assertEqual(\n                    wset,\n                    pattern[1],\n                    \"inherited pattern was not correctly followed for %s (%s != %s)\" % (pattern[0], pattern[1], wset),\n                )\n\n    def test_inherited_range(self):\n        for item in [(2, True), (10, False), (1, False)]:\n            with self.subTest(item=item):\n                wset = True\n                try:\n                    self.typedef.container.int_inheritance = item[0]\n                except ValueError:\n                    wset = False\n                self.assertEqual(\n                    wset,\n                    item[1],\n                    \"inherited range was not correctly followed for %s (%s != %s)\" % (item[0], item[1], wset),\n                )\n\n    def test_stacked_union(self):\n        for item in [(\"aardvark\", True), (\"bear\", True), (\"chicken\", False), (\"deer\", False), (\"zebra\", True)]:\n            with self.subTest(item=item):\n                wset = True\n                try:\n                    self.typedef.container.stacked_union.append(item[0])\n                except ValueError:\n                    wset = False\n                self.assertEqual(\n                    wset,\n                    item[1],\n                    \"incorrectly dealt with %s when added as a list key (%s != %s)\" % (item[0], wset, item[1]),\n                )\n\n    def test_hybrid_typedef_across_modules(self):\n        for item in [(\"zebra\", True), (\"yak\", False)]:\n            with self.subTest(item=item):\n                wset = True\n                try:\n                    self.typedef.container.include_of_include_definition = item[0]\n                except ValueError:\n                    wset = False\n                self.assertEqual(\n                    wset,\n                    item[1],\n                    \"definition with hybrid typedef across two modules was not set correctly for %s (%s != %s)\"\n                    % (item[0], item[1], wset),\n                )\n\n    def test_identity_reference(self):\n        for item in [(\"IDONE\", True), (\"IDTWO\", True), (\"IDTHREE\", False)]:\n            with self.subTest(item=item):\n                wset = True\n                try:\n                    self.typedef.container.identity_one_typedef = item[0]\n                except ValueError:\n                    wset = False\n                self.assertEqual(\n                    wset,\n                    item[1],\n                    \"definition with a typedef which references an identity was not set correctly for %s (%s != %s)\"\n                    % (item[0], item[1], wset),\n                )\n\n    def test_union_with_union(self):\n        for item in [(\"aardvark\", True), (\"bear\", True), (\"chicken\", False), (\"quail\", True), (\"zebra\", False)]:\n            with self.subTest(item=item):\n                wset = True\n                try:\n                    self.typedef.container.union_with_union = item[0]\n                except ValueError:\n                    wset = False\n                self.assertEqual(\n                    wset,\n                    item[1],\n                    \"definition which was a union including a typedef was not set correctly for %s (%s != %s)\"\n                    % (item[0], item[1], wset),\n                )\n\n    def test_scoped_leaf(self):\n        self.typedef.container.scoped_leaf = \"aardwolf\"\n        self.assertEqual(\n            self.typedef.container.scoped_leaf,\n            \"aardwolf\",\n            \"scoped leaf was not set correctly (%s)\" % self.typedef.container.scoped_leaf,\n        )\n\n    def test_union_with_identityref(self):\n        for item in [(\"IDONE\", True), (42, True), (-127, False), (\"badstr\", False)]:\n            with self.subTest(item=item):\n                wset = True\n                try:\n                    self.typedef.container.union_idref = item[0]\n                except ValueError:\n                    wset = False\n                self.assertEqual(\n                    wset,\n                    item[1],\n                    \"union with an identityref within it was not set correctly: %s != %s (%s)\"\n                    % (wset, item[1], item[0]),\n                )\n\n    def test_nested_typedefs(self):\n        self.typedef.scoped_container_typedef.two = \"amber\"\n        self.assertEqual(\n            self.typedef.scoped_container_typedef.two,\n            \"amber\",\n            \"scoped typedef leaf within a container not set correctly\",\n        )\n\n\nif __name__ == \"__main__\":\n    unittest.main()\n"
  },
  {
    "path": "tests/typedef/second-remote.yang",
    "content": "module second-remote {\n    yang-version \"1\";\n    namespace \"http://rob.sh/yang/test/typedef/second-remote\";\n    prefix \"second-remote\";\n\n    organization \"BugReports Inc\";\n    contact \"A bug reporter\";\n\n    description\n        \"A test module\";\n    revision 2014-01-01 {\n        description \"april-fools\";\n        reference \"fooled-you\";\n    }\n\n    typedef second-remote-definition {\n        type string {\n            pattern \"z.*\";\n        }\n    }\n}\n"
  },
  {
    "path": "tests/typedef/typedef.yang",
    "content": "module typedef {\n    yang-version \"1.1\";\n    namespace \"http://rob.sh/yang/test/list\";\n    prefix \"foo\";\n\n    import remote { prefix defn; }\n\n    organization \"BugReports Inc\";\n    contact \"A bug reporter\";\n\n    description\n        \"A test module\";\n    revision 2014-01-01 {\n        description \"april-fools\";\n        reference \"fooled-you\";\n    }\n\n    typedef derived-string-type {\n        type string;\n    }\n\n    typedef restricted-integer-type {\n        type uint16 {\n            range 0..64;\n        }\n    }\n\n    typedef bgp-session-direction {\n        type enumeration {\n            enum INBOUND;\n            enum OUTBOUND;\n        }\n    }\n\n    typedef new-string-type {\n        type string;\n        default \"defaultValue\";\n    }\n\n    typedef restricted-inherit {\n        type string {\n            pattern \"^a.*\";\n        }\n    }\n\n    typedef restricted-int-inherit {\n        type int8 {\n            range 0..100;\n        }\n    }\n\n    typedef parent-union {\n        type union {\n            type string {\n                pattern \"a.*\";\n            }\n            type string {\n                pattern \"b.*\";\n            }\n        }\n    }\n\n    typedef child-union {\n        type union {\n            type parent-union;\n            type string {\n                pattern \"z.*\";\n            }\n        }\n    }\n\n    typedef union-included {\n        type union {\n            type string {\n                pattern \"a.*\";\n            }\n            type string {\n                pattern \"b.*\";\n            }\n        }\n    }\n\n    identity identity_base;\n    identity IDONE {\n        base \"identity_base\";\n    }\n\n    identity IDTWO {\n        base \"identity_base\";\n    }\n\n    typedef identity_one {\n        type identityref {\n            base identity_base;\n        }\n    }\n\n    typedef union-with-idref {\n        type union {\n            type identityref {\n                base identity_base;\n            }\n            type uint8;\n        }\n    }\n\n    typedef referenced-leaf {\n        type leafref {\n            path \"/container/target\";\n            require-instance false;\n        }\n    }\n\n    grouping scoped-typedef {\n        typedef scoped-type {\n            type string {\n                pattern \"a.*\";\n            }\n        }\n\n        leaf scoped-leaf {\n            type scoped-type;\n        }\n    }\n\n    container container {\n        description\n            \"A container\";\n\n        leaf-list target {\n            type string;\n            description\n                \"A target leaf for leafref checks\";\n        }\n\n        leaf string {\n            type derived-string-type;\n        }\n\n        leaf integer {\n            type restricted-integer-type;\n        }\n\n        leaf stringdefault {\n            type derived-string-type;\n            default \"aDefaultValue\";\n        }\n\n        leaf integerdefault {\n            type restricted-integer-type;\n            default 10;\n        }\n\n        leaf new-string {\n            type new-string-type;\n        }\n\n        leaf remote-new-type {\n            type defn:remote-definition;\n        }\n\n        leaf session-dir {\n            type bgp-session-direction;\n        }\n\n        leaf remote-local-type {\n            type defn:remote-local-definition;\n        }\n\n        leaf inheritance {\n            type restricted-inherit {\n                pattern \".*k\";\n            }\n        }\n\n        leaf int-inheritance {\n            type restricted-int-inherit {\n                range 2..5;\n            }\n        }\n\n        leaf-list stacked-union {\n            type child-union;\n        }\n\n        leaf include-of-include-definition {\n            type defn:hybrid-definition;\n        }\n\n        leaf identity-one-typedef {\n            type identity_one;\n        }\n\n        leaf union-with-union {\n            type union {\n                type union-included;\n                type string {\n                    pattern \"q.*\";\n                }\n            }\n        }\n\n        leaf reference {\n            type referenced-leaf;\n        }\n\n        leaf union-idref {\n            type union-with-idref;\n        }\n\n        uses scoped-typedef;\n    }\n\n    container scoped-container-typedef {\n        typedef ATEST {\n            type string {\n                pattern \"a.*\";\n            }\n        }\n\n        leaf two {\n            type ATEST;\n        }\n    }\n}\n"
  },
  {
    "path": "tests/uint/__init__.py",
    "content": ""
  },
  {
    "path": "tests/uint/run.py",
    "content": "#!/usr/bin/env python\n\nfrom __future__ import unicode_literals\n\nimport unittest\n\nfrom tests.base import PyangBindTestCase\n\n\nclass UIntTests(PyangBindTestCase):\n    yang_files = [\"uint.yang\"]\n\n    def setUp(self):\n        self.instance = self.bindings.uint()\n\n    def test_uint_maximum_default_values(self):\n        for size, value in [\n            (\"eight\", 2**8 - 1),\n            (\"sixteen\", 2**16 - 1),\n            (\"thirtytwo\", 2**32 - 1),\n            (\"sixtyfour\", 2**64 - 1),\n        ]:\n            with self.subTest(size=size, value=value):\n                default = getattr(self.instance.uint_container, \"%sdefault\" % size)._default\n                self.assertEqual(default, value)\n\n    def test_set_uint_values(self):\n        for leaf in [\"eight\", \"sixteen\", \"thirtytwo\", \"sixtyfour\"]:\n            with self.subTest(leaf=leaf):\n                setattr(self.instance.uint_container, leaf, 42)\n                value = getattr(self.instance.uint_container, leaf)\n                self.assertEqual(value, 42)\n\n    def test_set_uint_values_marks_changes(self):\n        for leaf in [\"eight\", \"sixteen\", \"thirtytwo\", \"sixtyfour\"]:\n            with self.subTest(leaf=leaf):\n                setattr(self.instance.uint_container, leaf, 42)\n                leaf_ref = getattr(self.instance.uint_container, leaf)\n                self.assertTrue(leaf_ref._changed())\n\n    def test_uint8_with_restricted_range(self):\n        for value, valid in [(0, False), (7, True), (11, False)]:\n            with self.subTest(value=value, valid=valid):\n                allowed = True\n                try:\n                    self.instance.uint_container.eightrestricted = value\n                except ValueError:\n                    allowed = False\n                self.assertEqual(allowed, valid)\n\n    def test_uint16_with_restricted_range(self):\n        for value, valid in [(99, False), (123, True), (1001, False)]:\n            with self.subTest(value=value, valid=valid):\n                allowed = True\n                try:\n                    self.instance.uint_container.sixteenrestricted = value\n                except ValueError:\n                    allowed = False\n                self.assertEqual(allowed, valid)\n\n    def test_uint32_with_restricted_range(self):\n        for value, valid in [(9999, False), (424242, True), (500001, False)]:\n            with self.subTest(value=value, valid=valid):\n                allowed = True\n                try:\n                    self.instance.uint_container.thirtytworestricted = value\n                except ValueError:\n                    allowed = False\n                self.assertEqual(allowed, valid)\n\n    def test_uint64_with_restricted_range(self):\n        for value, valid in [(799, False), (42424242, True), (18446744073709551615, False)]:\n            with self.subTest(value=value, valid=valid):\n                allowed = True\n                try:\n                    self.instance.uint_container.sixtyfourrestricted = value\n                except ValueError:\n                    allowed = False\n                self.assertEqual(allowed, valid)\n\n    def test_additional_uint32_range(self):\n        for value, valid in [(0, True), (10, True), (2**32 - 1, True), (2**64, False)]:\n            with self.subTest(value=value, valid=valid):\n                allowed = True\n                try:\n                    self.instance.issue_fixes.region_id = value\n                except ValueError:\n                    allowed = False\n                self.assertEqual(allowed, valid)\n\n    def test_set_uint_values_to_zero(self):\n        for size in [\"eight\", \"sixteen\", \"thirtytwo\", \"sixtyfour\"]:\n            with self.subTest(size=size):\n                setter = getattr(self.instance.uint_container, \"_set_%s\" % size)\n                allowed = True\n                try:\n                    setter(0)\n                except ValueError:\n                    allowed = False\n                self.assertTrue(allowed)\n\n    def test_set_uint_values_below_zero(self):\n        for size in [\"eight\", \"sixteen\", \"thirtytwo\", \"sixtyfour\"]:\n            with self.subTest(size=size):\n                setter = getattr(self.instance.uint_container, \"_set_%s\" % size)\n                with self.assertRaises(ValueError):\n                    setter(-1)\n\n    def test_set_uint_values_above_upper_bounds(self):\n        bounds = {\"eight\": 2**8, \"sixteen\": 2**16, \"thirtytwo\": 2**32, \"sixtyfour\": 2**64}\n        for size, value in bounds.items():\n            with self.subTest(size=size, value=value), self.assertRaises(ValueError):\n                setter = getattr(self.instance.uint_container, \"_set_%s\" % size)\n                setter(value)\n\n\nif __name__ == \"__main__\":\n    unittest.main()\n"
  },
  {
    "path": "tests/uint/uint.yang",
    "content": "module uint {\n    yang-version \"1\";\n    namespace \"http://rob.sh/yang/test/uint\";\n    prefix \"foo\";\n    organization \"BugReports Inc\";\n    contact \"A bug reporter\";\n\n    description\n        \"A test module\";\n    revision 2014-01-01 {\n        description \"april-fools\";\n        reference \"fooled-you\";\n    }\n\n\n    container issue-fixes {\n      leaf region-id {\n        // match attribute mode=type: name=regionId type=long\n        type uint32 {\n          // match constraint type=range: # values=1\n          range \"0..4294967295\";\n        }\n        default \"0\";\n      } // leaf region-id\n    }\n\n    container uint-container {\n        description\n            \"A container\";\n\n        leaf eight {\n            type uint8;\n            description\n              \"A test leaf for uint8\";\n        }\n\n        leaf eightdefault {\n            type uint8;\n            default 255;\n            description\n              \"A test leaf for uint8 with a default\";\n        }\n\n        leaf eightresult {\n            type uint8;\n            description\n              \"A test leaf that stores results of operations\";\n        }\n\n        leaf sixteen {\n            type uint16;\n            description\n              \"A test leaf for uint16\";\n        }\n\n        leaf sixteendefault {\n            type uint16;\n            default 65535;\n            description\n              \"A test leaf for uint16 with a default\";\n        }\n\n        leaf sixteenresult {\n            type uint16;\n            description\n              \"A test leaf that stores results of operations\";\n        }\n\n        leaf thirtytwo {\n            type uint32;\n            description\n              \"A test leaf for uint32\";\n        }\n\n        leaf thirtytwodefault {\n            type uint32;\n            default 4294967295;\n            description\n              \"A test leaf for uint32 with a default\";\n        }\n\n        leaf thirtytworesult {\n            type uint32;\n            description\n              \"A test leaf that stores results of operations\";\n        }\n\n        leaf sixtyfour {\n            type uint64;\n            description\n              \"A test leaf for uint16\";\n        }\n\n        leaf sixtyfourdefault {\n            type uint64;\n            default 18446744073709551615;\n            description\n              \"A test leaf for uint16 with a default\";\n        }\n\n        leaf sixtyfourresult {\n            type uint64;\n            description\n              \"A test leaf that stores results of operations\";\n        }\n\n        leaf eightrestricted {\n            type uint8 {\n                range 1..10;\n            }\n            description\n              \"A test uint8 that has restricted range\";\n        }\n\n        leaf sixteenrestricted {\n            type uint16 {\n                range 100..1000;\n            }\n            description\n              \"A test uint16 that has a restricted range\";\n        }\n\n        leaf thirtytworestricted {\n            type uint32 {\n                range 10000..500000;\n            }\n            description\n              \"A test uint32 that has a restricted range\";\n        }\n\n        leaf sixtyfourrestricted {\n            type uint64 {\n                range 800..173709551615;\n            }\n            description\n              \"A test uint32 that has a restricted range\";\n        }\n    }\n}\n"
  },
  {
    "path": "tests/union/__init__.py",
    "content": ""
  },
  {
    "path": "tests/union/run.py",
    "content": "#!/usr/bin/env python\n\nfrom __future__ import unicode_literals\n\nimport unittest\n\nfrom tests.base import PyangBindTestCase\n\n\nclass UnionTests(PyangBindTestCase):\n    yang_files = [\"union.yang\"]\n\n    def setUp(self):\n        self.instance = self.bindings.union()\n\n    # u1: precedence of int over string\n    def test_union_of_int_over_string_allows_math_on_integer_value(self):\n        self.instance.container.u1 = 2\n        self.instance.container.u1 += 1\n        self.assertEqual(self.instance.container.u1, 3)\n\n    def test_set_union_of_int_over_string_to_string_after_int(self):\n        self.instance.container.u1 = 3\n        self.instance.container.u1 = \"aStringTest\"\n        self.assertEqual(self.instance.container.u1, \"aStringTest\")\n\n    def test_union_of_int_over_string_allows_concatenation_to_string_value(self):\n        self.instance.container.u1 = \"aStringTest\"\n        self.instance.container.u1 += \"A\"\n        self.assertEqual(self.instance.container.u1, \"aStringTestA\")\n\n    # u2: precedence of string over int, with a default set\n    def test_union_of_string_over_int_with_default_is_empty_string(self):\n        self.assertEqual(self.instance.container.u2, \"\")\n\n    def test_default_value_of_string_over_int_with_default(self):\n        self.assertEqual(self.instance.container.u2._default, \"set from u2\")\n\n    def test_union_of_string_over_int_performs_string_concatenation(self):\n        self.instance.container.u2 = 2\n        self.instance.container.u2 += \"A\"\n        self.assertEqual(self.instance.container.u2, \"2A\")\n\n    # u3: union of int with precendence over string, but default is a string\n    def test_default_value_of_int_over_string_is_zero(self):\n        self.assertEqual(self.instance.container.u3, 0)\n\n    def test_default_value_of_int_over_string_with_default_string(self):\n        self.assertEqual(self.instance.container.u3._default, \"set from u3\")\n\n    def test_union_of_int_over_string_with_int_default_is_int_type(self):\n        self.assertIsInstance(self.instance.container.u4._default, int)\n\n    def test_default_value_gets_set_from_typedef(self):\n        self.assertIsInstance(self.instance.container.u6._default, str)\n\n    def test_set_typedef_union_of_int_over_string_to_a_string_value(self):\n        self.instance.container.u7 = \"hello\"\n        self.assertEqual(self.instance.container.u7, \"hello\")\n\n    def test_set_typedef_union_of_int_over_string_to_int_value_after_string(self):\n        self.instance.container.u7 = \"hello\"\n        self.instance.container.u7 = 10\n        self.assertEqual(self.instance.container.u7, 10)\n\n    def test_default_value_of_int_typedef_within_union_typedef(self):\n        self.assertEqual(self.instance.container.u8._default, 10)\n\n    def test_leaf_list_with_union_of_unions_from_typedefs(self):\n        for value, valid in [(1, True), (\"hello\", True), (42.42, True), (True, True), (b\"yang\", False)]:\n            with self.subTest(value=value, valid=valid):\n                allowed = True\n                try:\n                    self.instance.container.u9.append(value)\n                except ValueError:\n                    allowed = False\n                self.assertEqual(allowed, valid)\n\n    def test_leaf_list_with_union_of_unions_from_typedefs_with_restricted_types(self):\n        for value, valid in [\n            (15, True),\n            (35, True),\n            (\"aardvark\", True),\n            (\"bear\", True),\n            (21, False),\n            (42, False),\n            (\"cat\", False),\n            (\"fish\", False),\n        ]:\n            with self.subTest(value=value, valid=valid):\n                allowed = True\n                try:\n                    self.instance.container.u10.append(value)\n                except ValueError:\n                    allowed = False\n                self.assertEqual(allowed, valid)\n\n    def test_union_of_unions_from_typedefs_with_local_default_gets_proper_default(self):\n        self.assertIsInstance(self.instance.container.u11._default, str)\n\n    def test_union_of_restricted_class_types(self):\n        for value, valid in [(\"unlimited\", True), (1, True), (0, True), (\"fish\", False), (2**64, False)]:\n            with self.subTest(value=value, valid=valid):\n                allowed = True\n                try:\n                    self.instance.container.u12 = value\n                except ValueError:\n                    allowed = False\n                self.assertEqual(allowed, valid)\n\n\nif __name__ == \"__main__\":\n    unittest.main()\n"
  },
  {
    "path": "tests/union/union.yang",
    "content": "module union {\n    yang-version \"1.1\";\n    namespace \"http://rob.sh/yang/test/union\";\n    prefix \"foo\";\n    organization \"BugReports Inc\";\n    contact \"A bug reporter\";\n\n    description\n        \"A test module\";\n    revision 2014-01-01 {\n        description \"april-fools\";\n        reference \"fooled-you\";\n    }\n\n    typedef derived-union-type {\n        type union {\n            type int8;\n            type string;\n        }\n    }\n\n    typedef derived-union-default {\n        type union {\n            type derived-string-type;\n            type int8;\n        }\n    }\n\n    typedef derived-string-type {\n        type string;\n        default \"hello\";\n    }\n\n    typedef derived-int-type {\n        type int8;\n        default 10;\n    }\n\n    typedef derived-union-int-default {\n        type union {\n            type derived-int-type;\n            type string;\n        }\n    }\n\n    typedef union-a {\n        type union {\n            type int8;\n            type string {\n                pattern \"h.*\";\n            }\n        }\n    }\n\n    typedef union-b {\n        type union {\n            type decimal64 {\n                fraction-digits 2;\n            }\n            type boolean;\n        }\n    }\n\n    typedef union-c {\n        type union {\n            type uint16 {\n                range 10..20;\n            }\n            type string {\n                pattern \"a.*\";\n            }\n        }\n    }\n\n    typedef union-d {\n        type union {\n            type uint16 {\n                range 30..40;\n            }\n            type string {\n                pattern \"b.*\";\n            }\n        }\n    }\n\n    typedef second-level-union {\n        type union {\n            type string;\n            type uint32 {\n                range 10..20;\n            }\n        }\n    }\n\n    typedef top-level-union {\n        type union {\n            type second-level-union;\n            type uint32 {\n                range 100..200;\n            }\n        }\n    }\n\n    typedef union-with-default {\n        type union {\n            type string;\n            type uint64;\n        }\n        default \"0\";\n    }\n\n    container container {\n\n        leaf u2 {\n            type union {\n                type string;\n                type int8;\n            }\n            default \"set from u2\";\n            description\n                \"A test leaf with a default\";\n        }\n\n        leaf u3 {\n            type union {\n                type int8;\n                type string;\n            }\n            default \"set from u3\";\n            description\n                \"a test leaf with a default that must skip a type\";\n        }\n\n        leaf u4 {\n            type union {\n                type int8;\n                type string;\n            }\n            default 1;\n            description\n                \"a test leaf that does not need to skip but is an int\";\n        }\n\n        leaf u8 {\n            type derived-union-int-default;\n            description\n                \"a test leaf with a typedef within a union\";\n        }\n\n        leaf u1 {\n            type union {\n                type int8;\n                type string;\n            }\n            description\n                \"A test leaf\";\n        }\n\n        leaf u6 {\n            type union {\n                type uint8;\n                type derived-string-type;\n            }\n        }\n\n        leaf u7 {\n            type derived-union-type;\n        }\n\n        leaf-list u9 {\n            type union {\n                type union-a;\n                type union-b;\n            }\n        }\n\n        leaf-list u10 {\n            type union {\n                type union-c;\n                type union-d;\n            }\n        }\n\n        leaf u11 {\n            type top-level-union;\n            default \"string\";\n        }\n\n        // issue reported in pull req #48\n        leaf u12 {\n            type union {\n                type enumeration {\n                    enum unlimited;\n                }\n                type int16 {\n                    range \"0..max\";\n                }\n            }\n        }\n\n        // issue reported in pull req #318\n        leaf u13 {\n            type union-with-default;\n        }\n    }\n}\n"
  },
  {
    "path": "tests/unit/__init__.py",
    "content": ""
  },
  {
    "path": "tests/unit/models/simple.yang",
    "content": "module simple {\n    yang-version \"1\";\n    namespace \"http://rob.sh/yang/test/simple\";\n    prefix \"foo\";\n    organization \"BugReports Inc\";\n    contact \"A bug reporter\";\n\n    description\n        \"A test module\";\n    revision 2014-01-01 {\n        description \"april-fools\";\n        reference \"fooled-you\";\n    }\n\n    container test_leaf_changed {\n        leaf a1 {\n            type string;\n            default \"default\";\n            description\n                \"A test leaf\";\n        }\n    }\n}\n"
  },
  {
    "path": "tests/unit/test_api.py",
    "content": "from tests.base import PyangBindTestCase\n\n\nclass APITests(PyangBindTestCase):\n    yang_files = [\"models/simple.yang\"]\n\n    def setUp(self):\n        self.simple_unchanged = self.bindings.simple()\n        self.simple_changed_defaults = self.bindings.simple()\n        self.simple_changed_non_defaults = self.bindings.simple()\n\n    def test_leaf_unchanged(self):\n        self.assertFalse(self.simple.test_leaf_changed.a1._changed())\n        self.assertFalse(self.simple.test_leaf_changed._changed())\n\n    def test_leaf_changed_with_defaults(self):\n        self.simple_changed_defaults.test_leaf_changed.a1 = self.simple.test_leaf_changed.a1._default\n        self.assertTrue(\n            self.simple_changed_defaults.test_leaf_changed.a1\n            == self.simple_changed_defaults.test_leaf_changed.a1._default\n        )\n        self.assertTrue(self.simple_changed_defaults.test_leaf_changed.a1._changed())\n        self.assertTrue(self.simple_changed_defaults.test_leaf_changed._changed())\n\n    def test_leaf_changed_with_non_defaults(self):\n        self.simple_changed_non_defaults.test_leaf_changed.a1 = \"test\"\n        self.assertTrue(self.simple_changed_non_defaults.test_leaf_changed.a1 == \"test\")\n        self.assertTrue(self.simple_changed_non_defaults.test_leaf_changed.a1._changed())\n        self.assertTrue(self.simple_changed_non_defaults.test_leaf_changed._changed())\n"
  },
  {
    "path": "tests/xpath/00_pathhelper_base.py",
    "content": "#!/usr/bin/env python\n\nfrom __future__ import print_function\n\nfrom pyangbind.lib.xpathhelper import XPathError, YANGPathHelper\n\nimport unittest\n\n\nclass TestObject(object):\n    def __init__(self, name):\n        self._name = name\n\n    def name(self):\n        return self._name\n\n\nclass PathHelperBaseTests(unittest.TestCase):\n    def setUp(self):\n        self.tree = YANGPathHelper()\n\n    def test_get_returns_same_number_of_objects_as_registered(self):\n        obj = TestObject(\"testobj\")\n        self.tree.register([\"obj_one\"], obj)\n        self.assertEqual(len(self.tree.get([\"obj_one\"])), 1)\n\n    def test_get_returns_objects_of_same_class_as_registered(self):\n        obj = TestObject(\"testobj\")\n        self.tree.register([\"obj_one\"], obj)\n        self.assertIsInstance(self.tree.get([\"obj_one\"])[0], TestObject)\n\n    def test_get_returns_objects_with_same_attributes_as_registered(self):\n        obj = TestObject(\"testobj\")\n        self.tree.register([\"obj_one\"], obj)\n        self.assertEqual(self.tree.get([\"obj_one\"])[0].name(), \"testobj\")\n\n    def test_get_non_existent_path_returns_nothing(self):\n        self.assertEqual(len(self.tree.get(\"/a/non-existent/path\")), 0)\n\n    def test_register_invalid_path_raises_exception(self):\n        with self.assertRaises(XPathError):\n            self.tree.register(\"an-invalid-path-name\", TestObject(\"invalid\"))\n\n    def test_retrieve_object_at_bottom_of_hierarchy_returns_single_object(self):\n        self.tree.register([\"node0\"], TestObject(0))\n        self.tree.register([\"node0\", \"node1\"], TestObject(1))\n        self.tree.register([\"node0\", \"node1\", \"node2\"], TestObject(2))\n        self.assertEqual(len(self.tree.get(\"/node0/node1/node2\")), 1)\n\n    def test_retrieve_object_at_bottom_of_hierarchy_has_proper_name(self):\n        self.tree.register([\"node0\"], TestObject(0))\n        self.tree.register([\"node0\", \"node1\"], TestObject(1))\n        self.tree.register([\"node0\", \"node1\", \"node2\"], TestObject(2))\n        self.assertEqual(self.tree.get(\"/node0/node1/node2\")[0].name(), 2)\n\n    def test_register_object_with_attribute(self):\n        self.tree.register([\"container\"], TestObject(\"container\"))\n        allowed = True\n        try:\n            self.tree.register([\"container\", \"foo[id=0]\"], TestObject(\"bar\"))\n        except Exception:\n            allowed = False\n        self.assertTrue(allowed)\n\n    def test_retrieve_object_by_attribute_returns_single_object(self):\n        self.tree.register([\"container\"], TestObject(\"container\"))\n        self.tree.register([\"container\", \"foo[id=0]\"], TestObject(\"bar0\"))\n        self.tree.register([\"container\", \"foo[id=1]\"], TestObject(\"bar1\"))\n        self.assertEqual(len(self.tree.get(\"/container/foo[id=0]\")), 1)\n\n    def test_get_object_by_attribute_returns_object_of_same_class(self):\n        self.tree.register([\"container\"], TestObject(\"container\"))\n        self.tree.register([\"container\", \"foo[id=0]\"], TestObject(\"bar0\"))\n        self.assertIsInstance(self.tree.get(\"/container/foo[id=0]\")[0], TestObject)\n\n    def test_register_object_with_attribute_various_quoting_styles(self):\n        self.tree.register([\"container\"], TestObject(\"container\"))\n        for style in ['\"', \"'\", \"\"]:\n            with self.subTest(style=style):\n                allowed = True\n                try:\n                    self.tree.register([\"container\", \"foo[id={0}42{0}]\".format(style)], TestObject(42))\n                except Exception:\n                    allowed = False\n                self.assertTrue(allowed)\n\n    def test_get_object_with_attribute_various_quoting_styles(self):\n        self.tree.register([\"container\"], TestObject(\"container\"))\n        self.tree.register([\"container\", \"foo[id=42]\"], TestObject(\"bar42\"))\n        for style in ['\"', \"'\", \"\"]:\n            with self.subTest(style=style):\n                obj = self.tree.get(\"/container/foo[id={0}42{0}]\".format(style))[0]\n                self.assertEqual(obj.name(), \"bar42\")\n\n\nif __name__ == \"__main__\":\n    unittest.main()\n"
  },
  {
    "path": "tests/xpath/01-list_leaflist/__init__.py",
    "content": ""
  },
  {
    "path": "tests/xpath/01-list_leaflist/list-tc01.yang",
    "content": "module list-tc01 {\n    yang-version \"1.1\";\n    namespace \"http://rob.sh/yang/xpathelper/tc01\";\n    prefix \"tc01\";\n    organization \"BugReports Inc\";\n    contact \"A bug reporter\";\n\n    description\n        \"A test module\";\n    revision 2014-01-01 {\n        description \"april-fools\";\n        reference \"fooled-you\";\n    }\n\n    typedef derived-leafref-t5 {\n        type leafref {\n            path \"/container/t5\";\n            require-instance true;\n        }\n    }\n\n    typedef derived-leafref-t6 {\n        type leafref {\n            path \"/container/t6/keyval\";\n            require-instance true;\n        }\n    }\n\n    container container {\n        leaf-list t1 {\n            type string;\n        }\n\n        list t2 {\n            key \"keyval\";\n\n            leaf keyval {\n                type string;\n            }\n        }\n\n        leaf-list t3 {\n            type string;\n        }\n\n        list t4 {\n            key \"keyval\";\n            leaf keyval {\n                type string;\n            }\n        }\n\n        leaf-list t5 {\n            type string;\n        }\n\n        list t6 {\n            key \"keyval\";\n            leaf keyval {\n                type string;\n            }\n        }\n\n        leaf-list t7 {\n            type string;\n        }\n    }\n\n    container reference {\n        leaf t1-ptr {\n            type leafref {\n                path \"/container/t1\";\n                require-instance true;\n            }\n        }\n\n        leaf t1-ptr-noexist {\n            type leafref {\n                path \"/container/t1\";\n                require-instance false;\n            }\n        }\n\n        leaf t2-ptr {\n            type leafref {\n                path \"/container/t2/keyval\";\n                require-instance true;\n            }\n        }\n\n        leaf t5-ptr {\n            type derived-leafref-t5;\n        }\n\n        leaf t6-ptr {\n            type derived-leafref-t6;\n        }\n\n        leaf-list t7-ptr {\n            type leafref {\n                path \"/container/t7\";\n                require-instance true;\n            }\n        }\n    }\n\n    container standalone {\n        leaf-list ll {\n            type int8;\n        }\n\n        list l {\n            key x;\n            leaf x {\n                type int8;\n            }\n        }\n\n        leaf ref {\n            type leafref {\n                path \"../l/x\";\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "tests/xpath/01-list_leaflist/run.py",
    "content": "#!/usr/bin/env python\n\nimport unittest\n\nfrom pyangbind.lib.xpathhelper import YANGPathHelper\nfrom tests.base import PyangBindTestCase\n\n\nclass XPathListLeaflistTests(PyangBindTestCase):\n    yang_files = [\"list-tc01.yang\"]\n    pyang_flags = [\"--use-xpathhelper\"]\n\n    def setUp(self):\n        self.path_helper = YANGPathHelper()\n        self.instance = self.bindings.list_tc01(path_helper=self.path_helper)\n\n    def test_leaflist_leafref_with_require_instance_true(self):\n        for fish in [\"mackerel\", \"trout\", \"haddock\", \"flounder\"]:\n            self.instance.container.t1.append(fish)\n        for fish, valid in [(\"mackerel\", True), (\"haddock\", True), (\"minnow\", False)]:\n            with self.subTest(fish=fish, valid=valid):\n                allowed = True\n                try:\n                    self.instance.reference.t1_ptr = fish\n                except ValueError:\n                    allowed = False\n                self.assertEqual(valid, allowed)\n\n    def test_leaflist_leafref_with_require_instance_false(self):\n        for fish in [\"mackerel\", \"trout\", \"haddock\", \"flounder\"]:\n            self.instance.container.t1.append(fish)\n        for fish, exists in [(\"flounder\", True), (\"minnow\", False)]:\n            with self.subTest(fish=fish, exists=exists):\n                allowed = True\n                try:\n                    self.instance.reference.t1_ptr_noexist = fish\n                except ValueError:\n                    allowed = False\n                self.assertTrue(allowed)\n\n    def test_list_leafref_with_require_instance_true(self):\n        for animal in [\"kangaroo\", \"wallaby\", \"koala\", \"dingo\"]:\n            self.instance.container.t2.add(animal)\n\n        for animal, valid in [(\"kangaroo\", True), (\"koala\", True), (\"wombat\", False)]:\n            with self.subTest(animal=animal, valid=valid):\n                allowed = True\n                try:\n                    self.instance.reference.t2_ptr = animal\n                except ValueError:\n                    allowed = False\n                self.assertEqual(valid, allowed)\n\n    def test_get_leaflist_with_xpath_helper_returns_single_element(self):\n        for beer in [\"oatmeal-stout\", \"amber-ale\", \"pale-ale\", \"pils\", \"ipa\", \"session-ipa\"]:\n            self.instance.container.t3.append(beer)\n\n        self.assertEqual(len(self.path_helper.get(\"/container/t3\")), 1)\n\n    def test_find_elements_of_leaflist(self):\n        for beer in [\"oatmeal-stout\", \"amber-ale\", \"pale-ale\", \"pils\", \"ipa\", \"session-ipa\"]:\n            self.instance.container.t3.append(beer)\n\n        leaflist = self.path_helper.get(\"/container/t3\")[0]\n        for beer, valid in [(\"session-ipa\", True), (\"amber-ale\", True), (\"moose-drool\", False)]:\n            with self.subTest(beer=beer, valid=valid):\n                found = True\n                try:\n                    leaflist.index(beer)\n                except ValueError:\n                    found = False\n                self.assertEqual(valid, found)\n\n    def test_remove_elements_from_leaflist(self):\n        for beer in [\"oatmeal-stout\", \"amber-ale\", \"pale-ale\", \"pils\", \"ipa\", \"session-ipa\"]:\n            self.instance.container.t3.append(beer)\n\n        for beer, valid in [(\"session-ipa\", True), (\"amber-ale\", True), (\"moose-drool\", False)]:\n            with self.subTest(beer=beer, valid=valid):\n                removed = True\n                try:\n                    self.instance.container.t3.remove(beer)\n                except ValueError:\n                    removed = False\n                self.assertEqual(removed, valid)\n\n    def test_xpath_helper_gets_updated_leaflist_after_removing_items(self):\n        for beer in [\"oatmeal-stout\", \"amber-ale\", \"pale-ale\", \"pils\", \"ipa\", \"session-ipa\"]:\n            self.instance.container.t3.append(beer)\n        retr = self.path_helper.get(\"/container/t3\")  # Retrieve before to get the old value\n\n        for beer in [\"session-ipa\", \"amber-ale\"]:\n            self.instance.container.t3.remove(beer)\n        retr = self.path_helper.get(\"/container/t3\")\n\n        for beer in [\"session-ipa\", \"amber-ale\", \"moose-drool\"]:\n            with self.subTest(beer=beer), self.assertRaises(ValueError):\n                retr[0].index(beer)\n\n    def test_get_list_item_with_xpath_helper_returns_single_element(self):\n        for beer in [\"steam\", \"liberty\", \"california-lager\", \"porter\", \"ipa\", \"foghorn\"]:\n            self.instance.container.t4.add(beer)\n\n        for beer, exists in [(\"steam\", 1), (\"liberty\", 1), (\"pygmy-owl\", 0)]:\n            with self.subTest(beer=beer, exists=exists):\n                retr = self.path_helper.get(\"/container/t4[keyval=%s]\" % beer)\n                self.assertEqual(len(retr), exists)\n\n    def test_remove_elements_from_list(self):\n        for beer in [\"steam\", \"liberty\", \"california-lager\", \"porter\", \"ipa\", \"foghorn\"]:\n            self.instance.container.t4.add(beer)\n\n        for beer, valid in [(\"steam\", True), (\"liberty\", True), (\"pygmy-owl\", False)]:\n            with self.subTest(beer=beer, valid=valid):\n                removed = True\n                try:\n                    self.instance.container.t4.delete(beer)\n                except KeyError:\n                    removed = False\n                self.assertEqual(removed, valid)\n\n    def test_xpath_helper_gets_updated_list_after_removing_items(self):\n        for beer in [\"steam\", \"liberty\", \"california-lager\", \"porter\", \"ipa\", \"foghorn\"]:\n            self.instance.container.t4.add(beer)\n\n        for beer in [\"steam\", \"liberty\", \"pygmy-owl\"]:\n            with self.subTest(beer=beer):\n                path = \"/container/t4[keyval=%s]\"\n                retr = self.path_helper.get(path)\n                try:\n                    self.instance.container.t4.delete(beer)\n                except KeyError:\n                    pass\n                retr = self.path_helper.get(path)\n\n                self.assertEqual(len(retr), 0)\n\n    def test_typedef_leaflist_with_require_instance_true(self):\n        for city in [\"quebec-city\", \"montreal\", \"laval\", \"gatineau\"]:\n            self.instance.container.t5.append(city)\n\n        for city, valid in [(\"quebec-city\", True), (\"montreal\", True), (\"dallas\", False)]:\n            with self.subTest(city=city, valid=valid):\n                allowed = True\n                try:\n                    self.instance.reference.t5_ptr = city\n                except ValueError:\n                    allowed = False\n                self.assertEqual(valid, allowed)\n\n    def test_typedef_list_with_require_instance_true(self):\n        for beer in [\"la-ciboire\", \"la-chipie\", \"la-joufflue\", \"la-matante\"]:\n            self.instance.container.t6.add(beer)\n\n        for beer, valid in [(\"la-ciboire\", True), (\"la-matante\", True), (\"heiniken\", False)]:\n            with self.subTest(beer=beer, valid=valid):\n                allowed = True\n                try:\n                    self.instance.reference.t6_ptr = beer\n                except ValueError:\n                    allowed = False\n                self.assertEqual(valid, allowed)\n\n    def test_leaflist_of_leafrefs_with_require_instance_true(self):\n        for beer in [\"snapshot\", \"ranger\"]:\n            self.instance.container.t7.append(beer)\n\n        for beer, valid in [(\"snapshot\", True), (\"ranger\", True), (\"trout-slayer\", False)]:\n            with self.subTest(beer=beer, valid=valid):\n                allowed = True\n                try:\n                    self.instance.reference.t7_ptr.append(beer)\n                except ValueError:\n                    allowed = False\n                self.assertEqual(valid, allowed)\n\n    def test_standalone_leaflist(self):\n        self.instance.standalone.ll.append(1)\n        retr = self.path_helper.get(\"/standalone/ll\")\n        self.assertEqual(retr[0][0], 1)\n\n    def test_standlone_list(self):\n        self.instance.standalone.l.add(1)\n        retr = self.path_helper.get(\"/standalone/l\")\n        self.assertEqual(retr[0].x, 1)\n\n    def test_standalone_ref(self):\n        self.instance.standalone.l.add(1)\n        self.instance.standalone.ref = 1\n        self.assertEqual(self.instance.standalone.ref._referenced_object, 1)\n\n    def test_get_list_retrieves_correct_attribute(self):\n        self.assertEqual(self.path_helper.get_list(\"/standalone/l\")._yang_name, \"l\")\n\n    def test_get_list_returns_correct_type(self):\n        self.assertEqual(self.path_helper.get_list(\"/standalone/l\")._is_container, \"list\")\n\n\nif __name__ == \"__main__\":\n    unittest.main()\n"
  },
  {
    "path": "tests/xpath/02-static_ptr/__init__.py",
    "content": ""
  },
  {
    "path": "tests/xpath/02-static_ptr/ptr-tc02.yang",
    "content": "module ptr-tc02 {\n    yang-version \"1.1\";\n    namespace \"http://rob.sh/yang/xpathelper/tc02\";\n    prefix \"tc01\";\n    organization \"BugReports Inc\";\n    contact \"A bug reporter\";\n\n    description\n        \"A test module\";\n    revision 2014-01-01 {\n        description \"april-fools\";\n        reference \"fooled-you\";\n    }\n\n\n    container container {\n\n        list t1a {\n            key \"t1b\";\n\n            leaf t1b {\n                type leafref {\n                    path \"../t1c/t1d\";\n                }\n            }\n\n            container t1c {\n                leaf t1d {\n                    type string;\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "tests/xpath/02-static_ptr/run.py",
    "content": "#!/usr/bin/env python\nimport unittest\n\nfrom pyangbind.lib.xpathhelper import YANGPathHelper\nfrom tests.base import PyangBindTestCase\n\n\nclass XPathStaticPtrTests(PyangBindTestCase):\n    yang_files = [\"ptr-tc02.yang\"]\n    pyang_flags = [\"--use-xpathhelper\"]\n\n    def setUp(self):\n        self.path_helper = YANGPathHelper()\n        self.yang_obj = self.bindings.ptr_tc02(path_helper=self.path_helper)\n        for x in range(0, 100):\n            self.yang_obj.container.t1a.add(\"x%s\" % x)\n\n    def test_list_key_pointer(self):\n        for x in range(0, 100):\n            with self.subTest(key=x):\n                self.assertEqual(\n                    self.yang_obj.container.t1a[\"x%s\" % x].t1c.t1d,\n                    \"x%s\" % x,\n                    \"list key was not set correctly when acting as a pointer (%s != 'test')\"\n                    % self.yang_obj.container.t1a[\"x%s\" % x].t1c.t1d,\n                )\n\n    def test_list_key_value(self):\n        for x in range(0, 100):\n            with self.subTest(key=x):\n                self.assertEqual(\n                    str(self.yang_obj.container.t1a[\"x%s\" % x].t1b),\n                    \"x%s\" % x,\n                    \"list key pointer was not read correctly (value is %s)\"\n                    % self.yang_obj.container.t1a[\"x%s\" % x].t1b,\n                )\n\n\nif __name__ == \"__main__\":\n    unittest.main()\n"
  },
  {
    "path": "tests/xpath/03-current/__init__.py",
    "content": ""
  },
  {
    "path": "tests/xpath/03-current/current-tc03.yang",
    "content": "module current-tc03 {\n    yang-version \"1.1\";\n    namespace \"http://rob.sh/yang/xpathelper/tc03\";\n    prefix \"tc03\";\n    organization \"BugReports Inc\";\n    contact \"A bug reporter\";\n\n    description\n        \"A test module\";\n    revision 2014-01-01 {\n        description \"april-fools\";\n        reference \"fooled-you\";\n    }\n\n    list src-list {\n        key \"referenced value\";\n\n        leaf referenced {\n            type string;\n        }\n\n        leaf value {\n            type string;\n        }\n    }\n\n    list referencing-list {\n        key \"testnum\";\n\n        leaf testnum {\n            type uint8;\n        }\n\n        leaf source-val {\n            type string;\n        }\n\n        leaf reference {\n            type leafref {\n                path \"/src-list[referenced=current()/../source-val]/value\";\n                require-instance true;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "tests/xpath/03-current/run.py",
    "content": "#!/usr/bin/env python\nimport unittest\n\nfrom pyangbind.lib.xpathhelper import YANGPathHelper\nfrom tests.base import PyangBindTestCase\n\n\nclass XPathCurrentTests(PyangBindTestCase):\n    yang_files = [\"current-tc03.yang\"]\n    pyang_flags = [\"--use-xpathhelper\"]\n\n    def setUp(self):\n        self.path_helper = YANGPathHelper()\n        self.yang_obj = self.bindings.current_tc03(path_helper=self.path_helper)\n        for i in [(1, 2), (3, 4), (5, 6)]:\n            self.yang_obj.src_list.add(\"%s %s\" % i)\n        self.yang_obj.referencing_list.add(1)\n        self.yang_obj.referencing_list[1].source_val = \"1\"\n        self.yang_obj.referencing_list[1].reference = \"2\"\n\n    def test_referencing_list_source_val(self):\n        self.assertEqual(self.yang_obj.referencing_list[1].source_val, \"1\")\n\n    def test_referencing_list_reference(self):\n        self.assertEqual(str(self.yang_obj.referencing_list[1].reference), \"2\")\n\n    def test_src_list_referenced(self):\n        self.assertEqual(self.yang_obj.src_list[\"1 2\"].referenced, \"1\")\n\n    def test_src_list_value(self):\n        self.assertEqual(self.yang_obj.src_list[\"1 2\"].value, \"2\")\n\n\nif __name__ == \"__main__\":\n    unittest.main()\n"
  },
  {
    "path": "tests/xpath/04-root/__init__.py",
    "content": ""
  },
  {
    "path": "tests/xpath/04-root/json/04-serialise.json",
    "content": "{\n\t\"root-tc04-a\": {\n\t\t\"a\":\t\"emigration\"\n\t},\n\t\"root-tc04-b\": {\n\t\t\"b\":\t\"alpine-fork\"\n\t}\n}"
  },
  {
    "path": "tests/xpath/04-root/json/04b-ietf-serialise.json",
    "content": "{\n\t\"root-tc04-a:root-tc04-a\": {\n\t\t\"a\": \"emigration\"\n\t},\n\t\"root-tc04-b:root-tc04-b\": {\n\t\t\"b\": \"alpine-fork\"\n\t}\n}"
  },
  {
    "path": "tests/xpath/04-root/json/05-deserialise.json",
    "content": "{\n\t\"root-tc04-a\": {\n\t\t\"a\": \"red-butte\"\n\t},\n\t\"root-tc04-b\": {\n\t\t\"b\": \"parleys\"\n\t}\n}"
  },
  {
    "path": "tests/xpath/04-root/json/06-deserialise-ietf.json",
    "content": "{\n\t\"root-tc04-b:root-tc04-b\": {\n\t\t\"b\": \"millcreek\"\n\t},\n\t\"root-tc04-a:root-tc04-a\": {\n\t\t\"a\": \"bell\"\n\t}\n}"
  },
  {
    "path": "tests/xpath/04-root/root-tc04-a.yang",
    "content": "module root-tc04-a {\n    yang-version \"1\";\n    namespace \"http://rob.sh/yang/test/root-tc04-a\";\n    prefix \"foo\";\n    organization \"BugReports Inc\";\n    contact \"A bug reporter\";\n\n    description\n        \"A test module\";\n    revision 2014-01-01 {\n        description \"april-fools\";\n        reference \"fooled-you\";\n    }\n\n    container root-tc04-a {\n        leaf a {\n            type string;\n        }\n    }\n}\n"
  },
  {
    "path": "tests/xpath/04-root/root-tc04-b.yang",
    "content": "module root-tc04-b {\n    yang-version \"1\";\n    namespace \"http://rob.sh/yang/test/root-tc04-b\";\n    prefix \"foo\";\n    organization \"BugReports Inc\";\n    contact \"A bug reporter\";\n\n    description\n        \"A test module\";\n    revision 2014-01-01 {\n        description \"april-fools\";\n        reference \"fooled-you\";\n    }\n\n    container root-tc04-b {\n        leaf b {\n            type string;\n        }\n    }\n}\n"
  },
  {
    "path": "tests/xpath/04-root/run.py",
    "content": "#!/usr/bin/env python\n\nimport json\nimport os\nimport unittest\n\nimport pyangbind.lib.pybindJSON as pbJ\nfrom pyangbind.lib.serialise import pybindJSONDecoder\nfrom pyangbind.lib.xpathhelper import YANGPathHelper\nfrom pyangbind.lib.yangtypes import safe_name\nfrom tests.base import PyangBindTestCase\n\n\nclass XPathRootTests(PyangBindTestCase):\n    yang_files = [\"root-tc04-a.yang\", \"root-tc04-b.yang\"]\n    pyang_flags = [\"--use-extmethods\", \"--use-xpathhelper\"]\n\n    def setUp(self):\n        self.path_helper = YANGPathHelper()\n        self.instance_a = self.bindings.root_tc04_a(path_helper=self.path_helper)\n        self.instance_b = self.bindings.root_tc04_b(path_helper=self.path_helper)\n\n    def test_001_check_containers(self):\n        self.assertIsNot(getattr(self.instance_a, safe_name(\"root-tc04-a\"), None), None)\n        self.assertIsNot(getattr(self.instance_b, safe_name(\"root-tc04-b\"), None), None)\n\n    def test_002_base_gets(self):\n        # each of these raise exceptions so will cause test case failures\n        self.path_helper.get_unique(\"/\")\n        self.path_helper.get_unique(\"/root-tc04-a\")\n        self.path_helper.get_unique(\"/root-tc04-b\")\n\n    def test_003_base_sets(self):\n        a = self.path_helper.get_unique(\"/root-tc04-a\")\n        a.a = \"little-cottonwood\"\n        self.assertEqual(self.instance_a.root_tc04_a.a, \"little-cottonwood\")\n        b = self.path_helper.get_unique(\"/root-tc04-b\")\n        b.b = \"big-cottonwood\"\n        self.assertEqual(self.instance_b.root_tc04_b.b, \"big-cottonwood\")\n\n    def test_004_serialise(self):\n        self.instance_a.root_tc04_a.a = \"emigration\"\n        self.instance_b.root_tc04_b.b = \"alpine-fork\"\n        with open(os.path.join(os.path.dirname(__file__), \"json\", \"04-serialise.json\")) as fp:\n            expected_json = json.load(fp)\n        v = json.loads(pbJ.dumps(self.path_helper.get_unique(\"/\")))\n        self.assertEqual(v, expected_json)\n\n        with open(os.path.join(os.path.dirname(__file__), \"json\", \"04b-ietf-serialise.json\")) as fp:\n            expected_ietf_json = json.load(fp)\n        v = json.loads(pbJ.dumps(self.path_helper.get_unique(\"/\"), mode=\"ietf\"))\n        self.assertEqual(v, expected_ietf_json)\n\n    def test_005_deserialise(self):\n        root = self.path_helper.get_unique(\"/\")\n        with open(os.path.join(os.path.dirname(__file__), \"json\", \"05-deserialise.json\"), \"r\") as fp:\n            pybindJSONDecoder.load_json(json.load(fp), None, None, obj=root)\n        v = json.loads(pbJ.dumps(self.path_helper.get_unique(\"/\")))\n        with open(os.path.join(os.path.dirname(__file__), \"json\", \"05-deserialise.json\"), \"r\") as fp:\n            x = json.load(fp)\n        self.assertEqual(v, x)\n\n    def test_006_ietf_deserialise(self):\n        root = self.path_helper.get_unique(\"/\")\n        with open(os.path.join(os.path.dirname(__file__), \"json\", \"06-deserialise-ietf.json\"), \"r\") as fp:\n            pybindJSONDecoder.load_ietf_json(json.load(fp), None, None, obj=root)\n        v = json.loads(pbJ.dumps(self.path_helper.get_unique(\"/\"), mode=\"ietf\"))\n        with open(os.path.join(os.path.dirname(__file__), \"json\", \"06-deserialise-ietf.json\"), \"r\") as fp:\n            x = json.load(fp)\n        self.assertEqual(v, x)\n\n\nif __name__ == \"__main__\":\n    unittest.main()\n"
  },
  {
    "path": "tests/xpath/__init__.py",
    "content": ""
  },
  {
    "path": "tox.ini",
    "content": "[tox]\nenvlist = py{38,39,310,311,312,313},py,black\nskip_missing_interpreters = True\n\n[testenv]\nsetenv =\n  PYTHONDONTWRITEBYTECODE=1\ndeps = -rrequirements.DEVELOPER.txt\n       -rrequirements.txt\ncommands = \n    coverage run -m pytest {posargs}\n    coverage report -i \n    pyang -V --plugindir \"./pyangbind/plugin\" -f pybind --overwrite -o tests/base-binding-out.py tests/base-test.yang\n\n[testenv:black]\ndeps = black\n\ncommands =\n    black --check --line-length 119 .\n\n[pytest]\npython_files = run.py\ntestpaths =\n    tests\naddopts = \n    --disable-warnings\n    --import-mode importlib\n    "
  }
]