#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""Extension for the pyang command line interface.
This module includes tools for augmenting ``PYANG_PLUGINPATH`` with
the location of auto-discovered pyang plugins.
Pyang do not use the ``setuptools`` to register plugins. Instead it requires
that the paths of all directories containing plugins to be present in the
``PYANG_PLUGINPATH`` environment variable.
``pyangext`` reads all entry points under ``yang.plugins``, detect the path
to the file that contains the function registered, and builds a list with
the containing directories.
In this sense, ``pyangext run`` command can be used as a bridge to
the ``pyang`` command, but using the auto-discovery feature.
Note:
Including non pyang-plugin python files alongside pyang-plugins
python files (in the same directory) will result in a pyang CLI crash.
It is recommended that the function registered as entry-point follows
the proprietary pyang plugin convention, or in other words:
- it should be named ``pyang_plugin_init``
- it should call ``pyang.plugin.register_plugin`` with an instance of
``pyang.plugin.PyangPlugin`` as argument.
See Also:
https://pythonhosted.org/setuptools/setuptools.html#dynamic-discovery-of-services-and-plugins
Command Line Interface
======================
Usage:
``pyangext [OPTIONS] COMMAND [ARGS]``
Options:
-h, --help Show this message and exit.
-v, --version Show the version and exit.
--path Prints the auto discovered plugin path.
Python packages that register an entry-point
inside ``yang.plugins`` will be auto-detected.
--init, --export-path Prints an export shell statement with the auto
discovered plugin path.
This may be used by shell script to configure
``PYANG_PLUGINPATH`` environment variable.
Example: |example|
--help Show this message and exit.
Commands:
:``call``: invoke pyang script with plugin path adjusted using
auto-discovery.
.. |example| raw:: html
<code><pre>eval $(pyangext --export-path)</pre></code>
"""
import sys
from os import environ
from os.path import pathsep
from subprocess import Popen
from textwrap import dedent
from six.moves import shlex_quote
import click
from . import __version__ # noqa
from .paths import expanded
__author__ = "Anderson Bravalheri"
__copyright__ = "Copyright (C) 2016 Anderson Bravalheri"
__license__ = "mozilla"
def _fixdoc(func):
"""Return a new text with the text wrapping in a function's docstring"""
docstring = dedent(func.__doc__)
lines = (' '.join(line.split()) for line in docstring.split('\n\n'))
new_docstring = "\n".join(lines)
return new_docstring
# click option callback
[docs]def print_path(ctx, _, value):
"""\
Prints the auto discovered plugin path.
Packages that register an ``yang.plugins``
entry-point will be auto-detected.
"""
if not value or ctx.resilient_parsing:
return
click.echo(pathsep.join(expanded()))
ctx.exit()
# click option callback
[docs]def export_path(ctx, _, value):
"""\
Prints an export shell statement with the auto discovered plugin path.
This may be used by shell script to configure ``PYANG_PLUGINPATH``
environment variable.
Example:
::
eval $(pyangext --export-path)
"""
if not value or ctx.resilient_parsing:
return
click.echo(
'export PYANG_PLUGINPATH=' +
shlex_quote(pathsep.join(expanded())))
ctx.exit()
@click.group()
@click.help_option('-h', '--help')
@click.version_option(__version__, '-v', '--version')
@click.option(
'--path', help=_fixdoc(print_path),
is_flag=True, expose_value=False, callback=print_path)
@click.option(
'--init', '--export-path', help=_fixdoc(export_path),
is_flag=True, expose_value=False, callback=export_path)
def call():
"""pyang + sensible extensions
Includes self-registered pyang plugin auto-discovery
"""
pass
@call.command(
'run', context_settings={'ignore_unknown_options': True})
@click.argument('args', nargs=-1, type=click.UNPROCESSED)
def call_pyang(args):
"""invoke pyang script with plugin path adjusted using auto-discovery."""
environ['PYANG_PLUGINPATH'] = pathsep.join(expanded())
proc = Popen(['pyang'] + list(args), stdout=sys.stdout, stderr=sys.stderr)
proc.wait()
return proc.returncode